OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 The Native Client 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 "experimental/conways_life/audio/web_wav_sound_resource.h" | |
6 | |
7 #include <string.h> | |
8 #include <algorithm> | |
9 | |
10 #include "experimental/conways_life/web_resource_loader_inl.h" | |
11 #include "ppapi/cpp/instance.h" | |
12 #include "ppapi/cpp/url_response_info.h" | |
13 #include "ppapi/cpp/var.h" | |
14 | |
15 namespace { | |
16 // Wav record descriptor per | |
17 // https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ | |
18 #pragma pack(push, 1) | |
19 struct WavRecord { | |
20 // Header. | |
21 char header_id[4]; // Should be "RIFF" | |
22 int32_t record_size; // Should be size of data minus 8 | |
23 char format[4]; // Should be "WAVE" | |
24 // Subchunk 1, i.e. format descriptor. | |
25 char format_chunk_id[4]; // Should be "fmt ". | |
26 int32_t format_chunk_size; // Should be 16. | |
27 int16_t audio_format; // Should be 1 for uncompressed audio. | |
28 int16_t num_channels; // 1 for mono, 2 for stereo. | |
29 int32_t sample_rate; | |
30 int32_t byte_rate; | |
31 int16_t block_align; | |
32 int16_t bits_per_sample; | |
33 // Data chunk. | |
34 char data_id[4]; // Should be "data" | |
35 int32_t audio_chunk_size; // Size of sample data that follows. | |
36 char audio_data; // The first byte of actual sound data; | |
37 }; | |
38 #pragma pack(pop) | |
39 | |
40 // Parse the headers in the url response and return the length of the | |
41 // resource content ion bytes. Returns 0 in case of error. | |
42 int GetContentLength(const pp::URLResponseInfo response) { | |
43 pp::Var header_var = response.GetHeaders(); | |
44 if (!header_var.is_string()) { | |
45 return 0; | |
46 } | |
47 | |
48 static const std::string kContentLengthKey("Content-Length: "); | |
49 std::string headers = header_var.AsString(); | |
50 size_t i = headers.find(kContentLengthKey); | |
51 if (i == headers.npos) { | |
52 return 0; | |
53 } | |
54 | |
55 return atoi(headers.substr(i + kContentLengthKey.length()).c_str()); | |
56 } | |
57 | |
58 // Verify that the wav data in the buffer is valid and playable with | |
59 // the pepper audio interface. | |
60 bool VerifyWavData(const char* data, size_t data_size) { | |
61 // Do we have enough data. | |
62 if (data_size < sizeof(WavRecord)) { | |
63 printf("Wav data size = %u is too short.\n", | |
64 static_cast<uint32_t>(data_size)); | |
65 return false; | |
66 } | |
67 | |
68 const WavRecord* wav = reinterpret_cast<const WavRecord*>(data); | |
69 // Check various chunk formats. | |
70 if (::strncmp(wav->header_id, "RIFF", 4) != 0 || | |
71 ::strncmp(wav->format, "WAVE", 4) != 0 || | |
72 ::strncmp(wav->format_chunk_id, "fmt ", 4) != 0 || | |
73 ::strncmp(wav->data_id, "data", 4) != 0) { | |
74 printf("Did not find 'RIFF', 'WAVE' and 'fmt ' headers in wav data.\n"); | |
75 return false; | |
76 } | |
77 // Check that we support the audio format. | |
78 if (wav->sample_rate != 44100 && wav->sample_rate != 48000) { | |
79 printf("Wav sample rate = %i. MNacl only supports 44100 or 48000.\n", | |
80 wav->sample_rate); | |
81 return false; | |
82 } | |
83 if (wav->num_channels != 2 || wav->bits_per_sample != 16) { | |
84 printf("NaCl supports stereo audio and 16-bit per sample at this time.\n"); | |
85 return false; | |
86 } | |
87 // Finally, check that the actual data size and wav sample data size match. | |
88 bool check_audio_size = (wav->audio_chunk_size == | |
89 static_cast<int32_t>(data_size - sizeof(WavRecord) + 1)); | |
90 if (!check_audio_size) { | |
91 printf("Wav audio size: actual = %i, expected = %i\n", | |
92 wav->audio_chunk_size, | |
93 static_cast<int>(data_size - sizeof(WavRecord) + 1)); | |
94 } | |
95 return check_audio_size; | |
96 } | |
97 | |
98 } // anonymous namespace | |
99 | |
100 | |
101 namespace audio { | |
102 | |
103 WebWavSoundResource::WebWavSoundResource() | |
104 : is_ready_(false), | |
105 wav_data_size_(0), | |
106 received_data_size_(0), | |
107 loader_(NULL) { | |
108 } | |
109 | |
110 WebWavSoundResource::~WebWavSoundResource() { | |
111 Clear(); | |
112 } | |
113 | |
114 void WebWavSoundResource::Init(const std::string& url, pp::Instance* instance) { | |
115 assert(wav_data_.empty()); // Can only init once. | |
116 if (wav_data_.empty()) { | |
117 loader_ = new Loader(instance, this); | |
118 loader_->LoadURL(url); | |
119 } | |
120 } | |
121 | |
122 int32_t WebWavSoundResource::GetSampleRate() const { | |
123 assert(IsReady()); | |
124 const WavRecord* wav = reinterpret_cast<const WavRecord*>(&wav_data_[0]); | |
125 return wav->sample_rate; | |
126 } | |
127 | |
128 const char* WebWavSoundResource::GetAudioData() const { | |
129 assert(IsReady()); | |
130 const WavRecord* wav = reinterpret_cast<const WavRecord*>(&wav_data_[0]); | |
131 return &(wav->audio_data); | |
132 } | |
133 | |
134 size_t WebWavSoundResource::GetAudioDataSize() const { | |
135 assert(IsReady()); | |
136 const WavRecord* wav = reinterpret_cast<const WavRecord*>(&wav_data_[0]); | |
137 return wav->audio_chunk_size; | |
138 } | |
139 | |
140 bool WebWavSoundResource::OnWebResourceLoaderCallback( | |
141 Loader::DispatchOpCode op_code, Loader* loader) { | |
142 switch (op_code) { | |
143 case Loader::kUrlResponseInfoReady: { | |
144 int content_length = GetContentLength(loader->GetResponseInfo()); | |
145 if (content_length <= 0) { | |
146 // Either the download failed, or the sound data is bad. Abort. | |
147 Clear(); | |
148 return false; | |
149 } else { | |
150 // Create a local buffer big enough to receive the sound data. | |
151 wav_data_.reserve(content_length); | |
152 wav_data_size_ = content_length; | |
153 received_data_size_ = 0; | |
154 return true; | |
155 } | |
156 break; | |
157 } | |
158 case Loader::kDataReceived: { | |
159 if (received_data_size_ + loader->data_size() > wav_data_size_) { | |
160 // Size of sound data exceeds expected size. Abort. | |
161 return false; | |
162 } else { | |
163 // Append chunk of data to local sound buffer. | |
164 ::memcpy(&wav_data_[0] + received_data_size_, | |
165 loader->buffer(), loader->data_size()); | |
166 received_data_size_ += loader->data_size(); | |
167 return true; | |
168 } | |
169 break; | |
170 } | |
171 case Loader::kDownloadComplete: { | |
172 is_ready_ = VerifyWavData(&wav_data_[0], wav_data_size_); | |
173 if (!is_ready_) { | |
174 Clear(); | |
175 } | |
176 return true; | |
177 break; | |
178 } | |
179 } | |
180 return false; | |
181 } | |
182 | |
183 void WebWavSoundResource::OnWebResourceLoaderError(int32_t error, | |
184 Loader* loader) { | |
185 } | |
186 | |
187 void WebWavSoundResource::OnWebResourceLoaderDone(Loader* loader) { | |
188 loader_->CloseAndDeleteSelf(); | |
189 loader_ = NULL; | |
190 } | |
191 | |
192 void WebWavSoundResource::Clear() { | |
193 is_ready_ = false; | |
194 wav_data_.clear(); | |
195 wav_data_size_ = 0; | |
196 received_data_size_ = 0; | |
197 if (loader_) { | |
198 loader_->CloseAndDeleteSelf(); | |
199 loader_ = NULL; | |
200 } | |
201 } | |
202 | |
203 } // namespace audio | |
OLD | NEW |