Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / ppapi / examples / audio_input / audio_input.cc
blobe2df2b87ba373efd7ff54e32938bf713a068f146
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.
5 #include <stdlib.h>
6 #include <string.h>
8 #include <algorithm>
9 #include <limits>
10 #include <vector>
12 #include "ppapi/cpp/audio_config.h"
13 #include "ppapi/cpp/dev/audio_input_dev.h"
14 #include "ppapi/cpp/dev/device_ref_dev.h"
15 #include "ppapi/cpp/graphics_2d.h"
16 #include "ppapi/cpp/image_data.h"
17 #include "ppapi/cpp/instance.h"
18 #include "ppapi/cpp/logging.h"
19 #include "ppapi/cpp/module.h"
20 #include "ppapi/cpp/rect.h"
21 #include "ppapi/cpp/size.h"
22 #include "ppapi/utility/completion_callback_factory.h"
24 // When compiling natively on Windows, PostMessage can be #define-d to
25 // something else.
26 #ifdef PostMessage
27 #undef PostMessage
28 #endif
30 namespace {
32 // This sample frequency is guaranteed to work.
33 const PP_AudioSampleRate kSampleFrequency = PP_AUDIOSAMPLERATE_44100;
34 const uint32_t kSampleCount = 1024;
35 const uint32_t kChannelCount = 1;
36 const char* const kDelimiter = "#__#";
38 } // namespace
40 class MyInstance : public pp::Instance {
41 public:
42 explicit MyInstance(PP_Instance instance)
43 : pp::Instance(instance),
44 callback_factory_(this),
45 sample_count_(0),
46 channel_count_(0),
47 samples_(NULL),
48 timer_interval_(0),
49 pending_paint_(false),
50 waiting_for_flush_completion_(false) {
52 virtual ~MyInstance() {
53 device_detector_.MonitorDeviceChange(NULL, NULL);
54 audio_input_.Close();
56 delete[] samples_;
59 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
60 sample_count_ = pp::AudioConfig::RecommendSampleFrameCount(this,
61 kSampleFrequency,
62 kSampleCount);
63 PP_DCHECK(sample_count_ > 0);
64 channel_count_ = kChannelCount;
65 samples_ = new int16_t[sample_count_ * channel_count_];
66 memset(samples_, 0, sample_count_ * channel_count_ * sizeof(int16_t));
68 device_detector_ = pp::AudioInput_Dev(this);
70 // Try to ensure that we pick up a new set of samples between each
71 // timer-generated repaint.
72 timer_interval_ = (sample_count_ * 1000) / kSampleFrequency + 5;
73 ScheduleNextTimer();
75 return true;
78 virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
79 if (position.size() == size_)
80 return;
82 size_ = position.size();
83 device_context_ = pp::Graphics2D(this, size_, false);
84 if (!BindGraphics(device_context_))
85 return;
87 Paint();
90 virtual void HandleMessage(const pp::Var& message_data) {
91 if (message_data.is_string()) {
92 std::string event = message_data.AsString();
93 if (event == "PageInitialized") {
94 int32_t result = device_detector_.MonitorDeviceChange(
95 &MyInstance::MonitorDeviceChangeCallback, this);
96 if (result != PP_OK)
97 PostMessage(pp::Var("MonitorDeviceChangeFailed"));
99 pp::CompletionCallbackWithOutput<std::vector<pp::DeviceRef_Dev> >
100 callback = callback_factory_.NewCallbackWithOutput(
101 &MyInstance::EnumerateDevicesFinished);
102 result = device_detector_.EnumerateDevices(callback);
103 if (result != PP_OK_COMPLETIONPENDING)
104 PostMessage(pp::Var("EnumerationFailed"));
105 } else if (event == "UseDefault") {
106 Open(pp::DeviceRef_Dev());
107 } else if (event == "Stop") {
108 Stop();
109 } else if (event == "Start") {
110 Start();
111 } else if (event.find("Monitor:") == 0) {
112 std::string index_str = event.substr(strlen("Monitor:"));
113 int index = atoi(index_str.c_str());
114 if (index >= 0 && index < static_cast<int>(monitor_devices_.size()))
115 Open(monitor_devices_[index]);
116 else
117 PP_NOTREACHED();
118 } else if (event.find("Enumerate:") == 0) {
119 std::string index_str = event.substr(strlen("Enumerate:"));
120 int index = atoi(index_str.c_str());
121 if (index >= 0 && index < static_cast<int>(enumerate_devices_.size()))
122 Open(enumerate_devices_[index]);
123 else
124 PP_NOTREACHED();
129 private:
130 void ScheduleNextTimer() {
131 PP_DCHECK(timer_interval_ > 0);
132 pp::Module::Get()->core()->CallOnMainThread(
133 timer_interval_,
134 callback_factory_.NewCallback(&MyInstance::OnTimer),
138 void OnTimer(int32_t) {
139 ScheduleNextTimer();
140 Paint();
143 void DidFlush(int32_t result) {
144 waiting_for_flush_completion_ = false;
145 if (pending_paint_)
146 Paint();
149 void Paint() {
150 if (waiting_for_flush_completion_) {
151 pending_paint_ = true;
152 return;
155 pending_paint_ = false;
157 if (size_.IsEmpty())
158 return; // Nothing to do.
160 pp::ImageData image = PaintImage(size_);
161 if (!image.is_null()) {
162 device_context_.ReplaceContents(&image);
163 waiting_for_flush_completion_ = true;
164 device_context_.Flush(
165 callback_factory_.NewCallback(&MyInstance::DidFlush));
169 pp::ImageData PaintImage(const pp::Size& size) {
170 pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, size, false);
171 if (image.is_null())
172 return image;
174 // Clear to dark grey.
175 for (int y = 0; y < size.height(); y++) {
176 for (int x = 0; x < size.width(); x++)
177 *image.GetAddr32(pp::Point(x, y)) = 0xff202020;
180 int mid_height = size.height() / 2;
181 int max_amplitude = size.height() * 4 / 10;
183 // Draw some lines.
184 for (int x = 0; x < size.width(); x++) {
185 *image.GetAddr32(pp::Point(x, mid_height)) = 0xff606060;
186 *image.GetAddr32(pp::Point(x, mid_height + max_amplitude)) = 0xff404040;
187 *image.GetAddr32(pp::Point(x, mid_height - max_amplitude)) = 0xff404040;
190 // Draw our samples.
191 for (int x = 0, i = 0;
192 x < std::min(size.width(), static_cast<int>(sample_count_));
193 x++, i += channel_count_) {
194 int y = samples_[i] * max_amplitude /
195 (std::numeric_limits<int16_t>::max() + 1) + mid_height;
196 *image.GetAddr32(pp::Point(x, y)) = 0xffffffff;
199 return image;
202 void Open(const pp::DeviceRef_Dev& device) {
203 audio_input_.Close();
204 audio_input_ = pp::AudioInput_Dev(this);
206 pp::AudioConfig config = pp::AudioConfig(this,
207 kSampleFrequency,
208 sample_count_);
209 pp::CompletionCallback callback = callback_factory_.NewCallback(
210 &MyInstance::OpenFinished);
211 int32_t result = audio_input_.Open(device, config, CaptureCallback, this,
212 callback);
213 if (result != PP_OK_COMPLETIONPENDING)
214 PostMessage(pp::Var("OpenFailed"));
217 void Stop() {
218 if (!audio_input_.StopCapture())
219 PostMessage(pp::Var("StopFailed"));
222 void Start() {
223 if (!audio_input_.StartCapture())
224 PostMessage(pp::Var("StartFailed"));
227 void EnumerateDevicesFinished(int32_t result,
228 std::vector<pp::DeviceRef_Dev>& devices) {
229 if (result == PP_OK) {
230 enumerate_devices_.swap(devices);
231 std::string device_names = "Enumerate:";
232 for (size_t index = 0; index < enumerate_devices_.size(); ++index) {
233 pp::Var name = enumerate_devices_[index].GetName();
234 PP_DCHECK(name.is_string());
236 if (index != 0)
237 device_names += kDelimiter;
238 device_names += name.AsString();
240 PostMessage(pp::Var(device_names));
241 } else {
242 PostMessage(pp::Var("EnumerationFailed"));
246 void OpenFinished(int32_t result) {
247 if (result == PP_OK) {
248 if (!audio_input_.StartCapture())
249 PostMessage(pp::Var("StartFailed"));
250 } else {
251 PostMessage(pp::Var("OpenFailed"));
255 // TODO(viettrungluu): Danger! We really should lock, but which thread
256 // primitives to use? In any case, the |StopCapture()| in the destructor
257 // shouldn't return until this callback is done, so at least we should be
258 // writing to a valid region of memory.
259 static void CaptureCallback(const void* samples,
260 uint32_t num_bytes,
261 void* ctx) {
262 MyInstance* thiz = static_cast<MyInstance*>(ctx);
263 uint32_t buffer_size =
264 thiz->sample_count_ * thiz->channel_count_ * sizeof(int16_t);
265 PP_DCHECK(num_bytes <= buffer_size);
266 PP_DCHECK(num_bytes % (thiz->channel_count_ * sizeof(int16_t)) == 0);
267 memcpy(thiz->samples_, samples, num_bytes);
268 memset(reinterpret_cast<char*>(thiz->samples_) + num_bytes, 0,
269 buffer_size - num_bytes);
272 static void MonitorDeviceChangeCallback(void* user_data,
273 uint32_t device_count,
274 const PP_Resource devices[]) {
275 MyInstance* thiz = static_cast<MyInstance*>(user_data);
277 std::string device_names = "Monitor:";
278 thiz->monitor_devices_.clear();
279 thiz->monitor_devices_.reserve(device_count);
280 for (size_t index = 0; index < device_count; ++index) {
281 thiz->monitor_devices_.push_back(pp::DeviceRef_Dev(devices[index]));
282 pp::Var name = thiz->monitor_devices_.back().GetName();
283 PP_DCHECK(name.is_string());
285 if (index != 0)
286 device_names += kDelimiter;
287 device_names += name.AsString();
289 thiz->PostMessage(pp::Var(device_names));
292 pp::CompletionCallbackFactory<MyInstance> callback_factory_;
294 uint32_t sample_count_;
295 uint32_t channel_count_;
296 int16_t* samples_;
298 int32_t timer_interval_;
300 // Painting stuff.
301 pp::Size size_;
302 pp::Graphics2D device_context_;
303 bool pending_paint_;
304 bool waiting_for_flush_completion_;
306 // There is no need to have two resources to do capturing and device detecting
307 // separately. However, this makes the code of monitoring device change
308 // easier.
309 pp::AudioInput_Dev audio_input_;
310 pp::AudioInput_Dev device_detector_;
312 std::vector<pp::DeviceRef_Dev> enumerate_devices_;
313 std::vector<pp::DeviceRef_Dev> monitor_devices_;
316 class MyModule : public pp::Module {
317 public:
318 virtual pp::Instance* CreateInstance(PP_Instance instance) {
319 return new MyInstance(instance);
323 namespace pp {
325 // Factory function for your specialization of the Module object.
326 Module* CreateModule() {
327 return new MyModule();
330 } // namespace pp