| /* |
| * Copyright 2024 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "WaveFileWriter.h" |
| |
| #include <android/log.h> |
| |
| void WaveFileWriter::write(float value) { |
| if (!headerWritten) { |
| writeHeader(); |
| } |
| if (bitsPerSample == 24) { |
| writePCM24(value); |
| } else { |
| writePCM16(value); |
| } |
| } |
| |
| void WaveFileWriter::write(float *buffer, int32_t startSample, int32_t numSamples) { |
| for (int32_t i = 0; i < numSamples; i++) { |
| write(buffer[startSample + i]); |
| } |
| } |
| |
| void WaveFileWriter::writeIntLittle(int32_t n) { |
| writeByte(n); |
| writeByte(n >> 8); |
| writeByte(n >> 16); |
| writeByte(n >> 24); |
| } |
| |
| void WaveFileWriter::writeShortLittle(int16_t n) { |
| writeByte(n); |
| writeByte(n >> 8); |
| } |
| |
| void WaveFileWriter::writeFormatChunk() { |
| int32_t bytesPerSample = (bitsPerSample + 7) / 8; |
| |
| writeByte('f'); |
| writeByte('m'); |
| writeByte('t'); |
| writeByte(' '); |
| writeIntLittle(16); // chunk size |
| writeShortLittle(WAVE_FORMAT_PCM); |
| writeShortLittle((int16_t) mSamplesPerFrame); |
| writeIntLittle(mFrameRate); |
| // bytes/second |
| writeIntLittle(mFrameRate * mSamplesPerFrame * bytesPerSample); |
| // block align |
| writeShortLittle((int16_t) (mSamplesPerFrame * bytesPerSample)); |
| writeShortLittle((int16_t) bitsPerSample); |
| } |
| |
| void WaveFileWriter::writeDataChunkHeader() { |
| writeByte('d'); |
| writeByte('a'); |
| writeByte('t'); |
| writeByte('a'); |
| // Maximum size is not strictly correct but is commonly used |
| // when we do not know the final size. |
| writeIntLittle(INT32_MAX); |
| } |
| |
| void WaveFileWriter::writeHeader() { |
| writeRiffHeader(); |
| writeFormatChunk(); |
| writeDataChunkHeader(); |
| headerWritten = true; |
| } |
| |
| // Write lower 8 bits. Upper bits ignored. |
| void WaveFileWriter::writeByte(uint8_t b) { |
| mOutputStream->write(b); |
| bytesWritten += 1; |
| } |
| |
| void WaveFileWriter::writePCM24(float value) { |
| // Offset before casting so that we can avoid using floor(). |
| // Also round by adding 0.5 so that very small signals go to zero. |
| float temp = (PCM24_MAX * value) + 0.5 - PCM24_MIN; |
| int32_t sample = ((int) temp) + PCM24_MIN; |
| // clip to 24-bit range |
| if (sample > PCM24_MAX) { |
| sample = PCM24_MAX; |
| } else if (sample < PCM24_MIN) { |
| sample = PCM24_MIN; |
| } |
| // encode as little-endian |
| writeByte(sample); // little end |
| writeByte(sample >> 8); // middle |
| writeByte(sample >> 16); // big end |
| } |
| |
| void WaveFileWriter::writePCM16(float value) { |
| // Offset before casting so that we can avoid using floor(). |
| // Also round by adding 0.5 so that very small signals go to zero. |
| float temp = (INT16_MAX * value) + 0.5 - INT16_MIN; |
| int32_t sample = ((int) temp) + INT16_MIN; |
| if (sample > INT16_MAX) { |
| sample = INT16_MAX; |
| } else if (sample < INT16_MIN) { |
| sample = INT16_MIN; |
| } |
| writeByte(sample); // little end |
| writeByte(sample >> 8); // big end |
| } |
| |
| void WaveFileWriter::writeRiffHeader() { |
| writeByte('R'); |
| writeByte('I'); |
| writeByte('F'); |
| writeByte('F'); |
| // Maximum size is not strictly correct but is commonly used |
| // when we do not know the final size. |
| writeIntLittle(INT32_MAX); |
| writeByte('W'); |
| writeByte('A'); |
| writeByte('V'); |
| writeByte('E'); |
| } |