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