1 // Copyright 2014 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_buffer.h"
13 #include "ppapi/cpp/graphics_2d.h"
14 #include "ppapi/cpp/image_data.h"
15 #include "ppapi/cpp/instance.h"
16 #include "ppapi/cpp/logging.h"
17 #include "ppapi/cpp/media_stream_audio_track.h"
18 #include "ppapi/cpp/module.h"
19 #include "ppapi/cpp/rect.h"
20 #include "ppapi/cpp/size.h"
21 #include "ppapi/cpp/var_dictionary.h"
22 #include "ppapi/utility/completion_callback_factory.h"
24 // When compiling natively on Windows, PostMessage can be #define-d to
30 // This example demonstrates receiving audio samples from an AndioMediaTrack
31 // and visualizing them.
35 const uint32_t kColorRed
= 0xFFFF0000;
36 const uint32_t kColorGreen
= 0xFF00FF00;
37 const uint32_t kColorGrey1
= 0xFF202020;
38 const uint32_t kColorGrey2
= 0xFF404040;
39 const uint32_t kColorGrey3
= 0xFF606060;
41 class MediaStreamAudioInstance
: public pp::Instance
{
43 explicit MediaStreamAudioInstance(PP_Instance instance
)
44 : pp::Instance(instance
),
45 callback_factory_(this),
50 pending_paint_(false),
51 waiting_for_flush_completion_(false) {
54 virtual ~MediaStreamAudioInstance() {
57 virtual void DidChangeView(const pp::Rect
& position
, const pp::Rect
& clip
) {
58 if (position
.size() == size_
)
61 size_
= position
.size();
62 device_context_
= pp::Graphics2D(this, size_
, false);
63 if (!BindGraphics(device_context_
))
69 virtual void HandleMessage(const pp::Var
& var_message
) {
70 if (!var_message
.is_dictionary())
72 pp::VarDictionary
var_dictionary_message(var_message
);
73 pp::Var var_track
= var_dictionary_message
.Get("track");
74 if (!var_track
.is_resource())
77 pp::Resource resource_track
= var_track
.AsResource();
78 audio_track_
= pp::MediaStreamAudioTrack(resource_track
);
79 audio_track_
.GetBuffer(callback_factory_
.NewCallbackWithOutput(
80 &MediaStreamAudioInstance::OnGetBuffer
));
84 void ScheduleNextTimer() {
85 PP_DCHECK(timer_interval_
> 0);
86 pp::Module::Get()->core()->CallOnMainThread(
88 callback_factory_
.NewCallback(&MediaStreamAudioInstance::OnTimer
),
92 void OnTimer(int32_t) {
97 void DidFlush(int32_t result
) {
98 waiting_for_flush_completion_
= false;
104 if (waiting_for_flush_completion_
) {
105 pending_paint_
= true;
109 pending_paint_
= false;
112 return; // Nothing to do.
114 pp::ImageData image
= PaintImage(size_
);
115 if (!image
.is_null()) {
116 device_context_
.ReplaceContents(&image
);
117 waiting_for_flush_completion_
= true;
118 device_context_
.Flush(
119 callback_factory_
.NewCallback(&MediaStreamAudioInstance::DidFlush
));
123 pp::ImageData
PaintImage(const pp::Size
& size
) {
124 pp::ImageData
image(this, PP_IMAGEDATAFORMAT_BGRA_PREMUL
, size
, false);
128 // Clear to dark grey.
129 for (int y
= 0; y
< size
.height(); y
++) {
130 for (int x
= 0; x
< size
.width(); x
++)
131 *image
.GetAddr32(pp::Point(x
, y
)) = kColorGrey1
;
134 int mid_height
= size
.height() / 2;
135 int max_amplitude
= size
.height() * 4 / 10;
138 for (int x
= 0; x
< size
.width(); x
++) {
139 *image
.GetAddr32(pp::Point(x
, mid_height
)) = kColorGrey3
;
140 *image
.GetAddr32(pp::Point(x
, mid_height
+ max_amplitude
)) = kColorGrey2
;
141 *image
.GetAddr32(pp::Point(x
, mid_height
- max_amplitude
)) = kColorGrey2
;
146 for (int x
= 0, i
= 0;
147 x
< std::min(size
.width(), static_cast<int>(sample_count_
));
148 x
++, i
+= channel_count_
) {
149 for (uint32_t ch
= 0; ch
< std::min(channel_count_
, 2U); ++ch
) {
150 int y
= samples_
[i
+ ch
] * max_amplitude
/
151 (std::numeric_limits
<int16_t>::max() + 1) + mid_height
;
152 *image
.GetAddr32(pp::Point(x
, y
)) = (ch
== 0 ? kColorRed
: kColorGreen
);
159 // Callback that is invoked when new buffers are received.
160 void OnGetBuffer(int32_t result
, pp::AudioBuffer buffer
) {
164 PP_DCHECK(buffer
.GetSampleSize() == PP_AUDIOBUFFER_SAMPLESIZE_16_BITS
);
165 const char* data
= static_cast<const char*>(buffer
.GetDataBuffer());
166 uint32_t channels
= buffer
.GetNumberOfChannels();
167 uint32_t samples
= buffer
.GetNumberOfSamples() / channels
;
169 if (channel_count_
!= channels
|| sample_count_
!= samples
) {
170 channel_count_
= channels
;
171 sample_count_
= samples
;
173 samples_
.resize(sample_count_
* channel_count_
);
174 timer_interval_
= (sample_count_
* 1000) / buffer
.GetSampleRate() + 5;
175 // Start the timer for the first buffer.
177 first_buffer_
= false;
182 memcpy(samples_
.data(), data
,
183 sample_count_
* channel_count_
* sizeof(int16_t));
185 audio_track_
.RecycleBuffer(buffer
);
186 audio_track_
.GetBuffer(callback_factory_
.NewCallbackWithOutput(
187 &MediaStreamAudioInstance::OnGetBuffer
));
191 pp::MediaStreamAudioTrack audio_track_
;
192 pp::CompletionCallbackFactory
<MediaStreamAudioInstance
> callback_factory_
;
195 uint32_t sample_count_
;
196 uint32_t channel_count_
;
197 std::vector
<int16_t> samples_
;
199 int32_t timer_interval_
;
203 pp::Graphics2D device_context_
;
205 bool waiting_for_flush_completion_
;
208 class MediaStreamAudioModule
: public pp::Module
{
210 virtual pp::Instance
* CreateInstance(PP_Instance instance
) {
211 return new MediaStreamAudioInstance(instance
);
219 // Factory function for your specialization of the Module object.
220 Module
* CreateModule() {
221 return new MediaStreamAudioModule();