• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 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 // Based on the WaveFileWriter in Java from the open source JSyn library by Phil Burk
18 // https://github.com/philburk/jsyn/blob/master/src/main/java/com/jsyn/util/WaveFileWriter.java
19 
20 #ifndef UTIL_WAVE_FILE_WRITER
21 #define UTIL_WAVE_FILE_WRITER
22 
23 #include <cassert>
24 #include <stdio.h>
25 #include <algorithm>
26 
27 class WaveFileOutputStream {
28 public:
29     virtual ~WaveFileOutputStream() = default;
30     virtual void write(uint8_t b) = 0;
31 };
32 
33 /**
34  * Write audio data to a WAV file.
35  *
36  * <pre>
37  * <code>
38  * WaveFileWriter writer = new WaveFileWriter(waveFileOutputStream);
39  * writer.setFrameRate(48000);
40  * writer.setBitsPerSample(24);
41  * writer.write(floatArray, 0, numSamples);
42  * writer.close();
43  * </code>
44  * </pre>
45  *
46  */
47 class WaveFileWriter {
48 public:
49 
50     /**
51      * Create an object that will write a WAV file image to the specified stream.
52      *
53      * @param outputStream stream to receive the bytes
54      * @throws FileNotFoundException
55      */
WaveFileWriter(WaveFileOutputStream * outputStream)56     WaveFileWriter(WaveFileOutputStream *outputStream) {
57         mOutputStream = outputStream;
58     }
59 
60     /**
61      * Set the number of frames per second, also known as "sample rate".
62      *
63      * If you call this then it must be called before the first write().
64      *
65      * @param frameRate default is 44100
66      */
setFrameRate(int32_t frameRate)67     void setFrameRate(int32_t frameRate) {
68         mFrameRate = frameRate;
69     }
70 
getFrameRate()71     int32_t getFrameRate() const {
72         return mFrameRate;
73     }
74 
75     /**
76      * Set the size of one frame.
77      * For stereo, set this to 2. Default is mono = 1.
78      * Also known as ChannelCount.
79      *
80      * If you call this then it must be called before the first write().
81      *
82      * @param samplesPerFrame is 2 for stereo or 1 for mono
83      */
setSamplesPerFrame(int32_t samplesPerFrame)84     void setSamplesPerFrame(int32_t samplesPerFrame) {
85         mSamplesPerFrame = samplesPerFrame;
86     }
87 
88     /**
89      * Sets the number of frames in the file.
90      *
91      * If you do not know the final number of frames then that is OK.
92      * Just do not call this method and the RIFF and DATA chunk sizes
93      * will default to INT32_MAX. That is technically invalid WAV format
94      * but is common practice.
95      *
96      * If you call this then it must be called before the first write().
97      * @param frameCount number of frames to be written
98      */
setFrameCount(int32_t frameCount)99     void setFrameCount(int32_t frameCount) {
100         mFrameCount = frameCount;
101     }
102 
getSamplesPerFrame()103     int32_t getSamplesPerFrame() const {
104         return mSamplesPerFrame;
105     }
106 
107     /** Only 16 or 24 bit samples supported at the moment. Default is 16.
108      *
109      * If you call this then it must be called before the first write().
110      * @param bits number of bits in a PCM sample
111      */
setBitsPerSample(int32_t bits)112     void setBitsPerSample(int32_t bits) {
113         assert((bits == 16) || (bits == 24));
114         mBitsPerSample = bits;
115     }
116 
getBitsPerSample()117     int32_t getBitsPerSample() const {
118         return mBitsPerSample;
119     }
120 
close()121     void close() {
122     }
123 
124     /** Write single audio data value to the WAV file. */
125     void write(float value);
126 
127     /**
128      * Write a buffer to the WAV file.
129      */
130     void write(float *buffer, int32_t startSample, int32_t numSamples);
131 
132 private:
133     /**
134      * Write a 32 bit integer to the stream in Little Endian format.
135      */
136     void writeIntLittle(int32_t n);
137 
138     /**
139      * Write a 16 bit integer to the stream in Little Endian format.
140      */
141     void writeShortLittle(int16_t n);
142 
143     /**
144      * Write an 'fmt ' chunk to the WAV file containing the given information.
145      */
146     void writeFormatChunk();
147 
148     /**
149      * Write a 'data' chunk header to the WAV file. This should be followed by call to
150      * writeShortLittle() to write the data to the chunk.
151      */
152     void writeDataChunkHeader();
153 
154     /**
155      * Write a simple WAV header for PCM data.
156      */
157     void writeHeader();
158 
159     // Write lower 8 bits. Upper bits ignored.
160     void writeByte(uint8_t b);
161 
162     void writePCM24(float value);
163 
164     void writePCM16(float value);
165 
166     /**
167      * Write a 'RIFF' file header and a 'WAVE' ID to the WAV file.
168      */
169     void writeRiffHeader();
170 
171     int32_t getDataSizeInBytes();
172 
173     static constexpr int WAVE_FORMAT_PCM = 1;
174     WaveFileOutputStream *mOutputStream = nullptr;
175     int32_t mFrameRate = 48000;
176     int32_t mSamplesPerFrame = 1;
177     int32_t mFrameCount = 0; // 0 for unknown
178     int32_t mBitsPerSample = 16;
179     int32_t mBytesWritten = 0;
180     bool mHeaderWritten = false;
181     static constexpr int32_t PCM24_MIN = -(1 << 23);
182     static constexpr int32_t PCM24_MAX = (1 << 23) - 1;
183 
184 };
185 
186 #endif /* UTIL_WAVE_FILE_WRITER */
187 
188