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