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