• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.drrickorang.loopback;
18 
19 import java.io.FileDescriptor;
20 import java.io.FileOutputStream;
21 import java.io.IOException;
22 import java.util.Arrays;
23 
24 import android.content.Context;
25 import android.net.Uri;
26 import android.os.ParcelFileDescriptor;
27 import android.util.Log;
28 
29 
30 /**
31  * This class is used to save the results to a .wav file.
32  */
33 
34 public class AudioFileOutput {
35     private static final String TAG = "AudioFileOutput";
36 
37     private Uri              mUri;
38     private Context          mContext;
39     private FileOutputStream mOutputStream;
40     private final int        mSamplingRate;
41 
42 
AudioFileOutput(Context context, Uri uri, int samplingRate)43     public AudioFileOutput(Context context, Uri uri, int samplingRate) {
44         mContext = context;
45         mUri = uri;
46         mSamplingRate = samplingRate;
47     }
48 
49 
writeData(double[] data)50     public boolean writeData(double[] data) {
51         return writeRingBufferData(data, 0, data.length);
52     }
53 
54     /**
55      * Writes recorded wav data to file
56      *  endIndex <= startIndex:  Writes [startIndex, data.length) then [0, endIndex)
57      *  endIndex > startIndex :  Writes [startIndex, endIndex)
58      * Returns true on successful write to file
59      */
writeRingBufferData(double[] data, int startIndex, int endIndex)60     public boolean writeRingBufferData(double[] data, int startIndex, int endIndex) {
61 
62         boolean status = false;
63         ParcelFileDescriptor parcelFileDescriptor = null;
64         try {
65             parcelFileDescriptor =
66                     mContext.getContentResolver().openFileDescriptor(mUri, "w");
67             FileDescriptor fileDescriptor = parcelFileDescriptor.getFileDescriptor();
68             mOutputStream = new FileOutputStream(fileDescriptor);
69             log("Done creating output stream");
70             int sampleCount = endIndex - startIndex;
71             if (sampleCount <= 0) {
72                 sampleCount += data.length;
73             }
74             writeHeader(sampleCount);
75 
76             if (endIndex > startIndex) {
77                 writeDataBuffer(data, startIndex, endIndex);
78             } else {
79                 writeDataBuffer(data, startIndex, data.length);
80                 writeDataBuffer(data, 0, endIndex);
81             }
82 
83             mOutputStream.close();
84             status = true;
85             parcelFileDescriptor.close();
86         } catch (Exception e) {
87             mOutputStream = null;
88             log("Failed to open wavefile" + e);
89         } finally {
90             try {
91                 if (parcelFileDescriptor != null) {
92                     parcelFileDescriptor.close();
93                 }
94             } catch (Exception e) {
95                 e.printStackTrace();
96                 log("Error closing ParcelFile Descriptor");
97             }
98         }
99         return status;
100     }
101 
writeHeader(int samples)102     private void writeHeader(int samples) {
103         if (mOutputStream != null) {
104             try {
105                 int channels = 1;
106                 int blockAlignment = 2;
107                 int bitsPerSample = 16;
108                 byte[] chunkSize = new byte[4];
109                 byte[] dataSize = new byte[4];
110                 int tempChunkSize =  (samples * 2) + 36;
111                 chunkSize[3] = (byte) (tempChunkSize >> 24);
112                 chunkSize[2] = (byte) (tempChunkSize >> 16);
113                 chunkSize[1] = (byte) (tempChunkSize >> 8);
114                 chunkSize[0] = (byte) tempChunkSize;
115                 int tempDataSize  = samples * 2;
116                 dataSize[3] = (byte) (tempDataSize >> 24);
117                 dataSize[2] = (byte) (tempDataSize >> 16);
118                 dataSize[1] = (byte) (tempDataSize >> 8);
119                 dataSize[0] = (byte) tempDataSize;
120 
121                 byte[] header = new byte[] {
122                     'R', 'I', 'F', 'F',
123                     chunkSize[0], chunkSize[1], chunkSize[2], chunkSize[3],
124                     'W', 'A', 'V', 'E',
125                     'f', 'm', 't', ' ',
126                     16, 0, 0, 0,
127                     1, 0,   // PCM
128                     (byte) channels, 0,   // number of channels
129                     (byte) mSamplingRate, (byte) (mSamplingRate >> 8), 0, 0,    // sample rate
130                     0, 0, 0, 0, // byte rate
131                     (byte) (channels * blockAlignment),
132                     0,   // block alignment
133                     (byte) bitsPerSample,
134                     0,  // bits per sample
135                     'd', 'a', 't', 'a',
136                     dataSize[0], dataSize[1], dataSize[2], dataSize[3],
137                 };
138                 mOutputStream.write(header);
139                 log("Done writing header");
140             } catch (IOException e) {
141                 Log.e(TAG, "Error writing header " + e);
142             }
143         }
144     }
145 
146 
writeDataBuffer(double [] data, int startIndex, int end)147     private void writeDataBuffer(double [] data, int startIndex, int end) {
148         if (mOutputStream != null) {
149             try {
150                 int bufferSize = 1024; //blocks of 1024 samples
151                 byte [] buffer = new byte[bufferSize * 2];
152 
153                 for (int ii = startIndex; ii < end; ii += bufferSize) {
154                     //clear buffer
155                     Arrays.fill(buffer, (byte) 0);
156                     int bytesUsed = 0;
157                     for (int jj = 0; jj < bufferSize; jj++) {
158                         int index = ii + jj;
159                         if (index >= end)
160                             break;
161                         int value = (int) Math.round(data[index] * Short.MAX_VALUE);
162                         byte ba = (byte) (0xFF & (value >> 8));  //little-endian
163                         byte bb = (byte) (0xFF & (value));
164                         buffer[(jj * 2) + 1] = ba;
165                         buffer[jj * 2]   = bb;
166                         bytesUsed += 2;
167                     }
168                     mOutputStream.write(buffer, 0, bytesUsed);
169                 }
170                 log("Done writing data");
171             } catch (IOException e) {
172                 Log.e(TAG, "Error writing data " + e);
173             }
174         }
175     }
176 
177 
log(String msg)178     private static void log(String msg) {
179         Log.v(TAG, msg);
180     }
181 
182 }
183