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