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// Needed on Windows to get |M_PI| from math.h.
6#ifdef _WIN32
7#define _USE_MATH_DEFINES
8#endif
9
10#include <math.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include <limits>
15
16#include "ppapi/c/pp_errors.h"
17#include "ppapi/cpp/audio.h"
18#include "ppapi/cpp/audio_config.h"
19#include "ppapi/cpp/completion_callback.h"
20#include "ppapi/cpp/instance.h"
21#include "ppapi/cpp/module.h"
22#include "ppapi/cpp/view.h"
23
24// Separate left and right frequency to make sure we didn't swap L & R.
25// Sounds pretty horrible, though...
26const double kLeftFrequency = 400;
27const double kRightFrequency = 1000;
28
29// This sample frequency is guaranteed to work.
30const PP_AudioSampleRate kDefaultSampleRate = PP_AUDIOSAMPLERATE_44100;
31const uint32_t kDefaultSampleCount = 4096;
32
33const char kSampleRateAttributeName[] = "samplerate";
34
35class MyInstance : public pp::Instance {
36 public:
37  explicit MyInstance(PP_Instance instance)
38      : pp::Instance(instance),
39        visible_(false),
40        sample_rate_(kDefaultSampleRate),
41        sample_count_(0),
42        audio_wave_l_(0.0),
43        audio_wave_r_(0.0) {
44  }
45
46  virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
47    for (uint32_t i = 0; i < argc; i++) {
48      if (strcmp(kSampleRateAttributeName, argn[i]) == 0) {
49        int value = atoi(argv[i]);
50        if (value > 0 && value <= 1000000)
51          sample_rate_ = static_cast<PP_AudioSampleRate>(value);
52        else
53          return false;
54      }
55    }
56
57    pp::AudioConfig config;
58    sample_count_ = pp::AudioConfig::RecommendSampleFrameCount(
59        this, sample_rate_, kDefaultSampleCount);
60    config = pp::AudioConfig(this, sample_rate_, sample_count_);
61    audio_ = pp::Audio(this, config, SineWaveCallbackTrampoline, this);
62    return audio_.StartPlayback();
63  }
64
65  virtual void DidChangeView(const pp::View& view) {
66    // The frequency will change depending on whether the page is in the
67    // foreground or background.
68    visible_ = view.IsPageVisible();
69  }
70
71 private:
72  static void SineWaveCallbackTrampoline(void* samples,
73                                         uint32_t num_bytes,
74                                         void* thiz) {
75    static_cast<MyInstance*>(thiz)->SineWaveCallback(samples, num_bytes);
76  }
77
78  void SineWaveCallback(void* samples, uint32_t num_bytes) {
79    double delta_l = 2.0 * M_PI * kLeftFrequency / sample_rate_ /
80        (visible_ ? 1 : 2);
81    double delta_r = 2.0 * M_PI * kRightFrequency / sample_rate_ /
82        (visible_ ? 1 : 2);
83
84    // Use per channel audio wave value to avoid clicks on buffer boundries.
85    double wave_l = audio_wave_l_;
86    double wave_r = audio_wave_r_;
87    const int16_t max_int16 = std::numeric_limits<int16_t>::max();
88    int16_t* buf = reinterpret_cast<int16_t*>(samples);
89    for (size_t sample = 0; sample < sample_count_; ++sample) {
90      *buf++ = static_cast<int16_t>(sin(wave_l) * max_int16);
91      *buf++ = static_cast<int16_t>(sin(wave_r) * max_int16);
92      // Add delta, keep within -2 * M_PI .. 2 * M_PI to preserve precision.
93      wave_l += delta_l;
94      if (wave_l > 2.0 * M_PI)
95        wave_l -= 2.0 * M_PI;
96      wave_r += delta_r;
97      if (wave_r > 2.0 * M_PI)
98        wave_r -= 2.0 * M_PI;
99    }
100    // Store current value to use as starting point for next callback.
101    audio_wave_l_ = wave_l;
102    audio_wave_r_ = wave_r;
103  }
104
105  bool visible_;
106
107  PP_AudioSampleRate sample_rate_;
108  uint32_t sample_count_;
109
110  pp::Audio audio_;
111
112  // Current audio wave position, used to prevent sine wave skips
113  // on buffer boundaries.
114  double audio_wave_l_;
115  double audio_wave_r_;
116};
117
118class MyModule : public pp::Module {
119 public:
120  // Override CreateInstance to create your customized Instance object.
121  virtual pp::Instance* CreateInstance(PP_Instance instance) {
122    return new MyInstance(instance);
123  }
124};
125
126namespace pp {
127
128// Factory function for your specialization of the Module object.
129Module* CreateModule() {
130  return new MyModule();
131}
132
133}  // namespace pp
134