summaryrefslogtreecommitdiffstats
path: root/native_client_sdk/src/examples/sine_synth/sine_synth.cc
blob: d2b3f4d8c1bdb4128f2948316b408eb35f4e9be5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// 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 <cassert>
#include <cmath>
#include <limits>
#include <sstream>
#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 <embed> 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<SineSynthInstance*>(data);
    const double frequency = sine_synth_instance->frequency();
    const double delta = kTwoPi * frequency / PP_AUDIOSAMPLERATE_44100;
    const int16_t max_int16 = std::numeric_limits<int16_t>::max();

    int16_t* buff = reinterpret_cast<int16_t*>(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<int16_t>(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(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 <embed> 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 <embed> 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