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 |
| 6 #include <nacl/nacl_log.h> |
| 7 |
| 8 #include <stdint.h> |
| 9 #include <stdio.h> |
| 10 #include <stdlib.h> |
| 11 #include <string.h> |
| 12 |
| 13 #include <cmath> |
| 14 #include <limits> |
| 15 #include <string> |
| 16 #include <nacl/nacl_inttypes.h> |
| 17 |
| 18 #include "native_client/src/shared/ppapi_proxy/utility.h" |
| 19 #include "ppapi/c/pp_bool.h" |
| 20 #include "ppapi/c/pp_errors.h" |
| 21 #include "ppapi/c/ppb_audio.h" |
| 22 #include "ppapi/c/ppb_audio_config.h" |
| 23 #include "ppapi/cpp/audio.h" |
| 24 #include "ppapi/cpp/audio_config.h" |
| 25 #include "ppapi/cpp/completion_callback.h" |
| 26 #include "ppapi/cpp/instance.h" |
| 27 #include "ppapi/cpp/module.h" |
| 28 #include "ppapi/cpp/var.h" |
| 29 |
| 30 |
| 31 // Most of this example is borrowed from ppapi/examples/audio/audio.cc |
| 32 |
| 33 // Separate left and right frequency to make sure we didn't swap L & R. |
| 34 // Sounds pretty horrible, though... |
| 35 const double kDefaultFrequencyLeft = 400.0; |
| 36 const double kDefaultFrequencyRight = 1000.0; |
| 37 const uint32_t kDefaultDuration = 10000; |
| 38 |
| 39 // This sample frequency is guaranteed to work. |
| 40 const PP_AudioSampleRate kSampleFrequency = PP_AUDIOSAMPLERATE_44100; |
| 41 // Buffer size in units of sample frames. |
| 42 // 4096 is a conservative size that should avoid underruns on most systems. |
| 43 const uint32_t kSampleFrameCount = 4096; |
| 44 |
| 45 const double kPi = 3.141592653589; |
| 46 const double kTwoPi = 2.0 * kPi; |
| 47 |
| 48 void LogFailure(const char* msg) { |
| 49 NaClLog(LOG_ERROR, "\n*** FAILURE **** example: %s", msg); |
| 50 } |
| 51 |
| 52 class MyInstance : public pp::Instance { |
| 53 private: |
| 54 void ParseArgs(uint32_t argc, const char* argn[], const char* argv[]) { |
| 55 NaClLog(1, "example: parsing %d args\n", static_cast<int>(argc)); |
| 56 for (uint32_t i = 0; i < argc; ++i) { |
| 57 NaClLog(1, "example: arg %d: [%s] [%s]\n", |
| 58 static_cast<int>(i), argn[i], argv[i]); |
| 59 const std::string tag = argn[i]; |
| 60 if (tag == "frequency_l") frequency_l_ = strtod(argv[i], 0); |
| 61 if (tag == "frequency_r") frequency_r_ = strtod(argv[i], 0); |
| 62 if (tag == "amplitude_l") amplitude_l_ = strtod(argv[i], 0); |
| 63 if (tag == "amplitude_r") amplitude_r_ = strtod(argv[i], 0); |
| 64 if (tag == "duration_msec") duration_msec_ = strtod(argv[i], 0); |
| 65 if (tag == "basic_tests") basic_tests_ = (0 != atoi(argv[i])); |
| 66 if (tag == "stress_tests") stress_tests_ = (0 != atoi(argv[i])); |
| 67 if (tag == "headless") headless_ = (0 != atoi(argv[i])); |
| 68 // ignore other tags |
| 69 } |
| 70 } |
| 71 |
| 72 public: |
| 73 explicit MyInstance(PP_Instance instance) |
| 74 : pp::Instance(instance), |
| 75 config_(NULL), |
| 76 audio_(NULL), |
| 77 audio_wave_l_(0.0), |
| 78 audio_wave_r_(0.0), |
| 79 frequency_l_(kDefaultFrequencyLeft), |
| 80 frequency_r_(kDefaultFrequencyRight), |
| 81 amplitude_l_(1.0), |
| 82 amplitude_r_(1.0), |
| 83 headless_(false), |
| 84 basic_tests_(false), |
| 85 stress_tests_(false), |
| 86 duration_msec_(kDefaultDuration), |
| 87 obtained_sample_frame_count_(0), |
| 88 callback_count_(0) {} |
| 89 |
| 90 virtual void HandleMessage(const pp::Var& message) { |
| 91 NaClLog(1, "example: received HandleMessage\n"); |
| 92 if (message.is_string()) { |
| 93 if (message.AsString() == "StartPlayback") { |
| 94 StartOutput(); |
| 95 } |
| 96 } |
| 97 } |
| 98 |
| 99 void StartOutput() { |
| 100 bool audio_start_playback = audio_->StartPlayback(); |
| 101 CHECK(true == audio_start_playback); |
| 102 NaClLog(1, "example: frequencies are %f %f\n", frequency_l_, frequency_r_); |
| 103 NaClLog(1, "example: amplitudes are %f %f\n", amplitude_l_, amplitude_r_); |
| 104 NaClLog(1, "example: Scheduling StopOutput on main thread in %" |
| 105 NACL_PRIu32"msec\n", duration_msec_); |
| 106 // Schedule a callback in duration_msec_ to stop audio output |
| 107 pp::CompletionCallback cc(StopOutput, this); |
| 108 pp::Module::Get()->core()->CallOnMainThread(duration_msec_, cc, PP_OK); |
| 109 } |
| 110 |
| 111 static void StopOutput(void* user_data, int32_t err) { |
| 112 MyInstance* instance = static_cast<MyInstance*>(user_data); |
| 113 |
| 114 const int kMaxResult = 256; |
| 115 char result[kMaxResult]; |
| 116 NaClLog(1, "example: StopOutput() invoked on main thread\n"); |
| 117 if (PP_OK == err) { |
| 118 if (instance->audio_->StopPlayback()) { |
| 119 // In headless mode, the build bots may not have an audio driver, in |
| 120 // which case the callback won't be invoked. |
| 121 // TODO(nfullagar): Other ways to determine if machine has audio |
| 122 // capabilities. Currently PPAPI returns a valid resource regardless. |
| 123 if ((instance->callback_count_ >= 2) || instance->headless_) { |
| 124 snprintf(result, kMaxResult, "StopOutput:PASSED"); |
| 125 } else { |
| 126 snprintf(result, kMaxResult, "StopOutput:FAILED - too " |
| 127 "few callbacks (only %d callbacks detected)", |
| 128 static_cast<int>(instance->callback_count_)); |
| 129 } |
| 130 } |
| 131 } else { |
| 132 snprintf(result, kMaxResult, |
| 133 "StopOutput: FAILED - returned err is %d", static_cast<int>(err)); |
| 134 } |
| 135 // Release audio & config instance. |
| 136 delete instance->audio_; |
| 137 delete instance->config_; |
| 138 instance->audio_ = NULL; |
| 139 instance->config_ = NULL; |
| 140 // At this point the test has finished, report result. |
| 141 pp::Var message(result); |
| 142 instance->PostMessage(message); |
| 143 } |
| 144 |
| 145 // To enable basic tests, use basic_tests="1" in the embed tag. |
| 146 void BasicTests() { |
| 147 // Verify obtained_sample_frame_count isn't out of range. |
| 148 CHECK(obtained_sample_frame_count_ >= PP_AUDIOMINSAMPLEFRAMECOUNT); |
| 149 CHECK(obtained_sample_frame_count_ <= PP_AUDIOMAXSAMPLEFRAMECOUNT); |
| 150 // Do some sanity checks below; verify c & cpp interfaces agree. |
| 151 // Note: This is test code and is not normally needed for an application. |
| 152 PPB_GetInterface get_browser_interface = |
| 153 pp::Module::Get()->get_browser_interface(); |
| 154 const struct PPB_AudioConfig* audio_config_interface = |
| 155 static_cast<const struct PPB_AudioConfig*>( |
| 156 get_browser_interface(PPB_AUDIO_CONFIG_INTERFACE)); |
| 157 const struct PPB_Audio* audio_interface = |
| 158 static_cast<const struct PPB_Audio*>( |
| 159 get_browser_interface(PPB_AUDIO_INTERFACE)); |
| 160 PP_Resource audio_config_resource = config_->pp_resource(); |
| 161 PP_Resource audio_resource = audio_->pp_resource(); |
| 162 NaClLog(1, "example: audio config resource: %"NACL_PRId32"\n", |
| 163 audio_config_resource); |
| 164 NaClLog(1, "example: audio resource: %"NACL_PRId32"\n", audio_resource); |
| 165 CHECK(PP_TRUE == audio_config_interface-> |
| 166 IsAudioConfig(audio_config_resource)); |
| 167 CHECK(PP_TRUE == audio_interface->IsAudio(audio_resource)); |
| 168 CHECK(PP_FALSE == audio_config_interface->IsAudioConfig(audio_resource)); |
| 169 CHECK(PP_FALSE == audio_interface->IsAudio(audio_config_resource)); |
| 170 CHECK(audio_interface->GetCurrentConfig(audio_resource) == |
| 171 audio_config_resource); |
| 172 CHECK(0 == audio_interface->GetCurrentConfig(audio_config_resource)); |
| 173 CHECK(audio_config_interface->GetSampleRate(audio_config_resource) == |
| 174 config_->sample_rate()); |
| 175 CHECK(audio_config_interface->GetSampleFrameCount(audio_config_resource) == |
| 176 config_->sample_frame_count()); |
| 177 CHECK(audio_->config().pp_resource() == audio_config_resource); |
| 178 } |
| 179 |
| 180 // To enable stress tests, use stress_tests="1" in the embed tag. |
| 181 void StressTests() { |
| 182 // Attempt to create many audio devices, then immediately shut them down. |
| 183 // Chrome may generate some warnings on the console, but should not crash. |
| 184 const int kNumManyAudio = 1000; |
| 185 pp::Audio* many_audio[kNumManyAudio]; |
| 186 for (int i = 0; i < kNumManyAudio; ++i) |
| 187 many_audio[i] = new pp::Audio(this, *config_, SilenceCallback, this); |
| 188 for (int i = 0; i < kNumManyAudio; ++i) |
| 189 CHECK(true == many_audio[i]->StartPlayback()); |
| 190 for (int i = 0; i < kNumManyAudio; ++i) |
| 191 delete many_audio[i]; |
| 192 } |
| 193 |
| 194 void TestSuite() { |
| 195 if (basic_tests_) |
| 196 BasicTests(); |
| 197 if (stress_tests_) |
| 198 StressTests(); |
| 199 } |
| 200 |
| 201 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) { |
| 202 ParseArgs(argc, argn, argv); |
| 203 obtained_sample_frame_count_ = pp::AudioConfig::RecommendSampleFrameCount( |
| 204 kSampleFrequency, kSampleFrameCount); |
| 205 config_ = new |
| 206 pp::AudioConfig(this, kSampleFrequency, obtained_sample_frame_count_); |
| 207 CHECK(NULL != config_); |
| 208 audio_ = new pp::Audio(this, *config_, SineWaveCallback, this); |
| 209 CHECK(NULL != audio_); |
| 210 // Run through test suite before attempting real playback. |
| 211 TestSuite(); |
| 212 return true; |
| 213 } |
| 214 |
| 215 private: |
| 216 static void SineWaveCallback(void* samples, uint32_t num_bytes, void* thiz) { |
| 217 MyInstance* instance = reinterpret_cast<MyInstance*>(thiz); |
| 218 const double delta_l = kTwoPi * instance->frequency_l_ / kSampleFrequency; |
| 219 const double delta_r = kTwoPi * instance->frequency_r_ / kSampleFrequency; |
| 220 |
| 221 // Verify num_bytes and obtained_sample_frame_count match up. |
| 222 const int kNumChannelsForStereo = 2; |
| 223 const int kSizeOfSample = sizeof(int16_t); |
| 224 const size_t single_sample = kNumChannelsForStereo * kSizeOfSample; |
| 225 |
| 226 // CHECK inside callback is only for testing purposes. |
| 227 CHECK(instance->obtained_sample_frame_count_ * single_sample == num_bytes); |
| 228 |
| 229 // Use per channel audio wave value to avoid clicks on buffer boundries. |
| 230 double wave_l = instance->audio_wave_l_; |
| 231 double wave_r = instance->audio_wave_r_; |
| 232 const int16_t max_int16 = std::numeric_limits<int16_t>::max(); |
| 233 int16_t* buf = reinterpret_cast<int16_t*>(samples); |
| 234 for (size_t i = 0; i < instance->obtained_sample_frame_count_; ++i) { |
| 235 const double l = sin(wave_l) * instance->amplitude_l_ * max_int16; |
| 236 const double r = sin(wave_r) * instance->amplitude_r_ * max_int16; |
| 237 *buf++ = static_cast<int16_t>(l); |
| 238 *buf++ = static_cast<int16_t>(r); |
| 239 // Add delta, keep within -kTwoPi..kTwoPi to preserve precision. |
| 240 wave_l += delta_l; |
| 241 if (wave_l > kTwoPi) |
| 242 wave_l -= kTwoPi * 2.0; |
| 243 wave_r += delta_r; |
| 244 if (wave_r > kTwoPi) |
| 245 wave_r -= kTwoPi * 2.0; |
| 246 } |
| 247 // Store current value to use as starting point for next callback. |
| 248 instance->audio_wave_l_ = wave_l; |
| 249 instance->audio_wave_r_ = wave_r; |
| 250 |
| 251 ++instance->callback_count_; |
| 252 } |
| 253 |
| 254 static void SilenceCallback(void* samples, uint32_t num_bytes, void* thiz) { |
| 255 memset(samples, 0, num_bytes); |
| 256 } |
| 257 |
| 258 // Audio config resource. Allocated in Init(). |
| 259 pp::AudioConfig* config_; |
| 260 |
| 261 // Audio resource. Allocated in Init(). |
| 262 pp::Audio* audio_; |
| 263 |
| 264 // Current audio wave position, used to prevent sine wave skips |
| 265 // on buffer boundaries. |
| 266 double audio_wave_l_; |
| 267 double audio_wave_r_; |
| 268 |
| 269 double frequency_l_; |
| 270 double frequency_r_; |
| 271 |
| 272 double amplitude_l_; |
| 273 double amplitude_r_; |
| 274 |
| 275 bool headless_; |
| 276 |
| 277 bool basic_tests_; |
| 278 bool stress_tests_; |
| 279 |
| 280 uint32_t duration_msec_; |
| 281 uint32_t obtained_sample_frame_count_; |
| 282 |
| 283 int callback_count_; |
| 284 }; |
| 285 |
| 286 class MyModule : public pp::Module { |
| 287 public: |
| 288 // Override CreateInstance to create your customized Instance object. |
| 289 virtual pp::Instance* CreateInstance(PP_Instance instance) { |
| 290 return new MyInstance(instance); |
| 291 } |
| 292 }; |
| 293 |
| 294 namespace pp { |
| 295 |
| 296 // Factory function for your specialization of the Module object. |
| 297 Module* CreateModule() { |
| 298 NaClLogModuleInit(); |
| 299 return new MyModule(); |
| 300 } |
| 301 |
| 302 } // namespace pp |
OLD | NEW |