Home | History | Annotate | Download | only in audio_input
      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:
     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   }
     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 
     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 
     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 
     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:
    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 
    142   void OnTimer(int32_t) {
    143     ScheduleNextTimer();
    144     Paint();
    145   }
    146 
    147   void DidFlush(int32_t result) {
    148     waiting_for_flush_completion_ = false;
    149     if (pending_paint_)
    150       Paint();
    151   }
    152 
    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 
    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 
    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 
    233   void Stop() {
    234     if (!audio_input_.StopCapture())
    235       PostMessage(pp::Var("StopFailed"));
    236   }
    237 
    238   void Start() {
    239     if (!audio_input_.StartCapture())
    240       PostMessage(pp::Var("StartFailed"));
    241   }
    242 
    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 
    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 
    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 
    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:
    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.
    346 Module* CreateModule() {
    347   return new MyModule();
    348 }
    349 
    350 }  // namespace pp
    351