Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 .. _devguide-coding-audio: | 1 .. _devguide-coding-audio: |
| 2 | 2 |
| 3 ##### | 3 ##### |
| 4 Audio | 4 Audio |
| 5 ##### | 5 ##### |
| 6 | 6 |
| 7 fooooooooooooooooooooo | 7 .. contents:: |
| 8 | 8 :local: |
| 9 :backlinks: none | |
| 10 :depth: 2 | |
| 11 | |
| 12 This chapter describes how to use the Pepper audio API to play an audio | |
| 13 stream. The Pepper audio API provides a low-level means of playing a stream of | |
| 14 audio samples generated by a Native Client module. The API generally works as | |
| 15 follows: A Native Client module creates an audio resource that represents an | |
| 16 audio stream, and tells the browser to start or stop playing the audio | |
| 17 resource. The browser calls a function in the Native Client module to fill a | |
| 18 buffer with audio samples every time it needs data to play from the audio | |
| 19 stream. | |
| 20 | |
| 21 The code examples in this chapter describe a simple Native Client module that | |
| 22 generates audio samples using a sine wave with a frequency of 440 Hz. The module | |
| 23 starts playing the audio samples as soon as it is loaded into the browser. For a | |
| 24 slightly more sophisticated example, see the ``audio`` example (source code in | |
| 25 the SDK directory ``examples/api/audio``), which lets users specify a frequency | |
| 26 for the sine wave and click buttons to start and stop audio playback. | |
| 27 | |
| 28 Reference information | |
| 29 ===================== | |
| 30 | |
| 31 For reference information related to the Pepper audio API, see the following | |
| 32 documentation: | |
| 33 | |
| 34 * `pp::AudioConfig class | |
| 35 <https://developers.google.com/native-client/peppercpp/classpp_1_1_audio_confi g>`_ | |
| 36 | |
| 37 * `pp::Audio class | |
| 38 <https://developers.google.com/native-client/peppercpp/classpp_1_1_audio>`_ | |
| 39 | |
| 40 * `audio_config.h | |
| 41 <https://developers.google.com/native-client/peppercpp/audio__config_8h>`_ | |
| 42 | |
| 43 * `audio.h <https://developers.google.com/native-client/peppercpp/audio_8h>`_ | |
| 44 | |
| 45 * `PP_AudioSampleRate | |
| 46 <https://developers.google.com/native-client/pepperc/group___enums.html#gaee75 0c350655f2fb0fe04c04029e0ff8>`_ | |
| 47 | |
| 48 About the Pepper audio API | |
| 49 ========================== | |
| 50 | |
| 51 The Pepper audio API lets Native Client modules play audio streams in a | |
| 52 browser. To play an audio stream, a module generates audio samples and writes | |
| 53 them into a buffer. The browser reads the audio samples from the buffer and | |
| 54 plays them using an audio device on the client computer. | |
| 55 | |
| 56 .. image:: /images/pepper-audio-buffer.png | |
| 57 | |
| 58 This mechanism is simple but low-level. If you want to play plain sound files in | |
| 59 a web application, you may want to consider higher-level alternatives such as | |
| 60 using the HTML ``<audio>`` tag, JavaScript, or the new `Web Audio API | |
| 61 <http://chromium.googlecode.com/svn/trunk/samples/audio/index.html>`_. | |
|
binji
2013/10/02 07:00:42
We also include ogg/vorbis/openAL in the SDK now,
Jim Stichnoth
2013/10/17 21:57:36
I don't really have enough knowledge to add someth
binji
2013/10/17 21:59:54
OK, we can leave it out for now.
| |
| 62 | |
| 63 The Pepper audio API is a good option for playing audio data if you want to do | |
| 64 audio processing in your web application. You might use the audio API, for | |
| 65 example, if you want to apply audio effects to sounds, synthesize your own | |
| 66 sounds, or do any other type of CPU-intensive processing of audio | |
| 67 samples. Another likely use case is gaming applications: you might use a gaming | |
| 68 library to process audio data, and then simply use the audio API to output the | |
| 69 processed data. | |
| 70 | |
| 71 The Pepper audio API is straightforward to use: | |
| 72 | |
| 73 #. Your module creates an audio configuration resource and an audio resource. | |
| 74 | |
| 75 #. Your module implements a callback function that fills an audio buffer with | |
| 76 data. | |
| 77 | |
| 78 #. Your module invokes the StartPlayback and StopPlayback methods of the audio | |
| 79 resource (e.g., when certain events occur). | |
| 80 | |
| 81 #. The browser invokes your callback function whenever it needs audio data to | |
| 82 play. Your callback function can generate the audio data in a number of | |
| 83 ways---e.g., it can generate new data, or it can copy pre-mixed data into the | |
| 84 audio buffer. | |
| 85 | |
| 86 This basic interaction is illustrated below, and described in detail in the | |
| 87 sections that follow. | |
| 88 | |
| 89 .. image:: /images/pepper-audio-api.png | |
| 90 | |
| 91 Digital audio concepts | |
| 92 ====================== | |
| 93 | |
| 94 Before you use the Pepper audio API, it's helpful to understand a few concepts | |
| 95 that are fundamental to how digital audio is recorded and played back: | |
| 96 | |
| 97 sample rate | |
| 98 the number of times an input sound source is sampled per second; | |
| 99 correspondingly, the number of samples that are played back per second | |
| 100 | |
| 101 bit depth | |
| 102 the number of bits used to represent a sample | |
| 103 | |
| 104 channels | |
| 105 the number of input sources recorded in each sampling interval; | |
| 106 correspondingly, the number of outputs that are played back simultaneously | |
| 107 (typically using different speakers) | |
| 108 | |
| 109 The higher the sample rate and bit depth used to record a sound wave, the more | |
| 110 accurately the sound wave can be reproduced, since it will have been sampled | |
| 111 more frequently and stored using a higher level of quantization. Common sampling | |
| 112 rates include 44,100 Hz (44,100 samples/second, the sample rate used on CDs), | |
| 113 and 48,000 Hz (the sample rate used on DVDs and Digital Audio Tapes). A common | |
| 114 bit depth is 16 bits per sample, and a common number of channels is 2 (left and | |
| 115 right channels for stereo sound). | |
| 116 | |
| 117 The Pepper audio API currently lets Native Client modules play audio streams | |
| 118 with the following configurations: | |
| 119 | |
| 120 * **sample rate**: 44,100 Hz or 48,000 Hz | |
| 121 * **bit depth**: 16 | |
| 122 * **channels**: 2 (stereo) | |
| 123 | |
| 124 Setting up the module | |
| 125 ===================== | |
| 126 | |
| 127 The code examples below describe a simple Native Client module that generates | |
| 128 audio samples using a sine wave with a frequency of 440 Hz. The module starts | |
| 129 playing the audio samples as soon as it is loaded into the browser. | |
| 130 | |
| 131 The Native Client module is set up by implementing subclasses of the | |
| 132 ``pp::Module`` and ``pp::Instance`` classes, as normal. | |
|
binji
2013/10/02 07:00:42
None of the other documents (aside from the tutori
Jim Stichnoth
2013/10/17 21:57:36
I trimmed the example back a bunch.
| |
| 133 | |
| 134 .. naclcode:: | |
| 135 | |
| 136 #include <cassert> | |
| 137 #include <cmath> | |
| 138 #include <limits> | |
| 139 #include "ppapi/cpp/audio.h" | |
| 140 #include "ppapi/cpp/instance.h" | |
| 141 #include "ppapi/cpp/module.h" | |
| 142 | |
| 143 namespace { | |
| 144 // Constants used to generate a sine wave. | |
| 145 const double kFrequency = 440.0; | |
| 146 const double kTwoPi = 2.0 * 3.141592653589; | |
| 147 | |
| 148 // Constants used to create an audio configuration resource. | |
| 149 // The sample count we will request from the browser. | |
| 150 const uint32_t kSampleFrameCount = 4096u; | |
| 151 // The number of channels in the audio stream (only supporting stereo audio | |
| 152 // for now). | |
| 153 const uint32_t kChannels = 2u; | |
| 154 } // namespace | |
| 155 | |
| 156 namespace sine_synth { | |
| 157 // The Instance class. One of these exists for each instance of your NaCl | |
| 158 // module on the web page. The browser will ask the Module object to create | |
| 159 // a new Instance for each occurrence of the <embed> tag that has these | |
| 160 // attributes: | |
| 161 // type="application/x-nacl" | |
| 162 // src="sine_synth.nmf" | |
| 163 class SineSynthInstance : public pp::Instance { | |
| 164 public: | |
| 165 explicit SineSynthInstance(PP_Instance instance) | |
| 166 : pp::Instance(instance), | |
| 167 theta_(0), | |
| 168 sample_frame_count_(kSampleFrameCount) {} | |
| 169 virtual ~SineSynthInstance() {} | |
| 170 | |
| 171 // Called by the browser once the NaCl module is loaded and ready to | |
| 172 // initialize. Creates a Pepper audio context and initializes it. Returns | |
| 173 // true on success. Returning false causes the NaCl module to be deleted | |
| 174 // and no other functions to be called. | |
| 175 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]); | |
| 176 | |
| 177 private: | |
| 178 // Function called by the browser when it needs more audio samples. | |
| 179 static void SineWaveCallback(void* samples, | |
| 180 uint32_t buffer_size, | |
| 181 void* data) { | |
| 182 ... | |
| 183 } | |
| 184 | |
| 185 // Audio resource. | |
| 186 pp::Audio audio_; | |
| 187 | |
| 188 // The last parameter sent to the sin function. Used to prevent sine wave | |
| 189 // skips on buffer boundaries. | |
| 190 double theta_; | |
| 191 | |
| 192 // The count of sample frames per channel in an audio buffer. | |
| 193 uint32_t sample_frame_count_; | |
| 194 | |
| 195 }; | |
| 196 | |
| 197 // The Module class. The browser calls the CreateInstance() method to create | |
| 198 // an instance of your NaCl module on the web page. The browser creates a new | |
| 199 // instance for each <embed> tag with type="application/x-nacl". | |
| 200 class SineSynthModule : public pp::Module { | |
| 201 public: | |
| 202 SineSynthModule() : pp::Module() {} | |
| 203 ~SineSynthModule() {} | |
| 204 | |
| 205 // Create and return a SineSynthInstance object. | |
| 206 virtual pp::Instance* CreateInstance(PP_Instance instance) { | |
| 207 return new SineSynthInstance(instance); | |
| 208 } | |
| 209 }; | |
| 210 | |
| 211 } // namespace sine_synth | |
| 212 | |
| 213 // Factory function called by the browser when the module is first loaded. | |
| 214 // The browser keeps a singleton of this module. It calls the | |
| 215 // CreateInstance() method on the object you return to make instances. There | |
| 216 // is one instance per <embed> tag on the page. This is the main binding | |
| 217 // point for your NaCl module with the browser. | |
| 218 namespace pp { | |
| 219 Module* CreateModule() { | |
| 220 return new sine_synth::SineSynthModule(); | |
| 221 } | |
| 222 | |
| 223 Creating an audio configuration resource | |
| 224 ======================================== | |
| 225 | |
| 226 Resources | |
| 227 --------- | |
| 228 | |
| 229 Before the module can play an audio stream, it must create two resources: an | |
| 230 audio configuration resource and an audio resource. Resources are handles to | |
| 231 objects that the browser provides to module instances. An audio resource is an | |
| 232 object that represents the state of an audio stream, including whether the | |
| 233 stream is paused or being played back, and which callback function to invoke | |
| 234 when the samples in the stream's buffer run out. An audio configuration resource | |
| 235 is an object that stores configuration data for an audio resource, including the | |
| 236 sampling frequency of the audio samples, and the number of samples that the | |
| 237 callback function must provide when the browser invokes it. | |
| 238 | |
| 239 Sample frame count | |
| 240 ------------------ | |
| 241 | |
| 242 Prior to creating an audio configuration resource, the module should call | |
| 243 ``RecommendSampleFrameCount`` to obtain a *sample frame count* from the | |
| 244 browser. The sample frame count is the number of samples that the callback | |
| 245 function must provide per channel each time the browser invokes the callback | |
| 246 function. For example, if the sample frame count is 4096 for a stereo audio | |
| 247 stream, the callback function must provide a 8192 samples (4096 for the left | |
| 248 channel and 4096 for the right channel). | |
| 249 | |
| 250 The module can request a specific sample frame count, but the browser may return | |
| 251 a different sample frame count depending on the capabilities of the client | |
| 252 device. At present, ``RecommendSampleFrameCount`` simply bound-checks the | |
| 253 requested sample frame count (see | |
| 254 ``toolchain/platform/x86_64-nacl/include/ppapi/c/ppb_audio_config.h`` for the | |
|
binji
2013/10/02 07:00:42
this location is ``include/ppapi/c/ppb_audio_confi
Jim Stichnoth
2013/10/17 21:57:36
Done.
| |
| 255 minimum and maximum sample frame counts, currently 64 and 32768). In the future, | |
| 256 ``RecommendSampleFrameCount`` may perform a more sophisticated calculation, | |
| 257 particularly if there is an intrinsic buffer size for the client device. | |
| 258 | |
| 259 Selecting a sample frame count for an audio stream involves a tradeoff between | |
| 260 latency and CPU usage. If you want your module to have short audio latency so | |
| 261 that it can rapidly change what's playing in the audio stream, you should | |
| 262 request a small sample frame count. That could be useful in gaming applications, | |
| 263 for example, where sounds have to change frequently in response to game | |
| 264 action. However, a small sample frame count results in higher CPU usage, since | |
| 265 the browser must invoke the callback function frequently to refill the audio | |
| 266 buffer. Conversely, a large sample frame count results in higher latency but | |
| 267 lower CPU usage. You should request a large sample frame count if your module | |
| 268 will play long, uninterrupted audio segments. | |
| 269 | |
| 270 Supported audio configurations | |
| 271 ------------------------------ | |
| 272 | |
| 273 After the module obtains a sample frame count, it can create an audio | |
| 274 configuration resource. Currently the Pepper audio API supports audio streams | |
| 275 with the following configuration settings: | |
| 276 | |
| 277 * sample rate: 44,100 Hz or 48,000 Hz | |
|
binji
2013/10/02 07:00:42
There is a duplicated table above. Should we remov
Jim Stichnoth
2013/10/17 21:57:36
Done.
| |
| 278 * bit depth: 16 | |
| 279 * channels: 2 | |
| 280 | |
| 281 C++ modules can create a configuration resource by instantiating a | |
| 282 ``pp::AudioConfig`` object. Check ``audio_config.h`` for the latest | |
| 283 configurations that are supported. | |
| 284 | |
| 285 .. naclcode:: | |
| 286 | |
| 287 bool SineSynthInstance::Init(uint32_t argc, | |
|
binji
2013/10/02 07:00:42
This has changed slightly, but maybe it's better t
| |
| 288 const char* argn[], | |
| 289 const char* argv[]) { | |
| 290 | |
| 291 // Ask the browser/device for an appropriate sample frame count size. | |
| 292 sample_frame_count_ = | |
| 293 pp::AudioConfig::RecommendSampleFrameCount(PP_AUDIOSAMPLERATE_44100, | |
| 294 kSampleFrameCount); | |
| 295 | |
| 296 // Create an audio configuration resource. | |
| 297 pp::AudioConfig audio_config = pp::AudioConfig(this, | |
| 298 PP_AUDIOSAMPLERATE_44100, | |
| 299 sample_frame_count_); | |
| 300 | |
| 301 // Create an audio resource. | |
| 302 audio_ = pp::Audio(this, | |
| 303 audio_config, | |
| 304 SineWaveCallback, | |
| 305 this); | |
| 306 | |
| 307 // Start playback when the module instance is initialized. | |
| 308 return audio_.StartPlayback(); | |
| 309 } | |
| 310 | |
| 311 Creating an audio resource | |
| 312 ========================== | |
| 313 | |
| 314 Once the module has created an audio configuration resource, it can create an | |
| 315 audio resource. To do so, it instantiates a ``pp::Audio`` object, passing in a | |
| 316 pointer to the module instance, the audio configuration resource, a callback | |
| 317 function, and a pointer to user data (data that is used in the callback | |
| 318 function). | |
| 319 | |
| 320 .. naclcode:: | |
|
binji
2013/10/02 07:00:42
This is a duplicate of the above code. Remove one?
Jim Stichnoth
2013/10/17 21:57:36
Done.
| |
| 321 | |
| 322 bool SineSynthInstance::Init(uint32_t argc, | |
| 323 const char* argn[], | |
| 324 const char* argv[]) { | |
| 325 | |
| 326 // Ask the browser/device for an appropriate sample frame count size. | |
| 327 sample_frame_count_ = | |
| 328 pp::AudioConfig::RecommendSampleFrameCount(PP_AUDIOSAMPLERATE_44100, | |
| 329 kSampleFrameCount); | |
| 330 | |
| 331 // Create an audio configuration resource. | |
| 332 pp::AudioConfig audio_config = pp::AudioConfig(this, | |
| 333 PP_AUDIOSAMPLERATE_44100, | |
| 334 sample_frame_count_); | |
| 335 | |
| 336 // Create an audio resource. | |
| 337 audio_ = pp::Audio(this, | |
| 338 audio_config, | |
| 339 SineWaveCallback, | |
| 340 this); | |
| 341 | |
| 342 // Start playback when the module instance is initialized. | |
| 343 return audio_.StartPlayback(); | |
| 344 } | |
| 345 | |
| 346 Implementing a callback function | |
| 347 ================================ | |
| 348 | |
| 349 The browser calls the callback function associated with an audio resource every | |
| 350 time it needs more samples to play. The callback function can generate new | |
| 351 samples (e.g., by applying sound effects), or copy pre-mixed samples into the | |
| 352 audio buffer. The example below generates new samples by computing values of a | |
| 353 sine wave. | |
| 354 | |
| 355 The last parameter passed to the callback function is generic user data that the | |
| 356 function can use in processing samples. In the example below, the user data is a | |
| 357 pointer to the module instance, which includes member variables | |
| 358 ``sample_frame_count_`` (the sample frame count obtained from the browser) and | |
| 359 ``theta_`` (the last angle that was used to compute a sine value in the previous | |
| 360 callback; this lets the function generate a smooth sine wave by starting at that | |
| 361 angle plus a small delta). | |
| 362 | |
| 363 .. naclcode:: | |
| 364 | |
| 365 class SineSynthInstance : public pp::Instance { | |
| 366 public: | |
| 367 ... | |
| 368 | |
| 369 private: | |
| 370 static void SineWaveCallback(void* samples, | |
| 371 uint32_t buffer_size, | |
| 372 void* data) { | |
| 373 | |
| 374 // The user data in this example is a pointer to the module instance. | |
| 375 SineSynthInstance* sine_synth_instance = | |
| 376 reinterpret_cast<SineSynthInstance*>(data); | |
|
binji
2013/10/02 07:00:42
hmm, these reinterpret_casts should really be stat
| |
| 377 | |
| 378 // Delta by which to increase theta_ for each sample. | |
| 379 const double delta = kTwoPi * kFrequency / PP_AUDIOSAMPLERATE_44100; | |
| 380 // Amount by which to scale up the computed sine value. | |
| 381 const int16_t max_int16 = std::numeric_limits<int16_t>::max(); | |
| 382 | |
| 383 int16_t* buff = reinterpret_cast<int16_t*>(samples); | |
| 384 | |
| 385 // Make sure we can't write outside the buffer. | |
| 386 assert(buffer_size >= (sizeof(*buff) * kChannels * | |
| 387 sine_synth_instance->sample_frame_count_)); | |
| 388 | |
| 389 for (size_t sample_i = 0; | |
| 390 sample_i < sine_synth_instance->sample_frame_count_; | |
| 391 ++sample_i, sine_synth_instance->theta_ += delta) { | |
| 392 | |
| 393 // Keep theta_ from going beyond 2*Pi. | |
| 394 if (sine_synth_instance->theta_ > kTwoPi) { | |
| 395 sine_synth_instance->theta_ -= kTwoPi; | |
| 396 } | |
| 397 | |
| 398 // Compute the sine value for the current theta_, scale it up, | |
| 399 // and write it into the buffer once for each channel. | |
| 400 double sin_value(std::sin(sine_synth_instance->theta_)); | |
| 401 int16_t scaled_value = static_cast<int16_t>(sin_value * max_int16); | |
| 402 for (size_t channel = 0; channel < kChannels; ++channel) { | |
| 403 *buff++ = scaled_value; | |
| 404 } | |
|
binji
2013/10/02 07:00:42
This code is basically the same as the example. I
| |
| 405 } | |
| 406 } | |
| 407 | |
| 408 ... | |
| 409 }; | |
| 410 | |
| 411 Application threads and real-time requirements | |
| 412 ---------------------------------------------- | |
| 413 | |
| 414 The callback function runs in a background application thread. This allows audio | |
| 415 processing to continue even when the application is busy doing something | |
| 416 else. If the main application thread and the callback thread access the same | |
| 417 data, you may be tempted to use a lock to control access to that data. You | |
| 418 should avoid the use of locks in the callback thread, however, as attempting to | |
| 419 acquire a lock may cause the thread to get swapped out, resulting in audio | |
| 420 dropouts. | |
| 421 | |
| 422 In general, you must program the callback thread carefully, as the Pepper audio | |
| 423 API is a very low level API that needs to meet hard real-time requirements. If | |
| 424 the callback thread spends too much time processing, it can easily miss the | |
| 425 real-time deadline, resulting in audio dropouts. One way the callback thread can | |
| 426 miss the deadline is by taking too much time doing computation. Another way the | |
| 427 callback thread can miss the deadline is by executing a function call that swaps | |
| 428 out the callback thread. Unfortunately, such function calls include just about | |
| 429 all C Run-Time (CRT) library calls and Pepper API calls. The callback thread | |
| 430 should therefore avoid calls to malloc, gettimeofday, mutex, condvars, critical | |
| 431 sections, and so forth; any such calls could attempt to take a lock and swap out | |
| 432 the callback thread, which would be disastrous for audio playback. Similarly, | |
| 433 the callback thread should avoid Pepper API calls. Audio dropouts due to thread | |
| 434 swapping can be very rare and very hard to track down and debug---it's best to | |
| 435 avoid making system/Pepper calls in the first place. In short, the audio | |
| 436 (callback) thread should use "lock-free" techniques and avoid making CRT library | |
| 437 calls. | |
| 438 | |
| 439 One other issue to be aware of is that the ``StartPlayback`` function (discussed | |
| 440 below) is an asynchronous RPC; i.e., it does not block. That means that the | |
| 441 callback function may not be called immediately after the call to | |
| 442 ``StartPlayback``. If it's important to synchronize the callback thread with | |
| 443 another thread so that the audio stream starts playing simultaneously with | |
| 444 another action in your application, you must handle such synchronization | |
| 445 manually. | |
| 446 | |
| 447 Starting and stopping playback | |
| 448 ============================== | |
| 449 | |
| 450 To start and stop audio playback, the module simply calls the ``StartPlayback`` | |
| 451 and ``StopPlayback`` methods of the ``Audio`` object. In the example below, the | |
| 452 module calls ``StartPlayback`` when the module instance is created; a more | |
|
binji
2013/10/02 07:00:42
This is no longer true, the StartPlayback/StopPlay
Jim Stichnoth
2013/10/17 21:57:36
Done.
| |
| 453 realistic use would be for a module to start playback in response to user input. | |
| 454 | |
| 455 .. naclcode:: | |
| 456 | |
| 457 bool SineSynthInstance::Init(uint32_t argc, | |
| 458 const char* argn[], | |
| 459 const char* argv[]) { | |
| 460 | |
| 461 // Ask the browser/device for an appropriate sample frame count size. | |
| 462 sample_frame_count_ = | |
| 463 pp::AudioConfig::RecommendSampleFrameCount(PP_AUDIOSAMPLERATE_44100, | |
| 464 kSampleFrameCount); | |
| 465 | |
| 466 // Create an audio configuration resource. | |
| 467 pp::AudioConfig audio_config = pp::AudioConfig(this, | |
| 468 PP_AUDIOSAMPLERATE_44100, | |
| 469 sample_frame_count_); | |
| 470 | |
| 471 // Create an audio resource. | |
| 472 audio_ = pp::Audio(this, | |
| 473 audio_config, | |
| 474 SineWaveCallback, | |
| 475 this); | |
| 476 | |
| 477 // Start playback when the module instance is initialized. | |
| 478 return audio_.StartPlayback(); | |
| 479 } | |
| OLD | NEW |