Chromium Code Reviews| Index: experimental/life2011/life_stage_5/web_wav_sound_resource.cc |
| =================================================================== |
| --- experimental/life2011/life_stage_5/web_wav_sound_resource.cc (revision 0) |
| +++ experimental/life2011/life_stage_5/web_wav_sound_resource.cc (revision 0) |
| @@ -0,0 +1,204 @@ |
| +// Copyright 2011 The Native Client 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 "web_wav_sound_resource.h" |
| +#include "web_resource_loader_inl.h" |
| + |
| +#include <ppapi/cpp/instance.h> |
| +#include <ppapi/cpp/url_response_info.h> |
| +#include <ppapi/cpp/var.h> |
| + |
| +#include <algorithm> |
| +#include <string.h> |
| + |
| +namespace { |
| +// Wav record descriptor per |
| +// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ |
| +#pragma pack(push, 1) |
| +struct WavRecord { |
| + // Header. |
| + char header_id[4]; // Should be "RIFF" |
| + int32_t record_size; // Should be size of data minus 8 |
| + char format[4]; // Should be "WAVE" |
| + // Subchunk 1, i.e. format descriptor. |
| + char format_chunk_id[4]; // Should be "fmt ". |
| + int32_t format_chunk_size; // Should be 16. |
| + int16_t audio_format; // Should be 1 for uncompressed audio. |
| + int16_t num_channels; // 1 for mono, 2 for stereo. |
| + int32_t sample_rate; |
| + int32_t byte_rate; |
| + int16_t block_align; |
| + int16_t bits_per_sample; |
| + // Data chunk. |
| + char data_id[4]; // Should be "data" |
| + int32_t audio_chunk_size; // Size of sample data that follows. |
| + char audio_data; // The first byte of actual sound data; |
| +}; |
| +#pragma pack(pop) |
| + |
| +// Parse the headers in the url response and return the length of the |
| +// resource content ion bytes. Returns 0 in case of error. |
| +// TODO(gwink): Define a wrapper class around URLResponseInfo and parse |
| +// the headers into a map that can be conveniently queried. |
| +int GetContentLength(const pp::URLResponseInfo response) { |
| + pp::Var header_var = response.GetHeaders(); |
| + if (!header_var.is_string()) { |
| + return 0; |
| + } |
| + |
| + static const std::string kContentLengthKey("Content-Length: "); |
| + std::string headers = header_var.AsString(); |
| + size_t i = headers.find(kContentLengthKey); |
| + if (i == headers.npos) { |
| + return 0; |
| + } |
| + |
| + return atoi(headers.substr(i + kContentLengthKey.length()).c_str()); |
| +} |
| + |
| +// Verify that the wav data in the buffer is valid and playable with |
| +// the pepper audio interface. |
| +bool VerifyWavData(const char* data, size_t data_size) { |
| + // Do we have enough data. |
| + printf("Wav data size: %i\n", (int)data_size); |
| + if (data_size < sizeof(WavRecord)) { |
| + return false; |
| + } |
| + |
| + const WavRecord* wav = reinterpret_cast<const WavRecord*>(data); |
| + // Check various chunk formats. |
| + printf("Wav header id: |%4.4s|\n", wav->header_id); |
| + printf("Wav audio format id: |%4.4s|\n", wav->format); |
| + printf("Wav format header id: |%4.4s|\n", wav->format_chunk_id); |
| + printf("Wav data header id: |%4.4s|\n", wav->data_id); |
| + if (::strncmp(wav->header_id, "RIFF", 4) != 0 || |
| + ::strncmp(wav->format, "WAVE", 4) != 0 || |
| + ::strncmp(wav->format_chunk_id, "fmt ", 4) != 0 || |
| + ::strncmp(wav->data_id, "data", 4) != 0) { |
| + printf(" ** bad format id.\n"); |
| + return false; |
| + } |
| + // Check that we support the audio format. |
| + printf("Wav sample rate: %i\n", (int)wav->sample_rate); |
| + if (wav->sample_rate != 44100 && wav->sample_rate != 48000) { |
| + printf(" ** bad sample rate.\n"); |
| + return false; |
| + } |
| + printf("Wav num channels: %i\n", wav->num_channels); |
| + printf("Wav bits per sample: %i\n", wav->bits_per_sample); |
| + if (wav->num_channels != 2 || wav->bits_per_sample != 16) { |
| + printf(" ** not stereo or not 16-bit.\n"); |
| + return false; |
| + } |
| + // Finally, check that the actual data size and wav sample data size match. |
| + printf("Wav audio size: actual = %i, expected = %i\n", |
| + (int)wav->audio_chunk_size, |
| + static_cast<int>(data_size - sizeof(WavRecord) + 1)); |
| + return (wav->audio_chunk_size == |
| + static_cast<int32_t>(data_size - sizeof(WavRecord) + 1)); |
| +} |
| + |
| +} // anonymous namespace |
| + |
| + |
| +namespace life { |
| + |
| +WebWavSoundResource::WebWavSoundResource() |
| + : is_ready_(false), |
| + wav_data_size_(0), |
| + received_data_size_(0), |
| + loader_(NULL) { |
| +} |
| + |
| +WebWavSoundResource::~WebWavSoundResource() { |
| + Clear(); |
| +} |
| + |
| +void WebWavSoundResource::Init(const std::string& url, pp::Instance* instance) { |
| + assert(wav_data_.empty()); // Can only init once. |
| + if (wav_data_.empty()) { |
| + loader_ = new Loader(instance, this); |
| + loader_->LoadURL(url); |
| + } |
| +} |
| + |
| +int32_t WebWavSoundResource::GetSampleRate() const { |
| + assert(IsReady()); |
| + const WavRecord* wav = reinterpret_cast<const WavRecord*>(&wav_data_[0]); |
| + return wav->sample_rate; |
| +} |
| + |
| +const char* WebWavSoundResource::GetAudioData() const { |
| + assert(IsReady()); |
| + const WavRecord* wav = reinterpret_cast<const WavRecord*>(&wav_data_[0]); |
| + return &(wav->audio_data); |
| +} |
| + |
| +size_t WebWavSoundResource::GetAudioDataSize() const { |
| + assert(IsReady()); |
| + const WavRecord* wav = reinterpret_cast<const WavRecord*>(&wav_data_[0]); |
| + return wav->audio_chunk_size; |
| +} |
| + |
| +bool WebWavSoundResource::OnWebResourceLoaderCallback( |
| + Loader::DispatchOpCode op_code, Loader* loader) { |
| + switch(op_code) { |
| + case Loader::kUrlResponseInfoReady: { |
| + int content_length = GetContentLength(loader->GetResponseInfo()); |
| + if (content_length <= 0) { |
| + // Either the download failed, or the sound data is bad. Abort. |
| + Clear(); |
| + return false; |
| + } else { |
| + // Create a local buffer big enough to receive the sound data. |
| + wav_data_.reserve(content_length); |
| + wav_data_size_ = content_length; |
| + received_data_size_ = 0; |
| + return true; |
| + } |
| + break; |
| + } |
| + case Loader::kDataReceived: { |
| + if (received_data_size_ + loader->data_size() > wav_data_size_) { |
| + // Size of sound data exceeds expected size. Abort. |
| + return false; |
| + } else { |
| + // Append chunk of data to local sound buffer. |
| + ::memcpy(&wav_data_[0] + received_data_size_, |
| + loader->buffer(), loader->data_size()); |
| + received_data_size_ += loader->data_size(); |
| + return true; |
| + } |
| + break; |
| + } |
| + case Loader::kDownloadComplete: { |
| + is_ready_ = VerifyWavData(&wav_data_[0], wav_data_size_); |
| + if (!is_ready_) { |
| + Clear(); |
| + } |
| + return true; |
| + break; |
| + } |
| + } |
| + return false; |
| +} |
| + |
| +void WebWavSoundResource::OnWebResourceLoaderError(int32_t error, |
| + Loader* loader) { |
| +} |
| + |
| +void WebWavSoundResource::OnWebResourceLoaderDone(Loader* loader) { |
| + loader_->CloseAndDeleteSelf(); |
| + loader_ = NULL; |
| +} |
| + |
| +void WebWavSoundResource::Clear() { |
| + is_ready_ = false; |
| + wav_data_.clear(); |
| + wav_data_size_ = 0; |
| + received_data_size_ = 0; |
| + loader_->CloseAndDeleteSelf(); |
|
bbudge-google
2011/04/08 17:08:29
This needs a NULL check. Someone could instantiate
|
| + loader_ = NULL; |
| +} |
| + |
| +} // namespace life |