| Index: ppapi/native_client/tests/ppapi_example_audio/audio.cc
|
| ===================================================================
|
| --- ppapi/native_client/tests/ppapi_example_audio/audio.cc (revision 0)
|
| +++ ppapi/native_client/tests/ppapi_example_audio/audio.cc (revision 0)
|
| @@ -0,0 +1,302 @@
|
| +// Copyright (c) 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 <nacl/nacl_log.h>
|
| +
|
| +#include <stdint.h>
|
| +#include <stdio.h>
|
| +#include <stdlib.h>
|
| +#include <string.h>
|
| +
|
| +#include <cmath>
|
| +#include <limits>
|
| +#include <string>
|
| +#include <nacl/nacl_inttypes.h>
|
| +
|
| +#include "native_client/src/shared/ppapi_proxy/utility.h"
|
| +#include "ppapi/c/pp_bool.h"
|
| +#include "ppapi/c/pp_errors.h"
|
| +#include "ppapi/c/ppb_audio.h"
|
| +#include "ppapi/c/ppb_audio_config.h"
|
| +#include "ppapi/cpp/audio.h"
|
| +#include "ppapi/cpp/audio_config.h"
|
| +#include "ppapi/cpp/completion_callback.h"
|
| +#include "ppapi/cpp/instance.h"
|
| +#include "ppapi/cpp/module.h"
|
| +#include "ppapi/cpp/var.h"
|
| +
|
| +
|
| +// Most of this example is borrowed from ppapi/examples/audio/audio.cc
|
| +
|
| +// Separate left and right frequency to make sure we didn't swap L & R.
|
| +// Sounds pretty horrible, though...
|
| +const double kDefaultFrequencyLeft = 400.0;
|
| +const double kDefaultFrequencyRight = 1000.0;
|
| +const uint32_t kDefaultDuration = 10000;
|
| +
|
| +// This sample frequency is guaranteed to work.
|
| +const PP_AudioSampleRate kSampleFrequency = PP_AUDIOSAMPLERATE_44100;
|
| +// Buffer size in units of sample frames.
|
| +// 4096 is a conservative size that should avoid underruns on most systems.
|
| +const uint32_t kSampleFrameCount = 4096;
|
| +
|
| +const double kPi = 3.141592653589;
|
| +const double kTwoPi = 2.0 * kPi;
|
| +
|
| +void LogFailure(const char* msg) {
|
| + NaClLog(LOG_ERROR, "\n*** FAILURE **** example: %s", msg);
|
| +}
|
| +
|
| +class MyInstance : public pp::Instance {
|
| + private:
|
| + void ParseArgs(uint32_t argc, const char* argn[], const char* argv[]) {
|
| + NaClLog(1, "example: parsing %d args\n", static_cast<int>(argc));
|
| + for (uint32_t i = 0; i < argc; ++i) {
|
| + NaClLog(1, "example: arg %d: [%s] [%s]\n",
|
| + static_cast<int>(i), argn[i], argv[i]);
|
| + const std::string tag = argn[i];
|
| + if (tag == "frequency_l") frequency_l_ = strtod(argv[i], 0);
|
| + if (tag == "frequency_r") frequency_r_ = strtod(argv[i], 0);
|
| + if (tag == "amplitude_l") amplitude_l_ = strtod(argv[i], 0);
|
| + if (tag == "amplitude_r") amplitude_r_ = strtod(argv[i], 0);
|
| + if (tag == "duration_msec") duration_msec_ = strtod(argv[i], 0);
|
| + if (tag == "basic_tests") basic_tests_ = (0 != atoi(argv[i]));
|
| + if (tag == "stress_tests") stress_tests_ = (0 != atoi(argv[i]));
|
| + if (tag == "headless") headless_ = (0 != atoi(argv[i]));
|
| + // ignore other tags
|
| + }
|
| + }
|
| +
|
| + public:
|
| + explicit MyInstance(PP_Instance instance)
|
| + : pp::Instance(instance),
|
| + config_(NULL),
|
| + audio_(NULL),
|
| + audio_wave_l_(0.0),
|
| + audio_wave_r_(0.0),
|
| + frequency_l_(kDefaultFrequencyLeft),
|
| + frequency_r_(kDefaultFrequencyRight),
|
| + amplitude_l_(1.0),
|
| + amplitude_r_(1.0),
|
| + headless_(false),
|
| + basic_tests_(false),
|
| + stress_tests_(false),
|
| + duration_msec_(kDefaultDuration),
|
| + obtained_sample_frame_count_(0),
|
| + callback_count_(0) {}
|
| +
|
| + virtual void HandleMessage(const pp::Var& message) {
|
| + NaClLog(1, "example: received HandleMessage\n");
|
| + if (message.is_string()) {
|
| + if (message.AsString() == "StartPlayback") {
|
| + StartOutput();
|
| + }
|
| + }
|
| + }
|
| +
|
| + void StartOutput() {
|
| + bool audio_start_playback = audio_->StartPlayback();
|
| + CHECK(true == audio_start_playback);
|
| + NaClLog(1, "example: frequencies are %f %f\n", frequency_l_, frequency_r_);
|
| + NaClLog(1, "example: amplitudes are %f %f\n", amplitude_l_, amplitude_r_);
|
| + NaClLog(1, "example: Scheduling StopOutput on main thread in %"
|
| + NACL_PRIu32"msec\n", duration_msec_);
|
| + // Schedule a callback in duration_msec_ to stop audio output
|
| + pp::CompletionCallback cc(StopOutput, this);
|
| + pp::Module::Get()->core()->CallOnMainThread(duration_msec_, cc, PP_OK);
|
| + }
|
| +
|
| + static void StopOutput(void* user_data, int32_t err) {
|
| + MyInstance* instance = static_cast<MyInstance*>(user_data);
|
| +
|
| + const int kMaxResult = 256;
|
| + char result[kMaxResult];
|
| + NaClLog(1, "example: StopOutput() invoked on main thread\n");
|
| + if (PP_OK == err) {
|
| + if (instance->audio_->StopPlayback()) {
|
| + // In headless mode, the build bots may not have an audio driver, in
|
| + // which case the callback won't be invoked.
|
| + // TODO(nfullagar): Other ways to determine if machine has audio
|
| + // capabilities. Currently PPAPI returns a valid resource regardless.
|
| + if ((instance->callback_count_ >= 2) || instance->headless_) {
|
| + snprintf(result, kMaxResult, "StopOutput:PASSED");
|
| + } else {
|
| + snprintf(result, kMaxResult, "StopOutput:FAILED - too "
|
| + "few callbacks (only %d callbacks detected)",
|
| + static_cast<int>(instance->callback_count_));
|
| + }
|
| + }
|
| + } else {
|
| + snprintf(result, kMaxResult,
|
| + "StopOutput: FAILED - returned err is %d", static_cast<int>(err));
|
| + }
|
| + // Release audio & config instance.
|
| + delete instance->audio_;
|
| + delete instance->config_;
|
| + instance->audio_ = NULL;
|
| + instance->config_ = NULL;
|
| + // At this point the test has finished, report result.
|
| + pp::Var message(result);
|
| + instance->PostMessage(message);
|
| + }
|
| +
|
| + // To enable basic tests, use basic_tests="1" in the embed tag.
|
| + void BasicTests() {
|
| + // Verify obtained_sample_frame_count isn't out of range.
|
| + CHECK(obtained_sample_frame_count_ >= PP_AUDIOMINSAMPLEFRAMECOUNT);
|
| + CHECK(obtained_sample_frame_count_ <= PP_AUDIOMAXSAMPLEFRAMECOUNT);
|
| + // Do some sanity checks below; verify c & cpp interfaces agree.
|
| + // Note: This is test code and is not normally needed for an application.
|
| + PPB_GetInterface get_browser_interface =
|
| + pp::Module::Get()->get_browser_interface();
|
| + const struct PPB_AudioConfig* audio_config_interface =
|
| + static_cast<const struct PPB_AudioConfig*>(
|
| + get_browser_interface(PPB_AUDIO_CONFIG_INTERFACE));
|
| + const struct PPB_Audio* audio_interface =
|
| + static_cast<const struct PPB_Audio*>(
|
| + get_browser_interface(PPB_AUDIO_INTERFACE));
|
| + PP_Resource audio_config_resource = config_->pp_resource();
|
| + PP_Resource audio_resource = audio_->pp_resource();
|
| + NaClLog(1, "example: audio config resource: %"NACL_PRId32"\n",
|
| + audio_config_resource);
|
| + NaClLog(1, "example: audio resource: %"NACL_PRId32"\n", audio_resource);
|
| + CHECK(PP_TRUE == audio_config_interface->
|
| + IsAudioConfig(audio_config_resource));
|
| + CHECK(PP_TRUE == audio_interface->IsAudio(audio_resource));
|
| + CHECK(PP_FALSE == audio_config_interface->IsAudioConfig(audio_resource));
|
| + CHECK(PP_FALSE == audio_interface->IsAudio(audio_config_resource));
|
| + CHECK(audio_interface->GetCurrentConfig(audio_resource) ==
|
| + audio_config_resource);
|
| + CHECK(0 == audio_interface->GetCurrentConfig(audio_config_resource));
|
| + CHECK(audio_config_interface->GetSampleRate(audio_config_resource) ==
|
| + config_->sample_rate());
|
| + CHECK(audio_config_interface->GetSampleFrameCount(audio_config_resource) ==
|
| + config_->sample_frame_count());
|
| + CHECK(audio_->config().pp_resource() == audio_config_resource);
|
| + }
|
| +
|
| + // To enable stress tests, use stress_tests="1" in the embed tag.
|
| + void StressTests() {
|
| + // Attempt to create many audio devices, then immediately shut them down.
|
| + // Chrome may generate some warnings on the console, but should not crash.
|
| + const int kNumManyAudio = 1000;
|
| + pp::Audio* many_audio[kNumManyAudio];
|
| + for (int i = 0; i < kNumManyAudio; ++i)
|
| + many_audio[i] = new pp::Audio(this, *config_, SilenceCallback, this);
|
| + for (int i = 0; i < kNumManyAudio; ++i)
|
| + CHECK(true == many_audio[i]->StartPlayback());
|
| + for (int i = 0; i < kNumManyAudio; ++i)
|
| + delete many_audio[i];
|
| + }
|
| +
|
| + void TestSuite() {
|
| + if (basic_tests_)
|
| + BasicTests();
|
| + if (stress_tests_)
|
| + StressTests();
|
| + }
|
| +
|
| + virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
|
| + ParseArgs(argc, argn, argv);
|
| + obtained_sample_frame_count_ = pp::AudioConfig::RecommendSampleFrameCount(
|
| + kSampleFrequency, kSampleFrameCount);
|
| + config_ = new
|
| + pp::AudioConfig(this, kSampleFrequency, obtained_sample_frame_count_);
|
| + CHECK(NULL != config_);
|
| + audio_ = new pp::Audio(this, *config_, SineWaveCallback, this);
|
| + CHECK(NULL != audio_);
|
| + // Run through test suite before attempting real playback.
|
| + TestSuite();
|
| + return true;
|
| + }
|
| +
|
| + private:
|
| + static void SineWaveCallback(void* samples, uint32_t num_bytes, void* thiz) {
|
| + MyInstance* instance = reinterpret_cast<MyInstance*>(thiz);
|
| + const double delta_l = kTwoPi * instance->frequency_l_ / kSampleFrequency;
|
| + const double delta_r = kTwoPi * instance->frequency_r_ / kSampleFrequency;
|
| +
|
| + // Verify num_bytes and obtained_sample_frame_count match up.
|
| + const int kNumChannelsForStereo = 2;
|
| + const int kSizeOfSample = sizeof(int16_t);
|
| + const size_t single_sample = kNumChannelsForStereo * kSizeOfSample;
|
| +
|
| + // CHECK inside callback is only for testing purposes.
|
| + CHECK(instance->obtained_sample_frame_count_ * single_sample == num_bytes);
|
| +
|
| + // Use per channel audio wave value to avoid clicks on buffer boundries.
|
| + double wave_l = instance->audio_wave_l_;
|
| + double wave_r = instance->audio_wave_r_;
|
| + const int16_t max_int16 = std::numeric_limits<int16_t>::max();
|
| + int16_t* buf = reinterpret_cast<int16_t*>(samples);
|
| + for (size_t i = 0; i < instance->obtained_sample_frame_count_; ++i) {
|
| + const double l = sin(wave_l) * instance->amplitude_l_ * max_int16;
|
| + const double r = sin(wave_r) * instance->amplitude_r_ * max_int16;
|
| + *buf++ = static_cast<int16_t>(l);
|
| + *buf++ = static_cast<int16_t>(r);
|
| + // Add delta, keep within -kTwoPi..kTwoPi to preserve precision.
|
| + wave_l += delta_l;
|
| + if (wave_l > kTwoPi)
|
| + wave_l -= kTwoPi * 2.0;
|
| + wave_r += delta_r;
|
| + if (wave_r > kTwoPi)
|
| + wave_r -= kTwoPi * 2.0;
|
| + }
|
| + // Store current value to use as starting point for next callback.
|
| + instance->audio_wave_l_ = wave_l;
|
| + instance->audio_wave_r_ = wave_r;
|
| +
|
| + ++instance->callback_count_;
|
| + }
|
| +
|
| + static void SilenceCallback(void* samples, uint32_t num_bytes, void* thiz) {
|
| + memset(samples, 0, num_bytes);
|
| + }
|
| +
|
| + // Audio config resource. Allocated in Init().
|
| + pp::AudioConfig* config_;
|
| +
|
| + // Audio resource. Allocated in Init().
|
| + pp::Audio* audio_;
|
| +
|
| + // Current audio wave position, used to prevent sine wave skips
|
| + // on buffer boundaries.
|
| + double audio_wave_l_;
|
| + double audio_wave_r_;
|
| +
|
| + double frequency_l_;
|
| + double frequency_r_;
|
| +
|
| + double amplitude_l_;
|
| + double amplitude_r_;
|
| +
|
| + bool headless_;
|
| +
|
| + bool basic_tests_;
|
| + bool stress_tests_;
|
| +
|
| + uint32_t duration_msec_;
|
| + uint32_t obtained_sample_frame_count_;
|
| +
|
| + int callback_count_;
|
| +};
|
| +
|
| +class MyModule : public pp::Module {
|
| + public:
|
| + // Override CreateInstance to create your customized Instance object.
|
| + virtual pp::Instance* CreateInstance(PP_Instance instance) {
|
| + return new MyInstance(instance);
|
| + }
|
| +};
|
| +
|
| +namespace pp {
|
| +
|
| +// Factory function for your specialization of the Module object.
|
| +Module* CreateModule() {
|
| + NaClLogModuleInit();
|
| + return new MyModule();
|
| +}
|
| +
|
| +} // namespace pp
|
|
|