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 14 namespace { 15 16 const char kChunkId[] = "RIFF"; 17 const char kFormat[] = "WAVE"; 18 const char kSubchunk1Id[] = "fmt "; 19 const 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'. 23 const 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. 27 const size_t kChunkHeaderSize = 8; 28 29 // The minimum size of 'fmt' chunk. 30 const size_t kFmtChunkMinimumSize = 16; 31 32 // The offsets of 'fmt' fields. 33 const size_t kAudioFormatOffset = 0; 34 const size_t kChannelOffset = 2; 35 const size_t kSampleRateOffset = 4; 36 const size_t kBitsPerSampleOffset = 14; 37 38 // Some constants for audio format. 39 const int kAudioFormatPCM = 1; 40 41 // Reads an integer from |data| with |offset|. 42 template <typename T> 43 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 55 namespace media { 56 57 WavAudioHandler::WavAudioHandler(const base::StringPiece& wav_data) 58 : num_channels_(0), 59 sample_rate_(0), 60 bits_per_sample_(0) { 61 CHECK_LE(kWavFileHeaderSize, wav_data.size()) << "wav data is too small"; 62 CHECK(wav_data.starts_with(kChunkId) && 63 memcmp(wav_data.data() + 8, kFormat, 4) == 0) 64 << "incorrect wav header"; 65 66 uint32 total_length = std::min(ReadInt<uint32>(wav_data, 4), 67 static_cast<uint32>(wav_data.size())); 68 uint32 offset = kWavFileHeaderSize; 69 while (offset < total_length) { 70 const int length = ParseSubChunk(wav_data.substr(offset)); 71 CHECK_LE(0, length) << "can't parse wav sub-chunk"; 72 offset += length; 73 } 74 75 const int frame_count = data_.size() * 8 / num_channels_ / bits_per_sample_; 76 params_ = AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY, 77 GuessChannelLayout(num_channels_), 78 sample_rate_, 79 bits_per_sample_, 80 frame_count); 81 } 82 83 WavAudioHandler::~WavAudioHandler() {} 84 85 bool WavAudioHandler::AtEnd(size_t cursor) const { 86 return data_.size() <= cursor; 87 } 88 89 bool WavAudioHandler::CopyTo(AudioBus* bus, 90 size_t cursor, 91 size_t* bytes_written) const { 92 if (!bus) 93 return false; 94 if (bus->channels() != params_.channels()) { 95 DVLOG(1) << "Number of channel mismatch."; 96 return false; 97 } 98 if (AtEnd(cursor)) { 99 bus->Zero(); 100 return true; 101 } 102 const int remaining_frames = 103 (data_.size() - cursor) / params_.GetBytesPerFrame(); 104 const int frames = std::min(bus->frames(), remaining_frames); 105 bus->FromInterleaved(data_.data() + cursor, frames, 106 params_.bits_per_sample() / 8); 107 *bytes_written = frames * params_.GetBytesPerFrame(); 108 bus->ZeroFramesPartial(frames, bus->frames() - frames); 109 return true; 110 } 111 112 int WavAudioHandler::ParseSubChunk(const base::StringPiece& data) { 113 if (data.size() < kChunkHeaderSize) 114 return data.size(); 115 uint32 chunk_length = ReadInt<uint32>(data, 4); 116 if (data.starts_with(kSubchunk1Id)) { 117 if (!ParseFmtChunk(data.substr(kChunkHeaderSize, chunk_length))) 118 return -1; 119 } else if (data.starts_with(kSubchunk2Id)) { 120 if (!ParseDataChunk(data.substr(kChunkHeaderSize, chunk_length))) 121 return -1; 122 } else { 123 DVLOG(1) << "Unknown data chunk: " << data.substr(0, 4) << "."; 124 } 125 return chunk_length + kChunkHeaderSize; 126 } 127 128 bool WavAudioHandler::ParseFmtChunk(const base::StringPiece& data) { 129 if (data.size() < kFmtChunkMinimumSize) { 130 DLOG(ERROR) << "Data size " << data.size() << " is too short."; 131 return false; 132 } 133 DCHECK_EQ(ReadInt<uint16>(data, kAudioFormatOffset), kAudioFormatPCM); 134 num_channels_ = ReadInt<uint16>(data, kChannelOffset); 135 sample_rate_ = ReadInt<uint32>(data, kSampleRateOffset); 136 bits_per_sample_ = ReadInt<uint16>(data, kBitsPerSampleOffset); 137 return true; 138 } 139 140 bool WavAudioHandler::ParseDataChunk(const base::StringPiece& data) { 141 data_ = data; 142 return true; 143 } 144 145 } // namespace media 146