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/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 
26 class WaveFileOutputStream {
27 public:
28     virtual ~WaveFileOutputStream() = default;
29     virtual void write(uint8_t b) = 0;
30 };
31 
32 /**
33  * Write audio data to a WAV file.
34  *
35  * <pre>
36  * <code>
37  * WaveFileWriter writer = new WaveFileWriter(waveFileOutputStream);
38  * writer.setFrameRate(48000);
39  * writer.setBitsPerSample(24);
40  * writer.write(floatArray, 0, numSamples);
41  * writer.close();
42  * </code>
43  * </pre>
44  *
45  */
46 class WaveFileWriter {
47 public:
48 
49     /**
50      * Create an object that will write a WAV file image to the specified stream.
51      *
52      * @param outputStream stream to receive the bytes
53      * @throws FileNotFoundException
54      */
WaveFileWriter(WaveFileOutputStream * outputStream)55     WaveFileWriter(WaveFileOutputStream *outputStream) {
56         mOutputStream = outputStream;
57     }
58 
59     /**
60      * @param frameRate default is 44100
61      */
setFrameRate(int32_t frameRate)62     void setFrameRate(int32_t frameRate) {
63         mFrameRate = frameRate;
64     }
65 
getFrameRate()66     int32_t getFrameRate() const {
67         return mFrameRate;
68     }
69 
70     /**
71      * For stereo, set this to 2. Default is mono = 1.
72      * Also known as ChannelCount
73      */
setSamplesPerFrame(int32_t samplesPerFrame)74     void setSamplesPerFrame(int32_t samplesPerFrame) {
75         mSamplesPerFrame = samplesPerFrame;
76     }
77 
getSamplesPerFrame()78     int32_t getSamplesPerFrame() const {
79         return mSamplesPerFrame;
80     }
81 
82     /** Only 16 or 24 bit samples supported at the moment. Default is 16. */
setBitsPerSample(int32_t bits)83     void setBitsPerSample(int32_t bits) {
84         assert((bits == 16) || (bits == 24));
85         bitsPerSample = bits;
86     }
87 
getBitsPerSample()88     int32_t getBitsPerSample() const {
89         return bitsPerSample;
90     }
91 
close()92     void close() {
93     }
94 
95     /** Write single audio data value to the WAV file. */
96     void write(float value);
97 
98     /**
99      * Write a buffer to the WAV file.
100      */
101     void write(float *buffer, int32_t startSample, int32_t numSamples);
102 
103 private:
104     /**
105      * Write a 32 bit integer to the stream in Little Endian format.
106      */
107     void writeIntLittle(int32_t n);
108 
109     /**
110      * Write a 16 bit integer to the stream in Little Endian format.
111      */
112     void writeShortLittle(int16_t n);
113 
114     /**
115      * Write an 'fmt ' chunk to the WAV file containing the given information.
116      */
117     void writeFormatChunk();
118 
119     /**
120      * Write a 'data' chunk header to the WAV file. This should be followed by call to
121      * writeShortLittle() to write the data to the chunk.
122      */
123     void writeDataChunkHeader();
124 
125     /**
126      * Write a simple WAV header for PCM data.
127      */
128     void writeHeader();
129 
130     // Write lower 8 bits. Upper bits ignored.
131     void writeByte(uint8_t b);
132 
133     void writePCM24(float value);
134 
135     void writePCM16(float value);
136 
137     /**
138      * Write a 'RIFF' file header and a 'WAVE' ID to the WAV file.
139      */
140     void writeRiffHeader();
141 
142     static constexpr int WAVE_FORMAT_PCM = 1;
143     WaveFileOutputStream *mOutputStream = nullptr;
144     int32_t mFrameRate = 48000;
145     int32_t mSamplesPerFrame = 1;
146     int32_t bitsPerSample = 16;
147     int32_t bytesWritten = 0;
148     bool headerWritten = false;
149     static constexpr int32_t PCM24_MIN = -(1 << 23);
150     static constexpr int32_t PCM24_MAX = (1 << 23) - 1;
151 
152 };
153 
154 #endif /* UTIL_WAVE_FILE_WRITER */
155 
156