1 // Copyright (c) 2012 The Chromium 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 #include <cassert> 6 #include <cmath> 7 #include <limits> 8 #include <sstream> 9 #include "ppapi/cpp/audio.h" 10 #include "ppapi/cpp/instance.h" 11 #include "ppapi/cpp/module.h" 12 #include "ppapi/cpp/var.h" 13 14 namespace { 15 const char* const kPlaySoundId = "playSound"; 16 const char* const kStopSoundId = "stopSound"; 17 const char* const kSetFrequencyId = "setFrequency"; 18 static const char kMessageArgumentSeparator = ':'; 19 20 const double kDefaultFrequency = 440.0; 21 const double kPi = 3.141592653589; 22 const double kTwoPi = 2.0 * kPi; 23 // The sample count we will request. 24 const uint32_t kSampleFrameCount = 4096u; 25 // Only supporting stereo audio for now. 26 const uint32_t kChannels = 2u; 27 } // namespace 28 29 class AudioInstance : public pp::Instance { 30 public: 31 explicit AudioInstance(PP_Instance instance) 32 : pp::Instance(instance), 33 frequency_(kDefaultFrequency), 34 theta_(0), 35 sample_frame_count_(kSampleFrameCount) {} 36 virtual ~AudioInstance() {} 37 38 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]); 39 40 // Called by the browser to handle the postMessage() call in Javascript. 41 // |var_message| is expected to be a string that contains the name of the 42 // method to call. Note that the setFrequency method takes a single 43 // parameter, the frequency. The frequency parameter is encoded as a string 44 // and appended to the 'setFrequency' method name after a ':'. Examples 45 // of possible message strings are: 46 // playSound 47 // stopSound 48 // setFrequency:880 49 // If |var_message| is not a recognized method name, this method does nothing. 50 virtual void HandleMessage(const pp::Var& var_message); 51 52 // Set the frequency of the sine wave to |frequency|. Posts a message back 53 // to the browser with the new frequency value. 54 void SetFrequency(double frequency); 55 56 // The frequency property accessor. 57 double frequency() const { return frequency_; } 58 59 private: 60 static void SineWaveCallback(void* samples, 61 uint32_t buffer_size, 62 void* data) { 63 AudioInstance* instance = reinterpret_cast<AudioInstance*>(data); 64 const double frequency = instance->frequency(); 65 const double delta = kTwoPi * frequency / PP_AUDIOSAMPLERATE_44100; 66 const int16_t max_int16 = std::numeric_limits<int16_t>::max(); 67 68 int16_t* buff = reinterpret_cast<int16_t*>(samples); 69 70 // Make sure we can't write outside the buffer. 71 assert(buffer_size >= 72 (sizeof(*buff) * kChannels * instance->sample_frame_count_)); 73 74 for (size_t sample_i = 0; sample_i < instance->sample_frame_count_; 75 ++sample_i, instance->theta_ += delta) { 76 // Keep theta_ from going beyond 2*Pi. 77 if (instance->theta_ > kTwoPi) { 78 instance->theta_ -= kTwoPi; 79 } 80 double sin_value(std::sin(instance->theta_)); 81 int16_t scaled_value = static_cast<int16_t>(sin_value * max_int16); 82 for (size_t channel = 0; channel < kChannels; ++channel) { 83 *buff++ = scaled_value; 84 } 85 } 86 } 87 88 pp::Audio audio_; 89 double frequency_; 90 91 // The last parameter sent to the sin function. Used to prevent sine wave 92 // skips on buffer boundaries. 93 double theta_; 94 95 // The count of sample frames per channel in an audio buffer. 96 uint32_t sample_frame_count_; 97 }; 98 99 bool AudioInstance::Init(uint32_t argc, 100 const char* argn[], 101 const char* argv[]) { 102 // Ask the device for an appropriate sample count size. 103 sample_frame_count_ = pp::AudioConfig::RecommendSampleFrameCount( 104 this, PP_AUDIOSAMPLERATE_44100, kSampleFrameCount); 105 audio_ = pp::Audio( 106 this, 107 pp::AudioConfig(this, PP_AUDIOSAMPLERATE_44100, sample_frame_count_), 108 SineWaveCallback, 109 this); 110 return true; 111 } 112 113 void AudioInstance::HandleMessage(const pp::Var& var_message) { 114 if (!var_message.is_string()) { 115 return; 116 } 117 std::string message = var_message.AsString(); 118 if (message == kPlaySoundId) { 119 audio_.StartPlayback(); 120 } else if (message == kStopSoundId) { 121 audio_.StopPlayback(); 122 } else if (message.find(kSetFrequencyId) == 0) { 123 // The argument to setFrequency is everything after the first ':'. 124 size_t sep_pos = message.find_first_of(kMessageArgumentSeparator); 125 if (sep_pos != std::string::npos) { 126 std::string string_arg = message.substr(sep_pos + 1); 127 // Got the argument value as a string: try to convert it to a number. 128 std::istringstream stream(string_arg); 129 double double_value; 130 if (stream >> double_value) { 131 SetFrequency(double_value); 132 return; 133 } 134 } 135 } 136 } 137 138 void AudioInstance::SetFrequency(double frequency) { 139 frequency_ = frequency; 140 PostMessage(pp::Var(frequency_)); 141 } 142 143 class AudioModule : public pp::Module { 144 public: 145 AudioModule() : pp::Module() {} 146 ~AudioModule() {} 147 148 virtual pp::Instance* CreateInstance(PP_Instance instance) { 149 return new AudioInstance(instance); 150 } 151 }; 152 153 namespace pp { 154 Module* CreateModule() { return new AudioModule(); } 155 } // namespace pp 156