xref: /aosp_15_r20/cts/apps/CtsVerifier/jni/audio_loopback/WaveFileWriter.h (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1*b7c941bbSAndroid Build Coastguard Worker /*
2*b7c941bbSAndroid Build Coastguard Worker  * Copyright 2024 The Android Open Source Project
3*b7c941bbSAndroid Build Coastguard Worker  *
4*b7c941bbSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*b7c941bbSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*b7c941bbSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*b7c941bbSAndroid Build Coastguard Worker  *
8*b7c941bbSAndroid Build Coastguard Worker  *     http://www.apache.org/licenses/LICENSE-2.0
9*b7c941bbSAndroid Build Coastguard Worker  *
10*b7c941bbSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*b7c941bbSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*b7c941bbSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*b7c941bbSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*b7c941bbSAndroid Build Coastguard Worker  * limitations under the License.
15*b7c941bbSAndroid Build Coastguard Worker  */
16*b7c941bbSAndroid Build Coastguard Worker 
17*b7c941bbSAndroid Build Coastguard Worker // Based on the WaveFileWriter in Java from the open source JSyn library by Phil Burk
18*b7c941bbSAndroid Build Coastguard Worker // https://github.com/philburk/jsyn/blob/master/src/main/java/com/jsyn/util/WaveFileWriter.java
19*b7c941bbSAndroid Build Coastguard Worker 
20*b7c941bbSAndroid Build Coastguard Worker #ifndef UTIL_WAVE_FILE_WRITER
21*b7c941bbSAndroid Build Coastguard Worker #define UTIL_WAVE_FILE_WRITER
22*b7c941bbSAndroid Build Coastguard Worker 
23*b7c941bbSAndroid Build Coastguard Worker #include <cassert>
24*b7c941bbSAndroid Build Coastguard Worker #include <stdio.h>
25*b7c941bbSAndroid Build Coastguard Worker 
26*b7c941bbSAndroid Build Coastguard Worker class WaveFileOutputStream {
27*b7c941bbSAndroid Build Coastguard Worker public:
28*b7c941bbSAndroid Build Coastguard Worker     virtual ~WaveFileOutputStream() = default;
29*b7c941bbSAndroid Build Coastguard Worker     virtual void write(uint8_t b) = 0;
30*b7c941bbSAndroid Build Coastguard Worker };
31*b7c941bbSAndroid Build Coastguard Worker 
32*b7c941bbSAndroid Build Coastguard Worker /**
33*b7c941bbSAndroid Build Coastguard Worker  * Write audio data to a WAV file.
34*b7c941bbSAndroid Build Coastguard Worker  *
35*b7c941bbSAndroid Build Coastguard Worker  * <pre>
36*b7c941bbSAndroid Build Coastguard Worker  * <code>
37*b7c941bbSAndroid Build Coastguard Worker  * WaveFileWriter writer = new WaveFileWriter(waveFileOutputStream);
38*b7c941bbSAndroid Build Coastguard Worker  * writer.setFrameRate(48000);
39*b7c941bbSAndroid Build Coastguard Worker  * writer.setBitsPerSample(24);
40*b7c941bbSAndroid Build Coastguard Worker  * writer.write(floatArray, 0, numSamples);
41*b7c941bbSAndroid Build Coastguard Worker  * writer.close();
42*b7c941bbSAndroid Build Coastguard Worker  * </code>
43*b7c941bbSAndroid Build Coastguard Worker  * </pre>
44*b7c941bbSAndroid Build Coastguard Worker  *
45*b7c941bbSAndroid Build Coastguard Worker  */
46*b7c941bbSAndroid Build Coastguard Worker class WaveFileWriter {
47*b7c941bbSAndroid Build Coastguard Worker public:
48*b7c941bbSAndroid Build Coastguard Worker 
49*b7c941bbSAndroid Build Coastguard Worker     /**
50*b7c941bbSAndroid Build Coastguard Worker      * Create an object that will write a WAV file image to the specified stream.
51*b7c941bbSAndroid Build Coastguard Worker      *
52*b7c941bbSAndroid Build Coastguard Worker      * @param outputStream stream to receive the bytes
53*b7c941bbSAndroid Build Coastguard Worker      * @throws FileNotFoundException
54*b7c941bbSAndroid Build Coastguard Worker      */
WaveFileWriter(WaveFileOutputStream * outputStream)55*b7c941bbSAndroid Build Coastguard Worker     WaveFileWriter(WaveFileOutputStream *outputStream) {
56*b7c941bbSAndroid Build Coastguard Worker         mOutputStream = outputStream;
57*b7c941bbSAndroid Build Coastguard Worker         bitsPerSample = 24;
58*b7c941bbSAndroid Build Coastguard Worker     }
59*b7c941bbSAndroid Build Coastguard Worker 
reset()60*b7c941bbSAndroid Build Coastguard Worker     void reset() {
61*b7c941bbSAndroid Build Coastguard Worker         bytesWritten = 0;
62*b7c941bbSAndroid Build Coastguard Worker         headerWritten = false;
63*b7c941bbSAndroid Build Coastguard Worker     }
64*b7c941bbSAndroid Build Coastguard Worker 
65*b7c941bbSAndroid Build Coastguard Worker     /**
66*b7c941bbSAndroid Build Coastguard Worker      * @param frameRate default is 44100
67*b7c941bbSAndroid Build Coastguard Worker      */
setFrameRate(int32_t frameRate)68*b7c941bbSAndroid Build Coastguard Worker     void setFrameRate(int32_t frameRate) {
69*b7c941bbSAndroid Build Coastguard Worker         mFrameRate = frameRate;
70*b7c941bbSAndroid Build Coastguard Worker     }
71*b7c941bbSAndroid Build Coastguard Worker 
getFrameRate()72*b7c941bbSAndroid Build Coastguard Worker     int32_t getFrameRate() const {
73*b7c941bbSAndroid Build Coastguard Worker         return mFrameRate;
74*b7c941bbSAndroid Build Coastguard Worker     }
75*b7c941bbSAndroid Build Coastguard Worker 
76*b7c941bbSAndroid Build Coastguard Worker     /**
77*b7c941bbSAndroid Build Coastguard Worker      * For stereo, set this to 2. Default is mono = 1.
78*b7c941bbSAndroid Build Coastguard Worker      * Also known as ChannelCount
79*b7c941bbSAndroid Build Coastguard Worker      */
setSamplesPerFrame(int32_t samplesPerFrame)80*b7c941bbSAndroid Build Coastguard Worker     void setSamplesPerFrame(int32_t samplesPerFrame) {
81*b7c941bbSAndroid Build Coastguard Worker         mSamplesPerFrame = samplesPerFrame;
82*b7c941bbSAndroid Build Coastguard Worker     }
83*b7c941bbSAndroid Build Coastguard Worker 
getSamplesPerFrame()84*b7c941bbSAndroid Build Coastguard Worker     int32_t getSamplesPerFrame() const {
85*b7c941bbSAndroid Build Coastguard Worker         return mSamplesPerFrame;
86*b7c941bbSAndroid Build Coastguard Worker     }
87*b7c941bbSAndroid Build Coastguard Worker 
88*b7c941bbSAndroid Build Coastguard Worker     /** Only 16 or 24 bit samples supported at the moment. Default is 16. */
setBitsPerSample(int32_t bits)89*b7c941bbSAndroid Build Coastguard Worker     void setBitsPerSample(int32_t bits) {
90*b7c941bbSAndroid Build Coastguard Worker         assert((bits == 16) || (bits == 24));
91*b7c941bbSAndroid Build Coastguard Worker         bitsPerSample = bits;
92*b7c941bbSAndroid Build Coastguard Worker     }
93*b7c941bbSAndroid Build Coastguard Worker 
getBitsPerSample()94*b7c941bbSAndroid Build Coastguard Worker     int32_t getBitsPerSample() const {
95*b7c941bbSAndroid Build Coastguard Worker         return bitsPerSample;
96*b7c941bbSAndroid Build Coastguard Worker     }
97*b7c941bbSAndroid Build Coastguard Worker 
close()98*b7c941bbSAndroid Build Coastguard Worker     void close() {
99*b7c941bbSAndroid Build Coastguard Worker     }
100*b7c941bbSAndroid Build Coastguard Worker 
101*b7c941bbSAndroid Build Coastguard Worker     /** Write single audio data value to the WAV capture buffer. */
102*b7c941bbSAndroid Build Coastguard Worker     void write(float value);
103*b7c941bbSAndroid Build Coastguard Worker 
104*b7c941bbSAndroid Build Coastguard Worker     /**
105*b7c941bbSAndroid Build Coastguard Worker      * Write a buffer to the WAV capture buffer.
106*b7c941bbSAndroid Build Coastguard Worker      */
107*b7c941bbSAndroid Build Coastguard Worker     void write(float *buffer, int32_t startSample, int32_t numSamples);
108*b7c941bbSAndroid Build Coastguard Worker 
109*b7c941bbSAndroid Build Coastguard Worker private:
110*b7c941bbSAndroid Build Coastguard Worker     /**
111*b7c941bbSAndroid Build Coastguard Worker      * Write a 32 bit integer to the stream in Little Endian format.
112*b7c941bbSAndroid Build Coastguard Worker      */
113*b7c941bbSAndroid Build Coastguard Worker     void writeIntLittle(int32_t n);
114*b7c941bbSAndroid Build Coastguard Worker 
115*b7c941bbSAndroid Build Coastguard Worker     /**
116*b7c941bbSAndroid Build Coastguard Worker      * Write a 16 bit integer to the stream in Little Endian format.
117*b7c941bbSAndroid Build Coastguard Worker      */
118*b7c941bbSAndroid Build Coastguard Worker     void writeShortLittle(int16_t n);
119*b7c941bbSAndroid Build Coastguard Worker 
120*b7c941bbSAndroid Build Coastguard Worker     /**
121*b7c941bbSAndroid Build Coastguard Worker      * Write an 'fmt ' chunk to the WAV file containing the given information.
122*b7c941bbSAndroid Build Coastguard Worker      */
123*b7c941bbSAndroid Build Coastguard Worker     void writeFormatChunk();
124*b7c941bbSAndroid Build Coastguard Worker 
125*b7c941bbSAndroid Build Coastguard Worker     /**
126*b7c941bbSAndroid Build Coastguard Worker      * Write a 'data' chunk header to the WAV file. This should be followed by call to
127*b7c941bbSAndroid Build Coastguard Worker      * writeShortLittle() to write the data to the chunk.
128*b7c941bbSAndroid Build Coastguard Worker      */
129*b7c941bbSAndroid Build Coastguard Worker     void writeDataChunkHeader();
130*b7c941bbSAndroid Build Coastguard Worker 
131*b7c941bbSAndroid Build Coastguard Worker     /**
132*b7c941bbSAndroid Build Coastguard Worker      * Write a simple WAV header for PCM data.
133*b7c941bbSAndroid Build Coastguard Worker      */
134*b7c941bbSAndroid Build Coastguard Worker     void writeHeader();
135*b7c941bbSAndroid Build Coastguard Worker 
136*b7c941bbSAndroid Build Coastguard Worker     // Write lower 8 bits. Upper bits ignored.
137*b7c941bbSAndroid Build Coastguard Worker     void writeByte(uint8_t b);
138*b7c941bbSAndroid Build Coastguard Worker 
139*b7c941bbSAndroid Build Coastguard Worker     void writePCM24(float value);
140*b7c941bbSAndroid Build Coastguard Worker 
141*b7c941bbSAndroid Build Coastguard Worker     void writePCM16(float value);
142*b7c941bbSAndroid Build Coastguard Worker 
143*b7c941bbSAndroid Build Coastguard Worker     /**
144*b7c941bbSAndroid Build Coastguard Worker      * Write a 'RIFF' file header and a 'WAVE' ID to the WAV file.
145*b7c941bbSAndroid Build Coastguard Worker      */
146*b7c941bbSAndroid Build Coastguard Worker     void writeRiffHeader();
147*b7c941bbSAndroid Build Coastguard Worker 
148*b7c941bbSAndroid Build Coastguard Worker     static constexpr int WAVE_FORMAT_PCM = 1;
149*b7c941bbSAndroid Build Coastguard Worker     WaveFileOutputStream *mOutputStream = nullptr;
150*b7c941bbSAndroid Build Coastguard Worker     int32_t mFrameRate = 48000;
151*b7c941bbSAndroid Build Coastguard Worker     int32_t mSamplesPerFrame = 1;
152*b7c941bbSAndroid Build Coastguard Worker     int32_t bitsPerSample = 16;
153*b7c941bbSAndroid Build Coastguard Worker     int32_t bytesWritten = 0;
154*b7c941bbSAndroid Build Coastguard Worker     bool headerWritten = false;
155*b7c941bbSAndroid Build Coastguard Worker     static constexpr int32_t PCM24_MIN = -(1 << 23);
156*b7c941bbSAndroid Build Coastguard Worker     static constexpr int32_t PCM24_MAX = (1 << 23) - 1;
157*b7c941bbSAndroid Build Coastguard Worker 
158*b7c941bbSAndroid Build Coastguard Worker };
159*b7c941bbSAndroid Build Coastguard Worker 
160*b7c941bbSAndroid Build Coastguard Worker #endif /* UTIL_WAVE_FILE_WRITER */
161*b7c941bbSAndroid Build Coastguard Worker 
162