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