blob: 99bdf7f7d2c8e927ba8d7961eb665b8a6173acce [file] [log] [blame]
// 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(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 <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