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