1 /* 2 * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 // Based on the WAV file format documentation at 12 // https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ and 13 // http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html 14 15 #include "webrtc/common_audio/wav_header.h" 16 17 #include <algorithm> 18 #include <cstring> 19 #include <limits> 20 21 #include "webrtc/common_audio/include/audio_util.h" 22 23 namespace webrtc { 24 25 struct ChunkHeader { 26 uint32_t ID; 27 uint32_t Size; 28 }; 29 COMPILE_ASSERT(sizeof(ChunkHeader) == 8, chunk_header_size); 30 31 bool CheckWavParameters(int num_channels, 32 int sample_rate, 33 WavFormat format, 34 int bytes_per_sample, 35 uint32_t num_samples) { 36 // num_channels, sample_rate, and bytes_per_sample must be positive, must fit 37 // in their respective fields, and their product must fit in the 32-bit 38 // ByteRate field. 39 if (num_channels <= 0 || sample_rate <= 0 || bytes_per_sample <= 0) 40 return false; 41 if (static_cast<uint64_t>(sample_rate) > std::numeric_limits<uint32_t>::max()) 42 return false; 43 if (static_cast<uint64_t>(num_channels) > 44 std::numeric_limits<uint16_t>::max()) 45 return false; 46 if (static_cast<uint64_t>(bytes_per_sample) * 8 > 47 std::numeric_limits<uint16_t>::max()) 48 return false; 49 if (static_cast<uint64_t>(sample_rate) * num_channels * bytes_per_sample > 50 std::numeric_limits<uint32_t>::max()) 51 return false; 52 53 // format and bytes_per_sample must agree. 54 switch (format) { 55 case kWavFormatPcm: 56 // Other values may be OK, but for now we're conservative: 57 if (bytes_per_sample != 1 && bytes_per_sample != 2) 58 return false; 59 break; 60 case kWavFormatALaw: 61 case kWavFormatMuLaw: 62 if (bytes_per_sample != 1) 63 return false; 64 break; 65 default: 66 return false; 67 } 68 69 // The number of bytes in the file, not counting the first ChunkHeader, must 70 // be less than 2^32; otherwise, the ChunkSize field overflows. 71 const uint32_t max_samples = 72 (std::numeric_limits<uint32_t>::max() 73 - (kWavHeaderSize - sizeof(ChunkHeader))) / 74 bytes_per_sample; 75 if (num_samples > max_samples) 76 return false; 77 78 // Each channel must have the same number of samples. 79 if (num_samples % num_channels != 0) 80 return false; 81 82 return true; 83 } 84 85 #ifdef WEBRTC_ARCH_LITTLE_ENDIAN 86 static inline void WriteLE16(uint16_t* f, uint16_t x) { *f = x; } 87 static inline void WriteLE32(uint32_t* f, uint32_t x) { *f = x; } 88 static inline void WriteFourCC(uint32_t* f, char a, char b, char c, char d) { 89 *f = static_cast<uint32_t>(a) 90 | static_cast<uint32_t>(b) << 8 91 | static_cast<uint32_t>(c) << 16 92 | static_cast<uint32_t>(d) << 24; 93 } 94 #else 95 #error "Write be-to-le conversion functions" 96 #endif 97 98 void WriteWavHeader(uint8_t* buf, 99 int num_channels, 100 int sample_rate, 101 WavFormat format, 102 int bytes_per_sample, 103 uint32_t num_samples) { 104 assert(CheckWavParameters(num_channels, sample_rate, format, 105 bytes_per_sample, num_samples)); 106 107 struct { 108 struct { 109 ChunkHeader header; 110 uint32_t Format; 111 } riff; 112 struct { 113 ChunkHeader header; 114 uint16_t AudioFormat; 115 uint16_t NumChannels; 116 uint32_t SampleRate; 117 uint32_t ByteRate; 118 uint16_t BlockAlign; 119 uint16_t BitsPerSample; 120 } fmt; 121 struct { 122 ChunkHeader header; 123 } data; 124 } header; 125 COMPILE_ASSERT(sizeof(header) == kWavHeaderSize, no_padding_in_header); 126 127 const uint32_t bytes_in_payload = bytes_per_sample * num_samples; 128 129 WriteFourCC(&header.riff.header.ID, 'R', 'I', 'F', 'F'); 130 WriteLE32(&header.riff.header.Size, 131 bytes_in_payload + kWavHeaderSize - sizeof(ChunkHeader)); 132 WriteFourCC(&header.riff.Format, 'W', 'A', 'V', 'E'); 133 134 WriteFourCC(&header.fmt.header.ID, 'f', 'm', 't', ' '); 135 WriteLE32(&header.fmt.header.Size, sizeof(header.fmt) - sizeof(ChunkHeader)); 136 WriteLE16(&header.fmt.AudioFormat, format); 137 WriteLE16(&header.fmt.NumChannels, num_channels); 138 WriteLE32(&header.fmt.SampleRate, sample_rate); 139 WriteLE32(&header.fmt.ByteRate, (static_cast<uint32_t>(num_channels) 140 * sample_rate * bytes_per_sample)); 141 WriteLE16(&header.fmt.BlockAlign, num_channels * bytes_per_sample); 142 WriteLE16(&header.fmt.BitsPerSample, 8 * bytes_per_sample); 143 144 WriteFourCC(&header.data.header.ID, 'd', 'a', 't', 'a'); 145 WriteLE32(&header.data.header.Size, bytes_in_payload); 146 147 // Do an extra copy rather than writing everything to buf directly, since buf 148 // might not be correctly aligned. 149 memcpy(buf, &header, kWavHeaderSize); 150 } 151 152 } // namespace webrtc 153