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