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
14namespace {
15const char* const kPlaySoundId = "playSound";
16const char* const kStopSoundId = "stopSound";
17const char* const kSetFrequencyId = "setFrequency";
18static const char kMessageArgumentSeparator = ':';
19
20const double kDefaultFrequency = 440.0;
21const double kPi = 3.141592653589;
22const double kTwoPi = 2.0 * kPi;
23// The sample count we will request.
24const uint32_t kSampleFrameCount = 4096u;
25// Only supporting stereo audio for now.
26const uint32_t kChannels = 2u;
27}  // namespace
28
29class 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
99bool 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
113void 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
138void AudioInstance::SetFrequency(double frequency) {
139  frequency_ = frequency;
140  PostMessage(pp::Var(frequency_));
141}
142
143class 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
153namespace pp {
154Module* CreateModule() { return new AudioModule(); }
155}  // namespace pp
156