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.
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"
25 // When compiling natively on Windows, PostMessage can be #define-d to
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
= "#__#";
41 class MyInstance
: public pp::Instance
{
43 explicit MyInstance(PP_Instance instance
)
44 : pp::Instance(instance
),
45 callback_factory_(this),
51 pending_paint_(false),
52 waiting_for_flush_completion_(false) {
54 virtual ~MyInstance() {
55 device_detector_
.MonitorDeviceChange(NULL
, NULL
);
58 // The audio input thread has exited before the previous call returned, so
59 // it is safe to do so now.
63 virtual bool Init(uint32_t argc
, const char* argn
[], const char* argv
[]) {
64 sample_count_
= pp::AudioConfig::RecommendSampleFrameCount(this,
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));
72 device_detector_
= pp::AudioInput_Dev(this);
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;
82 virtual void DidChangeView(const pp::Rect
& position
, const pp::Rect
& clip
) {
83 if (position
.size() == size_
)
86 size_
= position
.size();
87 device_context_
= pp::Graphics2D(this, size_
, false);
88 if (!BindGraphics(device_context_
))
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);
101 PostMessage(pp::Var("MonitorDeviceChangeFailed"));
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") {
113 } else if (event
== "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
]);
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
]);
134 void ScheduleNextTimer() {
135 PP_DCHECK(timer_interval_
> 0);
136 pp::Module::Get()->core()->CallOnMainThread(
138 callback_factory_
.NewCallback(&MyInstance::OnTimer
),
142 void OnTimer(int32_t) {
147 void DidFlush(int32_t result
) {
148 waiting_for_flush_completion_
= false;
154 if (waiting_for_flush_completion_
) {
155 pending_paint_
= true;
159 pending_paint_
= false;
162 return; // Nothing to do.
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
));
173 pp::ImageData
PaintImage(const pp::Size
& size
) {
174 pp::ImageData
image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL
, size
, false);
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;
184 int mid_height
= size
.height() / 2;
185 int max_amplitude
= size
.height() * 4 / 10;
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;
195 pp::AutoLock
auto_lock(lock_
);
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;
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;
218 void Open(const pp::DeviceRef_Dev
& device
) {
219 audio_input_
.Close();
220 audio_input_
= pp::AudioInput_Dev(this);
222 pp::AudioConfig config
= pp::AudioConfig(this,
225 pp::CompletionCallback callback
= callback_factory_
.NewCallback(
226 &MyInstance::OpenFinished
);
227 int32_t result
= audio_input_
.Open(device
, config
, CaptureCallback
, this,
229 if (result
!= PP_OK_COMPLETIONPENDING
)
230 PostMessage(pp::Var("OpenFailed"));
234 if (!audio_input_
.StopCapture())
235 PostMessage(pp::Var("StopFailed"));
239 if (!audio_input_
.StartCapture())
240 PostMessage(pp::Var("StartFailed"));
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());
253 device_names
+= kDelimiter
;
254 device_names
+= name
.AsString();
256 PostMessage(pp::Var(device_names
));
258 PostMessage(pp::Var("EnumerationFailed"));
262 void OpenFinished(int32_t result
) {
263 if (result
== PP_OK
) {
264 if (!audio_input_
.StartCapture())
265 PostMessage(pp::Var("StartFailed"));
267 PostMessage(pp::Var("OpenFailed"));
271 static void CaptureCallback(const void* samples
,
273 PP_TimeDelta latency
,
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
);
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
);
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());
301 device_names
+= kDelimiter
;
302 device_names
+= name
.AsString();
304 thiz
->PostMessage(pp::Var(device_names
));
307 pp::CompletionCallbackFactory
<MyInstance
> callback_factory_
;
309 uint32_t sample_count_
;
310 uint32_t channel_count_
;
313 PP_TimeDelta latency_
;
315 int32_t timer_interval_
;
319 pp::Graphics2D device_context_
;
321 bool waiting_for_flush_completion_
;
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
326 pp::AudioInput_Dev audio_input_
;
327 pp::AudioInput_Dev device_detector_
;
329 std::vector
<pp::DeviceRef_Dev
> enumerate_devices_
;
330 std::vector
<pp::DeviceRef_Dev
> monitor_devices_
;
332 // Protects |samples_| and |latency_|.
336 class MyModule
: public pp::Module
{
338 virtual pp::Instance
* CreateInstance(PP_Instance instance
) {
339 return new MyInstance(instance
);
345 // Factory function for your specialization of the Module object.
346 Module
* CreateModule() {
347 return new MyModule();