Home | History | Annotate | Download | only in audio
      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