1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/audio/sounds/wav_audio_handler.h"
#include <algorithm>
#include <cstring>
#include "base/logging.h"
#include "base/sys_byteorder.h"
#include "media/base/audio_bus.h"
namespace {
const char kChunkId[] = "RIFF";
const char kFormat[] = "WAVE";
const char kSubchunk1Id[] = "fmt ";
const char kSubchunk2Id[] = "data";
// The size of the header of a wav file. The header consists of 'RIFF', 4 bytes
// of total data length, and 'WAVE'.
const size_t kWavFileHeaderSize = 12;
// The size of a chunk header in wav file format. A chunk header consists of a
// tag ('fmt ' or 'data') and 4 bytes of chunk length.
const size_t kChunkHeaderSize = 8;
// The minimum size of 'fmt' chunk.
const size_t kFmtChunkMinimumSize = 16;
// The offsets of 'fmt' fields.
const size_t kAudioFormatOffset = 0;
const size_t kChannelOffset = 2;
const size_t kSampleRateOffset = 4;
const size_t kBitsPerSampleOffset = 14;
// Some constants for audio format.
const int kAudioFormatPCM = 1;
// Reads an integer from |data| with |offset|.
template <typename T>
T ReadInt(const base::StringPiece& data, size_t offset) {
CHECK_LE(offset + sizeof(T), data.size());
T result;
memcpy(&result, data.data() + offset, sizeof(T));
#if !defined(ARCH_CPU_LITTLE_ENDIAN)
result = base::ByteSwap(result);
#endif
return result;
}
} // namespace
namespace media {
WavAudioHandler::WavAudioHandler(const base::StringPiece& wav_data)
: num_channels_(0),
sample_rate_(0),
bits_per_sample_(0) {
CHECK_LE(kWavFileHeaderSize, wav_data.size()) << "wav data is too small";
CHECK(wav_data.starts_with(kChunkId) &&
memcmp(wav_data.data() + 8, kFormat, 4) == 0)
<< "incorrect wav header";
uint32 total_length = std::min(ReadInt<uint32>(wav_data, 4),
static_cast<uint32>(wav_data.size()));
uint32 offset = kWavFileHeaderSize;
while (offset < total_length) {
const int length = ParseSubChunk(wav_data.substr(offset));
CHECK_LE(0, length) << "can't parse wav sub-chunk";
offset += length;
}
const int frame_count = data_.size() * 8 / num_channels_ / bits_per_sample_;
params_ = AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
GuessChannelLayout(num_channels_),
sample_rate_,
bits_per_sample_,
frame_count);
}
WavAudioHandler::~WavAudioHandler() {}
bool WavAudioHandler::AtEnd(size_t cursor) const {
return data_.size() <= cursor;
}
bool WavAudioHandler::CopyTo(AudioBus* bus,
size_t cursor,
size_t* bytes_written) const {
if (!bus)
return false;
if (bus->channels() != params_.channels()) {
DVLOG(1) << "Number of channel mismatch.";
return false;
}
if (AtEnd(cursor)) {
bus->Zero();
return true;
}
const int remaining_frames =
(data_.size() - cursor) / params_.GetBytesPerFrame();
const int frames = std::min(bus->frames(), remaining_frames);
bus->FromInterleaved(data_.data() + cursor, frames,
params_.bits_per_sample() / 8);
*bytes_written = frames * params_.GetBytesPerFrame();
bus->ZeroFramesPartial(frames, bus->frames() - frames);
return true;
}
int WavAudioHandler::ParseSubChunk(const base::StringPiece& data) {
if (data.size() < kChunkHeaderSize)
return data.size();
uint32 chunk_length = ReadInt<uint32>(data, 4);
if (data.starts_with(kSubchunk1Id)) {
if (!ParseFmtChunk(data.substr(kChunkHeaderSize, chunk_length)))
return -1;
} else if (data.starts_with(kSubchunk2Id)) {
if (!ParseDataChunk(data.substr(kChunkHeaderSize, chunk_length)))
return -1;
} else {
DVLOG(1) << "Unknown data chunk: " << data.substr(0, 4) << ".";
}
return chunk_length + kChunkHeaderSize;
}
bool WavAudioHandler::ParseFmtChunk(const base::StringPiece& data) {
if (data.size() < kFmtChunkMinimumSize) {
DLOG(ERROR) << "Data size " << data.size() << " is too short.";
return false;
}
DCHECK_EQ(ReadInt<uint16>(data, kAudioFormatOffset), kAudioFormatPCM);
num_channels_ = ReadInt<uint16>(data, kChannelOffset);
sample_rate_ = ReadInt<uint32>(data, kSampleRateOffset);
bits_per_sample_ = ReadInt<uint16>(data, kBitsPerSampleOffset);
return true;
}
bool WavAudioHandler::ParseDataChunk(const base::StringPiece& data) {
data_ = data;
return true;
}
} // namespace media
|