wav_header.cc (15081B)
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 "common_audio/wav_header.h" 16 17 #include <cstdint> 18 #include <cstring> 19 #include <limits> 20 #include <string> 21 22 #include "rtc_base/checks.h" 23 #include "rtc_base/logging.h" 24 #include "rtc_base/sanitizer.h" 25 #include "rtc_base/system/arch.h" 26 27 namespace webrtc { 28 namespace { 29 30 #ifndef WEBRTC_ARCH_LITTLE_ENDIAN 31 #error "Code not working properly for big endian platforms." 32 #endif 33 34 #pragma pack(2) 35 struct ChunkHeader { 36 uint32_t ID; 37 uint32_t Size; 38 }; 39 static_assert(sizeof(ChunkHeader) == 8, "ChunkHeader size"); 40 41 #pragma pack(2) 42 struct RiffHeader { 43 ChunkHeader header; 44 uint32_t Format; 45 }; 46 static_assert(sizeof(RiffHeader) == sizeof(ChunkHeader) + 4, "RiffHeader size"); 47 48 // We can't nest this definition in WavHeader, because VS2013 gives an error 49 // on sizeof(WavHeader::fmt): "error C2070: 'unknown': illegal sizeof operand". 50 #pragma pack(2) 51 struct FmtPcmSubchunk { 52 ChunkHeader header; 53 uint16_t AudioFormat; 54 uint16_t NumChannels; 55 uint32_t SampleRate; 56 uint32_t ByteRate; 57 uint16_t BlockAlign; 58 uint16_t BitsPerSample; 59 }; 60 static_assert(sizeof(FmtPcmSubchunk) == 24, "FmtPcmSubchunk size"); 61 const uint32_t kFmtPcmSubchunkSize = 62 sizeof(FmtPcmSubchunk) - sizeof(ChunkHeader); 63 64 // Pack struct to avoid additional padding bytes. 65 #pragma pack(2) 66 struct FmtIeeeFloatSubchunk { 67 ChunkHeader header; 68 uint16_t AudioFormat; 69 uint16_t NumChannels; 70 uint32_t SampleRate; 71 uint32_t ByteRate; 72 uint16_t BlockAlign; 73 uint16_t BitsPerSample; 74 uint16_t ExtensionSize; 75 }; 76 static_assert(sizeof(FmtIeeeFloatSubchunk) == 26, "FmtIeeeFloatSubchunk size"); 77 const uint32_t kFmtIeeeFloatSubchunkSize = 78 sizeof(FmtIeeeFloatSubchunk) - sizeof(ChunkHeader); 79 80 // Simple PCM wav header. It does not include chunks that are not essential to 81 // read audio samples. 82 #pragma pack(2) 83 struct WavHeaderPcm { 84 RiffHeader riff; 85 FmtPcmSubchunk fmt; 86 struct { 87 ChunkHeader header; 88 } data; 89 }; 90 static_assert(sizeof(WavHeaderPcm) == kPcmWavHeaderSize, 91 "no padding in header"); 92 93 // IEEE Float Wav header, includes extra chunks necessary for proper non-PCM 94 // WAV implementation. 95 #pragma pack(2) 96 struct WavHeaderIeeeFloat { 97 RiffHeader riff; 98 FmtIeeeFloatSubchunk fmt; 99 struct { 100 ChunkHeader header; 101 uint32_t SampleLength; 102 } fact; 103 struct { 104 ChunkHeader header; 105 } data; 106 }; 107 static_assert(sizeof(WavHeaderIeeeFloat) == kIeeeFloatWavHeaderSize, 108 "no padding in header"); 109 110 uint32_t PackFourCC(char a, char b, char c, char d) { 111 uint32_t packed_value = 112 static_cast<uint32_t>(a) | static_cast<uint32_t>(b) << 8 | 113 static_cast<uint32_t>(c) << 16 | static_cast<uint32_t>(d) << 24; 114 return packed_value; 115 } 116 117 std::string ReadFourCC(uint32_t x) { 118 return std::string(reinterpret_cast<char*>(&x), 4); 119 } 120 121 uint16_t MapWavFormatToHeaderField(WavFormat format) { 122 switch (format) { 123 case WavFormat::kWavFormatPcm: 124 return 1; 125 case WavFormat::kWavFormatIeeeFloat: 126 return 3; 127 case WavFormat::kWavFormatALaw: 128 return 6; 129 case WavFormat::kWavFormatMuLaw: 130 return 7; 131 } 132 RTC_CHECK_NOTREACHED(); 133 } 134 135 WavFormat MapHeaderFieldToWavFormat(uint16_t format_header_value) { 136 if (format_header_value == 1) { 137 return WavFormat::kWavFormatPcm; 138 } 139 if (format_header_value == 3) { 140 return WavFormat::kWavFormatIeeeFloat; 141 } 142 143 RTC_CHECK(false) << "Unsupported WAV format"; 144 } 145 146 uint32_t RiffChunkSize(size_t bytes_in_payload, size_t header_size) { 147 return static_cast<uint32_t>(bytes_in_payload + header_size - 148 sizeof(ChunkHeader)); 149 } 150 151 uint32_t ByteRate(size_t num_channels, 152 int sample_rate, 153 size_t bytes_per_sample) { 154 return static_cast<uint32_t>(num_channels * sample_rate * bytes_per_sample); 155 } 156 157 uint16_t BlockAlign(size_t num_channels, size_t bytes_per_sample) { 158 return static_cast<uint16_t>(num_channels * bytes_per_sample); 159 } 160 161 // Finds a chunk having the sought ID. If found, then `readable` points to the 162 // first byte of the sought chunk data. If not found, the end of the file is 163 // reached. 164 bool FindWaveChunk(ChunkHeader* chunk_header, 165 WavHeaderReader* readable, 166 const std::string sought_chunk_id) { 167 RTC_DCHECK_EQ(sought_chunk_id.size(), 4); 168 while (true) { 169 if (readable->Read(chunk_header, sizeof(*chunk_header)) != 170 sizeof(*chunk_header)) 171 return false; // EOF. 172 if (ReadFourCC(chunk_header->ID) == sought_chunk_id) 173 return true; // Sought chunk found. 174 // Ignore current chunk by skipping its payload. 175 if (!readable->SeekForward(chunk_header->Size)) 176 return false; // EOF or error. 177 } 178 } 179 180 bool ReadFmtChunkData(FmtPcmSubchunk* fmt_subchunk, WavHeaderReader* readable) { 181 // Reads "fmt " chunk payload. 182 if (readable->Read(&(fmt_subchunk->AudioFormat), kFmtPcmSubchunkSize) != 183 kFmtPcmSubchunkSize) 184 return false; 185 const uint32_t fmt_size = fmt_subchunk->header.Size; 186 if (fmt_size != kFmtPcmSubchunkSize) { 187 // There is an optional two-byte extension field permitted to be present 188 // with PCM, but which must be zero. 189 int16_t ext_size; 190 if (kFmtPcmSubchunkSize + sizeof(ext_size) != fmt_size) 191 return false; 192 if (readable->Read(&ext_size, sizeof(ext_size)) != sizeof(ext_size)) 193 return false; 194 if (ext_size != 0) 195 return false; 196 } 197 return true; 198 } 199 200 void WritePcmWavHeader(size_t num_channels, 201 int sample_rate, 202 size_t bytes_per_sample, 203 size_t num_samples, 204 uint8_t* buf, 205 size_t* header_size) { 206 RTC_CHECK(buf); 207 RTC_CHECK(header_size); 208 *header_size = kPcmWavHeaderSize; 209 auto header = MsanUninitialized<WavHeaderPcm>({}); 210 const size_t bytes_in_payload = bytes_per_sample * num_samples; 211 212 header.riff.header.ID = PackFourCC('R', 'I', 'F', 'F'); 213 header.riff.header.Size = RiffChunkSize(bytes_in_payload, *header_size); 214 header.riff.Format = PackFourCC('W', 'A', 'V', 'E'); 215 header.fmt.header.ID = PackFourCC('f', 'm', 't', ' '); 216 header.fmt.header.Size = kFmtPcmSubchunkSize; 217 header.fmt.AudioFormat = MapWavFormatToHeaderField(WavFormat::kWavFormatPcm); 218 header.fmt.NumChannels = static_cast<uint16_t>(num_channels); 219 header.fmt.SampleRate = sample_rate; 220 header.fmt.ByteRate = ByteRate(num_channels, sample_rate, bytes_per_sample); 221 header.fmt.BlockAlign = BlockAlign(num_channels, bytes_per_sample); 222 header.fmt.BitsPerSample = static_cast<uint16_t>(8 * bytes_per_sample); 223 header.data.header.ID = PackFourCC('d', 'a', 't', 'a'); 224 header.data.header.Size = static_cast<uint32_t>(bytes_in_payload); 225 226 // Do an extra copy rather than writing everything to buf directly, since buf 227 // might not be correctly aligned. 228 memcpy(buf, &header, *header_size); 229 } 230 231 void WriteIeeeFloatWavHeader(size_t num_channels, 232 int sample_rate, 233 size_t bytes_per_sample, 234 size_t num_samples, 235 uint8_t* buf, 236 size_t* header_size) { 237 RTC_CHECK(buf); 238 RTC_CHECK(header_size); 239 *header_size = kIeeeFloatWavHeaderSize; 240 auto header = MsanUninitialized<WavHeaderIeeeFloat>({}); 241 const size_t bytes_in_payload = bytes_per_sample * num_samples; 242 243 header.riff.header.ID = PackFourCC('R', 'I', 'F', 'F'); 244 header.riff.header.Size = RiffChunkSize(bytes_in_payload, *header_size); 245 header.riff.Format = PackFourCC('W', 'A', 'V', 'E'); 246 header.fmt.header.ID = PackFourCC('f', 'm', 't', ' '); 247 header.fmt.header.Size = kFmtIeeeFloatSubchunkSize; 248 header.fmt.AudioFormat = 249 MapWavFormatToHeaderField(WavFormat::kWavFormatIeeeFloat); 250 header.fmt.NumChannels = static_cast<uint16_t>(num_channels); 251 header.fmt.SampleRate = sample_rate; 252 header.fmt.ByteRate = ByteRate(num_channels, sample_rate, bytes_per_sample); 253 header.fmt.BlockAlign = BlockAlign(num_channels, bytes_per_sample); 254 header.fmt.BitsPerSample = static_cast<uint16_t>(8 * bytes_per_sample); 255 header.fmt.ExtensionSize = 0; 256 header.fact.header.ID = PackFourCC('f', 'a', 'c', 't'); 257 header.fact.header.Size = 4; 258 header.fact.SampleLength = static_cast<uint32_t>(num_channels * num_samples); 259 header.data.header.ID = PackFourCC('d', 'a', 't', 'a'); 260 header.data.header.Size = static_cast<uint32_t>(bytes_in_payload); 261 262 // Do an extra copy rather than writing everything to buf directly, since buf 263 // might not be correctly aligned. 264 memcpy(buf, &header, *header_size); 265 } 266 267 // Returns the number of bytes per sample for the format. 268 size_t GetFormatBytesPerSample(WavFormat format) { 269 switch (format) { 270 case WavFormat::kWavFormatPcm: 271 // Other values may be OK, but for now we're conservative. 272 return 2; 273 case WavFormat::kWavFormatALaw: 274 case WavFormat::kWavFormatMuLaw: 275 return 1; 276 case WavFormat::kWavFormatIeeeFloat: 277 return 4; 278 } 279 RTC_CHECK_NOTREACHED(); 280 } 281 282 bool CheckWavParameters(size_t num_channels, 283 int sample_rate, 284 WavFormat format, 285 size_t bytes_per_sample, 286 size_t num_samples) { 287 // num_channels, sample_rate, and bytes_per_sample must be positive, must fit 288 // in their respective fields, and their product must fit in the 32-bit 289 // ByteRate field. 290 if (num_channels == 0 || sample_rate <= 0 || bytes_per_sample == 0) 291 return false; 292 if (static_cast<uint64_t>(sample_rate) > std::numeric_limits<uint32_t>::max()) 293 return false; 294 if (num_channels > std::numeric_limits<uint16_t>::max()) 295 return false; 296 if (static_cast<uint64_t>(bytes_per_sample) * 8 > 297 std::numeric_limits<uint16_t>::max()) 298 return false; 299 if (static_cast<uint64_t>(sample_rate) * num_channels * bytes_per_sample > 300 std::numeric_limits<uint32_t>::max()) 301 return false; 302 303 // format and bytes_per_sample must agree. 304 switch (format) { 305 case WavFormat::kWavFormatPcm: 306 // Other values may be OK, but for now we're conservative: 307 if (bytes_per_sample != 1 && bytes_per_sample != 2) 308 return false; 309 break; 310 case WavFormat::kWavFormatALaw: 311 case WavFormat::kWavFormatMuLaw: 312 if (bytes_per_sample != 1) 313 return false; 314 break; 315 case WavFormat::kWavFormatIeeeFloat: 316 if (bytes_per_sample != 4) 317 return false; 318 break; 319 default: 320 return false; 321 } 322 323 // The number of bytes in the file, not counting the first ChunkHeader, must 324 // be less than 2^32; otherwise, the ChunkSize field overflows. 325 const size_t header_size = kPcmWavHeaderSize - sizeof(ChunkHeader); 326 const size_t max_samples = 327 (std::numeric_limits<uint32_t>::max() - header_size) / bytes_per_sample; 328 if (num_samples > max_samples) 329 return false; 330 331 // Each channel must have the same number of samples. 332 if (num_samples % num_channels != 0) 333 return false; 334 335 return true; 336 } 337 338 } // namespace 339 340 bool CheckWavParameters(size_t num_channels, 341 int sample_rate, 342 WavFormat format, 343 size_t num_samples) { 344 return CheckWavParameters(num_channels, sample_rate, format, 345 GetFormatBytesPerSample(format), num_samples); 346 } 347 348 void WriteWavHeader(size_t num_channels, 349 int sample_rate, 350 WavFormat format, 351 size_t num_samples, 352 uint8_t* buf, 353 size_t* header_size) { 354 RTC_CHECK(buf); 355 RTC_CHECK(header_size); 356 357 const size_t bytes_per_sample = GetFormatBytesPerSample(format); 358 RTC_CHECK(CheckWavParameters(num_channels, sample_rate, format, 359 bytes_per_sample, num_samples)); 360 if (format == WavFormat::kWavFormatPcm) { 361 WritePcmWavHeader(num_channels, sample_rate, bytes_per_sample, num_samples, 362 buf, header_size); 363 } else { 364 RTC_CHECK_EQ(format, WavFormat::kWavFormatIeeeFloat); 365 WriteIeeeFloatWavHeader(num_channels, sample_rate, bytes_per_sample, 366 num_samples, buf, header_size); 367 } 368 } 369 370 bool ReadWavHeader(WavHeaderReader* readable, 371 size_t* num_channels, 372 int* sample_rate, 373 WavFormat* format, 374 size_t* bytes_per_sample, 375 size_t* num_samples, 376 int64_t* data_start_pos) { 377 // Read using the PCM header, even though it might be float Wav file 378 auto header = MsanUninitialized<WavHeaderPcm>({}); 379 380 // Read RIFF chunk. 381 if (readable->Read(&header.riff, sizeof(header.riff)) != sizeof(header.riff)) 382 return false; 383 if (ReadFourCC(header.riff.header.ID) != "RIFF") 384 return false; 385 if (ReadFourCC(header.riff.Format) != "WAVE") 386 return false; 387 388 // Find "fmt " and "data" chunks. While the official Wave file specification 389 // does not put requirements on the chunks order, it is uncommon to find the 390 // "data" chunk before the "fmt " one. The code below fails if this is not the 391 // case. 392 if (!FindWaveChunk(&header.fmt.header, readable, "fmt ")) { 393 RTC_LOG(LS_ERROR) << "Cannot find 'fmt ' chunk."; 394 return false; 395 } 396 if (!ReadFmtChunkData(&header.fmt, readable)) { 397 RTC_LOG(LS_ERROR) << "Cannot read 'fmt ' chunk."; 398 return false; 399 } 400 if (!FindWaveChunk(&header.data.header, readable, "data")) { 401 RTC_LOG(LS_ERROR) << "Cannot find 'data' chunk."; 402 return false; 403 } 404 405 // Parse needed fields. 406 *format = MapHeaderFieldToWavFormat(header.fmt.AudioFormat); 407 *num_channels = header.fmt.NumChannels; 408 *sample_rate = header.fmt.SampleRate; 409 *bytes_per_sample = header.fmt.BitsPerSample / 8; 410 const size_t bytes_in_payload = header.data.header.Size; 411 if (*bytes_per_sample == 0) 412 return false; 413 *num_samples = bytes_in_payload / *bytes_per_sample; 414 415 const size_t header_size = *format == WavFormat::kWavFormatPcm 416 ? kPcmWavHeaderSize 417 : kIeeeFloatWavHeaderSize; 418 419 if (header.riff.header.Size < RiffChunkSize(bytes_in_payload, header_size)) 420 return false; 421 if (header.fmt.ByteRate != 422 ByteRate(*num_channels, *sample_rate, *bytes_per_sample)) 423 return false; 424 if (header.fmt.BlockAlign != BlockAlign(*num_channels, *bytes_per_sample)) 425 return false; 426 427 if (!CheckWavParameters(*num_channels, *sample_rate, *format, 428 *bytes_per_sample, *num_samples)) { 429 return false; 430 } 431 432 *data_start_pos = readable->GetPosition(); 433 return true; 434 } 435 436 } // namespace webrtc