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 <stdlib.h>
6 #include <string.h>
7
8 #include <algorithm>
9 #include <limits>
10 #include <vector>
11
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"
23 #include "ppapi/utility/threading/lock.h"
24
25 // When compiling natively on Windows, PostMessage can be #define-d to
26 // something else.
27 #ifdef PostMessage
28 #undef PostMessage
29 #endif
30
31 namespace {
32
33 // This sample frequency is guaranteed to work.
34 const PP_AudioSampleRate kSampleFrequency = PP_AUDIOSAMPLERATE_44100;
35 const uint32_t kSampleCount = 1024;
36 const uint32_t kChannelCount = 1;
37 const char* const kDelimiter = "#__#";
38
39 } // namespace
40
41 class MyInstance : public pp::Instance {
42 public:
MyInstance(PP_Instance instance)43 explicit MyInstance(PP_Instance instance)
44 : pp::Instance(instance),
45 callback_factory_(this),
46 sample_count_(0),
47 channel_count_(0),
48 samples_(NULL),
49 latency_(0),
50 timer_interval_(0),
51 pending_paint_(false),
52 waiting_for_flush_completion_(false) {
53 }
~MyInstance()54 virtual ~MyInstance() {
55 device_detector_.MonitorDeviceChange(NULL, NULL);
56 audio_input_.Close();
57
58 // The audio input thread has exited before the previous call returned, so
59 // it is safe to do so now.
60 delete[] samples_;
61 }
62
Init(uint32_t argc,const char * argn[],const char * argv[])63 virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]) {
64 sample_count_ = pp::AudioConfig::RecommendSampleFrameCount(this,
65 kSampleFrequency,
66 kSampleCount);
67 PP_DCHECK(sample_count_ > 0);
68 channel_count_ = kChannelCount;
69 samples_ = new int16_t[sample_count_ * channel_count_];
70 memset(samples_, 0, sample_count_ * channel_count_ * sizeof(int16_t));
71
72 device_detector_ = pp::AudioInput_Dev(this);
73
74 // Try to ensure that we pick up a new set of samples between each
75 // timer-generated repaint.
76 timer_interval_ = (sample_count_ * 1000) / kSampleFrequency + 5;
77 ScheduleNextTimer();
78
79 return true;
80 }
81
DidChangeView(const pp::Rect & position,const pp::Rect & clip)82 virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip) {
83 if (position.size() == size_)
84 return;
85
86 size_ = position.size();
87 device_context_ = pp::Graphics2D(this, size_, false);
88 if (!BindGraphics(device_context_))
89 return;
90
91 Paint();
92 }
93
HandleMessage(const pp::Var & message_data)94 virtual void HandleMessage(const pp::Var& message_data) {
95 if (message_data.is_string()) {
96 std::string event = message_data.AsString();
97 if (event == "PageInitialized") {
98 int32_t result = device_detector_.MonitorDeviceChange(
99 &MyInstance::MonitorDeviceChangeCallback, this);
100 if (result != PP_OK)
101 PostMessage(pp::Var("MonitorDeviceChangeFailed"));
102
103 pp::CompletionCallbackWithOutput<std::vector<pp::DeviceRef_Dev> >
104 callback = callback_factory_.NewCallbackWithOutput(
105 &MyInstance::EnumerateDevicesFinished);
106 result = device_detector_.EnumerateDevices(callback);
107 if (result != PP_OK_COMPLETIONPENDING)
108 PostMessage(pp::Var("EnumerationFailed"));
109 } else if (event == "UseDefault") {
110 Open(pp::DeviceRef_Dev());
111 } else if (event == "Stop") {
112 Stop();
113 } else if (event == "Start") {
114 Start();
115 } else if (event.find("Monitor:") == 0) {
116 std::string index_str = event.substr(strlen("Monitor:"));
117 int index = atoi(index_str.c_str());
118 if (index >= 0 && index < static_cast<int>(monitor_devices_.size()))
119 Open(monitor_devices_[index]);
120 else
121 PP_NOTREACHED();
122 } else if (event.find("Enumerate:") == 0) {
123 std::string index_str = event.substr(strlen("Enumerate:"));
124 int index = atoi(index_str.c_str());
125 if (index >= 0 && index < static_cast<int>(enumerate_devices_.size()))
126 Open(enumerate_devices_[index]);
127 else
128 PP_NOTREACHED();
129 }
130 }
131 }
132
133 private:
ScheduleNextTimer()134 void ScheduleNextTimer() {
135 PP_DCHECK(timer_interval_ > 0);
136 pp::Module::Get()->core()->CallOnMainThread(
137 timer_interval_,
138 callback_factory_.NewCallback(&MyInstance::OnTimer),
139 0);
140 }
141
OnTimer(int32_t)142 void OnTimer(int32_t) {
143 ScheduleNextTimer();
144 Paint();
145 }
146
DidFlush(int32_t result)147 void DidFlush(int32_t result) {
148 waiting_for_flush_completion_ = false;
149 if (pending_paint_)
150 Paint();
151 }
152
Paint()153 void Paint() {
154 if (waiting_for_flush_completion_) {
155 pending_paint_ = true;
156 return;
157 }
158
159 pending_paint_ = false;
160
161 if (size_.IsEmpty())
162 return; // Nothing to do.
163
164 pp::ImageData image = PaintImage(size_);
165 if (!image.is_null()) {
166 device_context_.ReplaceContents(&image);
167 waiting_for_flush_completion_ = true;
168 device_context_.Flush(
169 callback_factory_.NewCallback(&MyInstance::DidFlush));
170 }
171 }
172
PaintImage(const pp::Size & size)173 pp::ImageData PaintImage(const pp::Size& size) {
174 pp::ImageData image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL, size, false);
175 if (image.is_null())
176 return image;
177
178 // Clear to dark grey.
179 for (int y = 0; y < size.height(); y++) {
180 for (int x = 0; x < size.width(); x++)
181 *image.GetAddr32(pp::Point(x, y)) = 0xff202020;
182 }
183
184 int mid_height = size.height() / 2;
185 int max_amplitude = size.height() * 4 / 10;
186
187 // Draw some lines.
188 for (int x = 0; x < size.width(); x++) {
189 *image.GetAddr32(pp::Point(x, mid_height)) = 0xff606060;
190 *image.GetAddr32(pp::Point(x, mid_height + max_amplitude)) = 0xff404040;
191 *image.GetAddr32(pp::Point(x, mid_height - max_amplitude)) = 0xff404040;
192 }
193
194 {
195 pp::AutoLock auto_lock(lock_);
196
197 // Draw the latency as a red bar at the bottom.
198 PP_DCHECK(latency_ >= 0);
199 int latency_bar_length =
200 latency_ < 1 ? size.width() * latency_ : size.width();
201 for (int x = 0; x < latency_bar_length; ++x) {
202 *image.GetAddr32(pp::Point(x, mid_height + max_amplitude)) = 0xffff0000;
203 }
204
205 // Draw our samples.
206 for (int x = 0, i = 0;
207 x < std::min(size.width(), static_cast<int>(sample_count_));
208 x++, i += channel_count_) {
209 int y = samples_[i] * max_amplitude /
210 (std::numeric_limits<int16_t>::max() + 1) + mid_height;
211 *image.GetAddr32(pp::Point(x, y)) = 0xffffffff;
212 }
213 }
214
215 return image;
216 }
217
Open(const pp::DeviceRef_Dev & device)218 void Open(const pp::DeviceRef_Dev& device) {
219 audio_input_.Close();
220 audio_input_ = pp::AudioInput_Dev(this);
221
222 pp::AudioConfig config = pp::AudioConfig(this,
223 kSampleFrequency,
224 sample_count_);
225 pp::CompletionCallback callback = callback_factory_.NewCallback(
226 &MyInstance::OpenFinished);
227 int32_t result = audio_input_.Open(device, config, CaptureCallback, this,
228 callback);
229 if (result != PP_OK_COMPLETIONPENDING)
230 PostMessage(pp::Var("OpenFailed"));
231 }
232
Stop()233 void Stop() {
234 if (!audio_input_.StopCapture())
235 PostMessage(pp::Var("StopFailed"));
236 }
237
Start()238 void Start() {
239 if (!audio_input_.StartCapture())
240 PostMessage(pp::Var("StartFailed"));
241 }
242
EnumerateDevicesFinished(int32_t result,std::vector<pp::DeviceRef_Dev> & devices)243 void EnumerateDevicesFinished(int32_t result,
244 std::vector<pp::DeviceRef_Dev>& devices) {
245 if (result == PP_OK) {
246 enumerate_devices_.swap(devices);
247 std::string device_names = "Enumerate:";
248 for (size_t index = 0; index < enumerate_devices_.size(); ++index) {
249 pp::Var name = enumerate_devices_[index].GetName();
250 PP_DCHECK(name.is_string());
251
252 if (index != 0)
253 device_names += kDelimiter;
254 device_names += name.AsString();
255 }
256 PostMessage(pp::Var(device_names));
257 } else {
258 PostMessage(pp::Var("EnumerationFailed"));
259 }
260 }
261
OpenFinished(int32_t result)262 void OpenFinished(int32_t result) {
263 if (result == PP_OK) {
264 if (!audio_input_.StartCapture())
265 PostMessage(pp::Var("StartFailed"));
266 } else {
267 PostMessage(pp::Var("OpenFailed"));
268 }
269 }
270
CaptureCallback(const void * samples,uint32_t num_bytes,PP_TimeDelta latency,void * ctx)271 static void CaptureCallback(const void* samples,
272 uint32_t num_bytes,
273 PP_TimeDelta latency,
274 void* ctx) {
275 MyInstance* thiz = static_cast<MyInstance*>(ctx);
276 pp::AutoLock auto_lock(thiz->lock_);
277 thiz->latency_ = latency;
278 uint32_t buffer_size =
279 thiz->sample_count_ * thiz->channel_count_ * sizeof(int16_t);
280 PP_DCHECK(num_bytes <= buffer_size);
281 PP_DCHECK(num_bytes % (thiz->channel_count_ * sizeof(int16_t)) == 0);
282 memcpy(thiz->samples_, samples, num_bytes);
283 memset(reinterpret_cast<char*>(thiz->samples_) + num_bytes, 0,
284 buffer_size - num_bytes);
285 }
286
MonitorDeviceChangeCallback(void * user_data,uint32_t device_count,const PP_Resource devices[])287 static void MonitorDeviceChangeCallback(void* user_data,
288 uint32_t device_count,
289 const PP_Resource devices[]) {
290 MyInstance* thiz = static_cast<MyInstance*>(user_data);
291
292 std::string device_names = "Monitor:";
293 thiz->monitor_devices_.clear();
294 thiz->monitor_devices_.reserve(device_count);
295 for (size_t index = 0; index < device_count; ++index) {
296 thiz->monitor_devices_.push_back(pp::DeviceRef_Dev(devices[index]));
297 pp::Var name = thiz->monitor_devices_.back().GetName();
298 PP_DCHECK(name.is_string());
299
300 if (index != 0)
301 device_names += kDelimiter;
302 device_names += name.AsString();
303 }
304 thiz->PostMessage(pp::Var(device_names));
305 }
306
307 pp::CompletionCallbackFactory<MyInstance> callback_factory_;
308
309 uint32_t sample_count_;
310 uint32_t channel_count_;
311 int16_t* samples_;
312
313 PP_TimeDelta latency_;
314
315 int32_t timer_interval_;
316
317 // Painting stuff.
318 pp::Size size_;
319 pp::Graphics2D device_context_;
320 bool pending_paint_;
321 bool waiting_for_flush_completion_;
322
323 // There is no need to have two resources to do capturing and device detecting
324 // separately. However, this makes the code of monitoring device change
325 // easier.
326 pp::AudioInput_Dev audio_input_;
327 pp::AudioInput_Dev device_detector_;
328
329 std::vector<pp::DeviceRef_Dev> enumerate_devices_;
330 std::vector<pp::DeviceRef_Dev> monitor_devices_;
331
332 // Protects |samples_| and |latency_|.
333 pp::Lock lock_;
334 };
335
336 class MyModule : public pp::Module {
337 public:
CreateInstance(PP_Instance instance)338 virtual pp::Instance* CreateInstance(PP_Instance instance) {
339 return new MyInstance(instance);
340 }
341 };
342
343 namespace pp {
344
345 // Factory function for your specialization of the Module object.
CreateModule()346 Module* CreateModule() {
347 return new MyModule();
348 }
349
350 } // namespace pp
351