blob: 8df6d5c9e2786e9c75f72a46e2ad3b4a80d18f89 [file] [log] [blame]
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +00001/*
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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020015#include "common_audio/wav_header.h"
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +000016
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +000017#include <cstring>
18#include <limits>
andrew@webrtc.org048c5022014-12-16 20:17:21 +000019#include <string>
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +000020
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020021#include "rtc_base/checks.h"
Niels Möllera12c42a2018-07-25 16:05:48 +020022#include "rtc_base/system/arch.h"
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +000023
24namespace webrtc {
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000025namespace {
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +000026
27struct ChunkHeader {
28 uint32_t ID;
29 uint32_t Size;
30};
kwiberg@webrtc.org2ebfac52015-01-14 10:51:54 +000031static_assert(sizeof(ChunkHeader) == 8, "ChunkHeader size");
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +000032
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000033// We can't nest this definition in WavHeader, because VS2013 gives an error
34// on sizeof(WavHeader::fmt): "error C2070: 'unknown': illegal sizeof operand".
35struct FmtSubchunk {
36 ChunkHeader header;
37 uint16_t AudioFormat;
38 uint16_t NumChannels;
39 uint32_t SampleRate;
40 uint32_t ByteRate;
41 uint16_t BlockAlign;
42 uint16_t BitsPerSample;
43};
kwiberg@webrtc.org2ebfac52015-01-14 10:51:54 +000044static_assert(sizeof(FmtSubchunk) == 24, "FmtSubchunk size");
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000045const uint32_t kFmtSubchunkSize = sizeof(FmtSubchunk) - sizeof(ChunkHeader);
46
47struct WavHeader {
48 struct {
49 ChunkHeader header;
50 uint32_t Format;
51 } riff;
52 FmtSubchunk fmt;
53 struct {
54 ChunkHeader header;
55 } data;
56};
kwiberg@webrtc.org2ebfac52015-01-14 10:51:54 +000057static_assert(sizeof(WavHeader) == kWavHeaderSize, "no padding in header");
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +000058
59} // namespace
60
Peter Kasting69558702016-01-12 16:26:35 -080061bool CheckWavParameters(size_t num_channels,
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +000062 int sample_rate,
63 WavFormat format,
pkasting25702cb2016-01-08 13:50:27 -080064 size_t bytes_per_sample,
65 size_t num_samples) {
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +000066 // num_channels, sample_rate, and bytes_per_sample must be positive, must fit
67 // in their respective fields, and their product must fit in the 32-bit
68 // ByteRate field.
Peter Kasting69558702016-01-12 16:26:35 -080069 if (num_channels == 0 || sample_rate <= 0 || bytes_per_sample == 0)
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +000070 return false;
71 if (static_cast<uint64_t>(sample_rate) > std::numeric_limits<uint32_t>::max())
72 return false;
Peter Kasting69558702016-01-12 16:26:35 -080073 if (num_channels > std::numeric_limits<uint16_t>::max())
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +000074 return false;
75 if (static_cast<uint64_t>(bytes_per_sample) * 8 >
76 std::numeric_limits<uint16_t>::max())
77 return false;
78 if (static_cast<uint64_t>(sample_rate) * num_channels * bytes_per_sample >
79 std::numeric_limits<uint32_t>::max())
80 return false;
81
82 // format and bytes_per_sample must agree.
83 switch (format) {
84 case kWavFormatPcm:
85 // Other values may be OK, but for now we're conservative:
86 if (bytes_per_sample != 1 && bytes_per_sample != 2)
87 return false;
88 break;
89 case kWavFormatALaw:
90 case kWavFormatMuLaw:
91 if (bytes_per_sample != 1)
92 return false;
93 break;
94 default:
95 return false;
96 }
97
98 // The number of bytes in the file, not counting the first ChunkHeader, must
99 // be less than 2^32; otherwise, the ChunkSize field overflows.
pkasting25702cb2016-01-08 13:50:27 -0800100 const size_t header_size = kWavHeaderSize - sizeof(ChunkHeader);
101 const size_t max_samples =
102 (std::numeric_limits<uint32_t>::max() - header_size) / bytes_per_sample;
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +0000103 if (num_samples > max_samples)
104 return false;
105
106 // Each channel must have the same number of samples.
107 if (num_samples % num_channels != 0)
108 return false;
109
110 return true;
111}
112
113#ifdef WEBRTC_ARCH_LITTLE_ENDIAN
Yves Gerey665174f2018-06-19 15:03:05 +0200114static inline void WriteLE16(uint16_t* f, uint16_t x) {
115 *f = x;
116}
117static inline void WriteLE32(uint32_t* f, uint32_t x) {
118 *f = x;
119}
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +0000120static inline void WriteFourCC(uint32_t* f, char a, char b, char c, char d) {
Yves Gerey665174f2018-06-19 15:03:05 +0200121 *f = static_cast<uint32_t>(a) | static_cast<uint32_t>(b) << 8 |
122 static_cast<uint32_t>(c) << 16 | static_cast<uint32_t>(d) << 24;
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +0000123}
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000124
Yves Gerey665174f2018-06-19 15:03:05 +0200125static inline uint16_t ReadLE16(uint16_t x) {
126 return x;
127}
128static inline uint32_t ReadLE32(uint32_t x) {
129 return x;
130}
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000131static inline std::string ReadFourCC(uint32_t x) {
132 return std::string(reinterpret_cast<char*>(&x), 4);
133}
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +0000134#else
135#error "Write be-to-le conversion functions"
136#endif
137
pkasting25702cb2016-01-08 13:50:27 -0800138static inline uint32_t RiffChunkSize(size_t bytes_in_payload) {
Yves Gerey665174f2018-06-19 15:03:05 +0200139 return static_cast<uint32_t>(bytes_in_payload + kWavHeaderSize -
140 sizeof(ChunkHeader));
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000141}
142
Yves Gerey665174f2018-06-19 15:03:05 +0200143static inline uint32_t ByteRate(size_t num_channels,
144 int sample_rate,
pkasting25702cb2016-01-08 13:50:27 -0800145 size_t bytes_per_sample) {
146 return static_cast<uint32_t>(num_channels * sample_rate * bytes_per_sample);
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000147}
148
Peter Kasting69558702016-01-12 16:26:35 -0800149static inline uint16_t BlockAlign(size_t num_channels,
150 size_t bytes_per_sample) {
pkasting25702cb2016-01-08 13:50:27 -0800151 return static_cast<uint16_t>(num_channels * bytes_per_sample);
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000152}
153
andrew@webrtc.orgf866b2d2014-11-03 18:20:06 +0000154void WriteWavHeader(uint8_t* buf,
Peter Kasting69558702016-01-12 16:26:35 -0800155 size_t num_channels,
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +0000156 int sample_rate,
157 WavFormat format,
pkasting25702cb2016-01-08 13:50:27 -0800158 size_t bytes_per_sample,
159 size_t num_samples) {
henrikg91d6ede2015-09-17 00:24:34 -0700160 RTC_CHECK(CheckWavParameters(num_channels, sample_rate, format,
161 bytes_per_sample, num_samples));
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +0000162
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000163 WavHeader header;
pkasting25702cb2016-01-08 13:50:27 -0800164 const size_t bytes_in_payload = bytes_per_sample * num_samples;
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +0000165
166 WriteFourCC(&header.riff.header.ID, 'R', 'I', 'F', 'F');
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000167 WriteLE32(&header.riff.header.Size, RiffChunkSize(bytes_in_payload));
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +0000168 WriteFourCC(&header.riff.Format, 'W', 'A', 'V', 'E');
169
170 WriteFourCC(&header.fmt.header.ID, 'f', 'm', 't', ' ');
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000171 WriteLE32(&header.fmt.header.Size, kFmtSubchunkSize);
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +0000172 WriteLE16(&header.fmt.AudioFormat, format);
pkasting25702cb2016-01-08 13:50:27 -0800173 WriteLE16(&header.fmt.NumChannels, static_cast<uint16_t>(num_channels));
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +0000174 WriteLE32(&header.fmt.SampleRate, sample_rate);
Yves Gerey665174f2018-06-19 15:03:05 +0200175 WriteLE32(&header.fmt.ByteRate,
176 ByteRate(num_channels, sample_rate, bytes_per_sample));
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000177 WriteLE16(&header.fmt.BlockAlign, BlockAlign(num_channels, bytes_per_sample));
pkasting25702cb2016-01-08 13:50:27 -0800178 WriteLE16(&header.fmt.BitsPerSample,
179 static_cast<uint16_t>(8 * bytes_per_sample));
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +0000180
181 WriteFourCC(&header.data.header.ID, 'd', 'a', 't', 'a');
pkasting25702cb2016-01-08 13:50:27 -0800182 WriteLE32(&header.data.header.Size, static_cast<uint32_t>(bytes_in_payload));
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +0000183
184 // Do an extra copy rather than writing everything to buf directly, since buf
185 // might not be correctly aligned.
186 memcpy(buf, &header, kWavHeaderSize);
187}
188
andrew@webrtc.org048c5022014-12-16 20:17:21 +0000189bool ReadWavHeader(ReadableWav* readable,
Peter Kasting69558702016-01-12 16:26:35 -0800190 size_t* num_channels,
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000191 int* sample_rate,
192 WavFormat* format,
pkasting25702cb2016-01-08 13:50:27 -0800193 size_t* bytes_per_sample,
194 size_t* num_samples) {
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000195 WavHeader header;
andrew@webrtc.org048c5022014-12-16 20:17:21 +0000196 if (readable->Read(&header, kWavHeaderSize - sizeof(header.data)) !=
197 kWavHeaderSize - sizeof(header.data))
198 return false;
199
200 const uint32_t fmt_size = ReadLE32(header.fmt.header.Size);
201 if (fmt_size != kFmtSubchunkSize) {
202 // There is an optional two-byte extension field permitted to be present
203 // with PCM, but which must be zero.
204 int16_t ext_size;
205 if (kFmtSubchunkSize + sizeof(ext_size) != fmt_size)
206 return false;
207 if (readable->Read(&ext_size, sizeof(ext_size)) != sizeof(ext_size))
208 return false;
209 if (ext_size != 0)
210 return false;
211 }
212 if (readable->Read(&header.data, sizeof(header.data)) != sizeof(header.data))
213 return false;
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000214
215 // Parse needed fields.
216 *format = static_cast<WavFormat>(ReadLE16(header.fmt.AudioFormat));
217 *num_channels = ReadLE16(header.fmt.NumChannels);
218 *sample_rate = ReadLE32(header.fmt.SampleRate);
219 *bytes_per_sample = ReadLE16(header.fmt.BitsPerSample) / 8;
pkasting25702cb2016-01-08 13:50:27 -0800220 const size_t bytes_in_payload = ReadLE32(header.data.header.Size);
221 if (*bytes_per_sample == 0)
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000222 return false;
223 *num_samples = bytes_in_payload / *bytes_per_sample;
224
225 // Sanity check remaining fields.
226 if (ReadFourCC(header.riff.header.ID) != "RIFF")
227 return false;
228 if (ReadFourCC(header.riff.Format) != "WAVE")
229 return false;
230 if (ReadFourCC(header.fmt.header.ID) != "fmt ")
231 return false;
232 if (ReadFourCC(header.data.header.ID) != "data")
233 return false;
234
andrew@webrtc.org048c5022014-12-16 20:17:21 +0000235 if (ReadLE32(header.riff.header.Size) < RiffChunkSize(bytes_in_payload))
andrew@webrtc.orga3ed7132014-10-31 21:51:03 +0000236 return false;
237 if (ReadLE32(header.fmt.ByteRate) !=
238 ByteRate(*num_channels, *sample_rate, *bytes_per_sample))
239 return false;
240 if (ReadLE16(header.fmt.BlockAlign) !=
241 BlockAlign(*num_channels, *bytes_per_sample))
242 return false;
243
244 return CheckWavParameters(*num_channels, *sample_rate, *format,
245 *bytes_per_sample, *num_samples);
246}
247
kwiberg@webrtc.org877083c2014-08-20 07:42:46 +0000248} // namespace webrtc