// Copyright (c) 2012 The Chromium 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 #include #include #include #include "ppapi/cpp/audio.h" #include "ppapi/cpp/instance.h" #include "ppapi/cpp/module.h" #include "ppapi/cpp/var.h" namespace { const char* const kPlaySoundId = "playSound"; const char* const kStopSoundId = "stopSound"; const char* const kSetFrequencyId = "setFrequency"; static const char kMessageArgumentSeparator = ':'; const double kDefaultFrequency = 440.0; const double kPi = 3.141592653589; const double kTwoPi = 2.0 * kPi; // The sample count we will request. const uint32_t kSampleFrameCount = 4096u; // Only supporting stereo audio for now. const uint32_t kChannels = 2u; } // namespace namespace sine_synth { // The Instance class. One of these exists for each instance of your NaCl // module on the web page. The browser will ask the Module object to create // a new Instance for each occurrence of the tag that has these // attributes: // type="application/x-nacl" // src="sine_synth.nmf" class SineSynthInstance : public pp::Instance { public: explicit SineSynthInstance(PP_Instance instance) : pp::Instance(instance), frequency_(kDefaultFrequency), theta_(0), sample_frame_count_(kSampleFrameCount) {} virtual ~SineSynthInstance() {} // Called by the browser once the NaCl module is loaded and ready to // initialize. Creates a Pepper audio context and initializes it. Returns // true on success. Returning false causes the NaCl module to be deleted and // no other functions to be called. virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]); // Called by the browser to handle the postMessage() call in Javascript. // |var_message| is expected to be a string that contains the name of the // method to call. Note that the setFrequency method takes a single // parameter, the frequency. The frequency parameter is encoded as a string // and appended to the 'setFrequency' method name after a ':'. Examples // of possible message strings are: // playSound // stopSound // setFrequency:880 // If |var_message| is not a recognized method name, this method does nothing. virtual void HandleMessage(const pp::Var& var_message); // Set the frequency of the sine wave to |frequency|. Posts a message back // to the browser with the new frequency value. void SetFrequency(double frequency); // The frequency property accessor. double frequency() const { return frequency_; } private: static void SineWaveCallback(void* samples, uint32_t buffer_size, void* data) { SineSynthInstance* sine_synth_instance = reinterpret_cast(data); const double frequency = sine_synth_instance->frequency(); const double delta = kTwoPi * frequency / PP_AUDIOSAMPLERATE_44100; const int16_t max_int16 = std::numeric_limits::max(); int16_t* buff = reinterpret_cast(samples); // Make sure we can't write outside the buffer. assert(buffer_size >= (sizeof(*buff) * kChannels * sine_synth_instance->sample_frame_count_)); for (size_t sample_i = 0; sample_i < sine_synth_instance->sample_frame_count_; ++sample_i, sine_synth_instance->theta_ += delta) { // Keep theta_ from going beyond 2*Pi. if (sine_synth_instance->theta_ > kTwoPi) { sine_synth_instance->theta_ -= kTwoPi; } double sin_value(std::sin(sine_synth_instance->theta_)); int16_t scaled_value = static_cast(sin_value * max_int16); for (size_t channel = 0; channel < kChannels; ++channel) { *buff++ = scaled_value; } } } pp::Audio audio_; double frequency_; // The last parameter sent to the sin function. Used to prevent sine wave // skips on buffer boundaries. double theta_; // The count of sample frames per channel in an audio buffer. uint32_t sample_frame_count_; }; bool SineSynthInstance::Init(uint32_t argc, const char* argn[], const char* argv[]) { // Ask the device for an appropriate sample count size. sample_frame_count_ = pp::AudioConfig::RecommendSampleFrameCount(this, PP_AUDIOSAMPLERATE_44100, kSampleFrameCount); audio_ = pp::Audio(this, pp::AudioConfig(this, PP_AUDIOSAMPLERATE_44100, sample_frame_count_), SineWaveCallback, this); return true; } void SineSynthInstance::HandleMessage(const pp::Var& var_message) { if (!var_message.is_string()) { return; } std::string message = var_message.AsString(); if (message == kPlaySoundId) { audio_.StartPlayback(); } else if (message == kStopSoundId) { audio_.StopPlayback(); } else if (message.find(kSetFrequencyId) == 0) { // The argument to setFrequency is everything after the first ':'. size_t sep_pos = message.find_first_of(kMessageArgumentSeparator); if (sep_pos != std::string::npos) { std::string string_arg = message.substr(sep_pos + 1); // Got the argument value as a string: try to convert it to a number. std::istringstream stream(string_arg); double double_value; if (stream >> double_value) { SetFrequency(double_value); return; } } } } void SineSynthInstance::SetFrequency(double frequency) { frequency_ = frequency; PostMessage(pp::Var(frequency_)); } // The Module class. The browser calls the CreateInstance() method to create // an instance of your NaCl module on the web page. The browser creates a new // instance for each tag with type="application/x-nacl". class SineSynthModule : public pp::Module { public: SineSynthModule() : pp::Module() {} ~SineSynthModule() {} // Create and return a HelloWorldInstance object. virtual pp::Instance* CreateInstance(PP_Instance instance) { return new SineSynthInstance(instance); } }; } // namespace sine_synth // Factory function called by the browser when the module is first loaded. // The browser keeps a singleton of this module. It calls the // CreateInstance() method on the object you return to make instances. There // is one instance per tag on the page. This is the main binding // point for your NaCl module with the browser. namespace pp { Module* CreateModule() { return new sine_synth::SineSynthModule(); } } // namespace pp