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 "ppapi/tests/test_audio.h"
6
7#include <string.h>
8
9#include "ppapi/c/ppb_audio_config.h"
10#include "ppapi/c/ppb_audio.h"
11#include "ppapi/cpp/module.h"
12#include "ppapi/tests/testing_instance.h"
13#include "ppapi/tests/test_utils.h"
14
15#define ARRAYSIZE_UNSAFE(a) \
16  ((sizeof(a) / sizeof(*(a))) / \
17   static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
18
19REGISTER_TEST_CASE(Audio);
20
21TestAudio::TestAudio(TestingInstance* instance)
22    : TestCase(instance),
23      audio_callback_method_(NULL),
24      audio_callback_event_(instance->pp_instance()),
25      test_done_(false),
26      audio_interface_(NULL),
27      audio_interface_1_0_(NULL),
28      audio_config_interface_(NULL),
29      core_interface_(NULL) {
30}
31
32TestAudio::~TestAudio() {
33}
34
35bool TestAudio::Init() {
36  audio_interface_ = static_cast<const PPB_Audio_1_1*>(
37      pp::Module::Get()->GetBrowserInterface(PPB_AUDIO_INTERFACE_1_1));
38  audio_interface_1_0_ = static_cast<const PPB_Audio_1_0*>(
39      pp::Module::Get()->GetBrowserInterface(PPB_AUDIO_INTERFACE_1_0));
40  audio_config_interface_ = static_cast<const PPB_AudioConfig*>(
41      pp::Module::Get()->GetBrowserInterface(PPB_AUDIO_CONFIG_INTERFACE));
42  core_interface_ = static_cast<const PPB_Core*>(
43      pp::Module::Get()->GetBrowserInterface(PPB_CORE_INTERFACE));
44  return audio_interface_ && audio_interface_1_0_ && audio_config_interface_ &&
45         core_interface_;
46}
47
48void TestAudio::RunTests(const std::string& filter) {
49  RUN_TEST(Creation, filter);
50  RUN_TEST(DestroyNoStop, filter);
51  RUN_TEST(Failures, filter);
52  RUN_TEST(AudioCallback1, filter);
53  RUN_TEST(AudioCallback2, filter);
54  RUN_TEST(AudioCallback3, filter);
55  RUN_TEST(AudioCallback4, filter);
56}
57
58// Test creating audio resources for all guaranteed sample rates and various
59// frame counts.
60std::string TestAudio::TestCreation() {
61  static const PP_AudioSampleRate kSampleRates[] = {
62    PP_AUDIOSAMPLERATE_44100,
63    PP_AUDIOSAMPLERATE_48000
64  };
65  static const uint32_t kRequestFrameCounts[] = {
66    PP_AUDIOMINSAMPLEFRAMECOUNT,
67    PP_AUDIOMAXSAMPLEFRAMECOUNT,
68    // Include some "okay-looking" frame counts; check their validity below.
69    PP_AUDIOSAMPLERATE_44100 / 100,  // 10ms @ 44.1kHz
70    PP_AUDIOSAMPLERATE_48000 / 100,  // 10ms @ 48kHz
71    2 * PP_AUDIOSAMPLERATE_44100 / 100,  // 20ms @ 44.1kHz
72    2 * PP_AUDIOSAMPLERATE_48000 / 100,  // 20ms @ 48kHz
73    1024,
74    2048,
75    4096
76  };
77  PP_AudioSampleRate sample_rate = audio_config_interface_->RecommendSampleRate(
78      instance_->pp_instance());
79  ASSERT_TRUE(sample_rate == PP_AUDIOSAMPLERATE_NONE ||
80              sample_rate == PP_AUDIOSAMPLERATE_44100 ||
81              sample_rate == PP_AUDIOSAMPLERATE_48000);
82  for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kSampleRates); i++) {
83    PP_AudioSampleRate sample_rate = kSampleRates[i];
84
85    for (size_t j = 0; j < ARRAYSIZE_UNSAFE(kRequestFrameCounts); j++) {
86      // Make a config, create the audio resource, and release the config.
87      uint32_t request_frame_count = kRequestFrameCounts[j];
88      uint32_t frame_count = audio_config_interface_->RecommendSampleFrameCount(
89          instance_->pp_instance(), sample_rate, request_frame_count);
90      PP_Resource ac = audio_config_interface_->CreateStereo16Bit(
91          instance_->pp_instance(), sample_rate, frame_count);
92      ASSERT_TRUE(ac);
93      PP_Resource audio = audio_interface_->Create(
94          instance_->pp_instance(), ac, AudioCallbackTrampoline, this);
95      core_interface_->ReleaseResource(ac);
96      ac = 0;
97
98      ASSERT_TRUE(audio);
99      ASSERT_TRUE(audio_interface_->IsAudio(audio));
100
101      // Check that the config returned for |audio| matches what we gave it.
102      ac = audio_interface_->GetCurrentConfig(audio);
103      ASSERT_TRUE(ac);
104      ASSERT_TRUE(audio_config_interface_->IsAudioConfig(ac));
105      ASSERT_EQ(sample_rate, audio_config_interface_->GetSampleRate(ac));
106      ASSERT_EQ(frame_count, audio_config_interface_->GetSampleFrameCount(ac));
107      core_interface_->ReleaseResource(ac);
108      ac = 0;
109
110      // Start and stop audio playback. The documentation indicates that
111      // |StartPlayback()| and |StopPlayback()| may fail, but gives no
112      // indication as to why ... so check that they succeed.
113      audio_callback_method_ = &TestAudio::AudioCallbackTrivial;
114      ASSERT_TRUE(audio_interface_->StartPlayback(audio));
115      ASSERT_TRUE(audio_interface_->StopPlayback(audio));
116      audio_callback_method_ = NULL;
117
118      core_interface_->ReleaseResource(audio);
119    }
120  }
121
122  PASS();
123}
124
125// Test that releasing the resource without calling |StopPlayback()| "works".
126std::string TestAudio::TestDestroyNoStop() {
127  PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 2048);
128  ASSERT_TRUE(ac);
129  audio_callback_method_ = NULL;
130  PP_Resource audio = audio_interface_->Create(
131      instance_->pp_instance(), ac, AudioCallbackTrampoline, this);
132  core_interface_->ReleaseResource(ac);
133  ac = 0;
134
135  ASSERT_TRUE(audio);
136  ASSERT_TRUE(audio_interface_->IsAudio(audio));
137
138  // Start playback and release the resource.
139  audio_callback_method_ = &TestAudio::AudioCallbackTrivial;
140  ASSERT_TRUE(audio_interface_->StartPlayback(audio));
141  core_interface_->ReleaseResource(audio);
142  audio_callback_method_ = NULL;
143
144  PASS();
145}
146
147std::string TestAudio::TestFailures() {
148  // Test invalid parameters to |Create()|.
149
150  // We want a valid config for some of our tests of |Create()|.
151  PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 2048);
152  ASSERT_TRUE(ac);
153
154  // Failure cases should never lead to the callback being called.
155  audio_callback_method_ = NULL;
156
157  // Invalid instance -> failure.
158  PP_Resource audio = audio_interface_->Create(
159      0, ac, AudioCallbackTrampoline, this);
160  ASSERT_EQ(0, audio);
161
162  // Invalid config -> failure.
163  audio = audio_interface_->Create(
164      instance_->pp_instance(), 0, AudioCallbackTrampoline, this);
165  ASSERT_EQ(0, audio);
166
167  // Null callback -> failure.
168  audio = audio_interface_->Create(
169      instance_->pp_instance(), ac, NULL, NULL);
170  ASSERT_EQ(0, audio);
171
172  core_interface_->ReleaseResource(ac);
173  ac = 0;
174
175  // Test the other functions with an invalid audio resource.
176  ASSERT_FALSE(audio_interface_->IsAudio(0));
177  ASSERT_EQ(0, audio_interface_->GetCurrentConfig(0));
178  ASSERT_FALSE(audio_interface_->StartPlayback(0));
179  ASSERT_FALSE(audio_interface_->StopPlayback(0));
180
181  PASS();
182}
183
184// NOTE: |TestAudioCallbackN| assumes that the audio callback is called at least
185// once. If the audio stream does not start up correctly or is interrupted this
186// may not be the case and these tests will fail. However, in order to properly
187// test the audio callbacks, we must have a configuration where audio can
188// successfully play, so we assume this is the case on bots.
189
190// This test starts playback and verifies that:
191//  1) the audio callback is actually called;
192//  2) that |StopPlayback()| waits for the audio callback to finish.
193std::string TestAudio::TestAudioCallback1() {
194  PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 1024);
195  ASSERT_TRUE(ac);
196  audio_callback_method_ = NULL;
197  PP_Resource audio = audio_interface_->Create(
198      instance_->pp_instance(), ac, AudioCallbackTrampoline, this);
199  core_interface_->ReleaseResource(ac);
200  ac = 0;
201
202  audio_callback_event_.Reset();
203  test_done_ = false;
204
205  audio_callback_method_ = &TestAudio::AudioCallbackTest;
206  ASSERT_TRUE(audio_interface_->StartPlayback(audio));
207
208  // Wait for the audio callback to be called.
209  audio_callback_event_.Wait();
210  ASSERT_TRUE(audio_interface_->StopPlayback(audio));
211  test_done_ = true;
212
213  // If any more audio callbacks are generated, we should crash (which is good).
214  audio_callback_method_ = NULL;
215
216  core_interface_->ReleaseResource(audio);
217
218  PASS();
219}
220
221// This is the same as |TestAudioCallback1()|, except that instead of calling
222// |StopPlayback()|, it just releases the resource.
223std::string TestAudio::TestAudioCallback2() {
224  PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 1024);
225  ASSERT_TRUE(ac);
226  audio_callback_method_ = NULL;
227  PP_Resource audio = audio_interface_->Create(
228      instance_->pp_instance(), ac, AudioCallbackTrampoline, this);
229  core_interface_->ReleaseResource(ac);
230  ac = 0;
231
232  audio_callback_event_.Reset();
233  test_done_ = false;
234
235  audio_callback_method_ = &TestAudio::AudioCallbackTest;
236  ASSERT_TRUE(audio_interface_->StartPlayback(audio));
237
238  // Wait for the audio callback to be called.
239  audio_callback_event_.Wait();
240
241  core_interface_->ReleaseResource(audio);
242
243  test_done_ = true;
244
245  // If any more audio callbacks are generated, we should crash (which is good).
246  audio_callback_method_ = NULL;
247
248  PASS();
249}
250
251// This is the same as |TestAudioCallback1()|, except that it attempts a second
252// round of |StartPlayback| and |StopPlayback| to make sure the callback
253// function still responds when using the same audio resource.
254std::string TestAudio::TestAudioCallback3() {
255  PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 1024);
256  ASSERT_TRUE(ac);
257  audio_callback_method_ = NULL;
258  PP_Resource audio = audio_interface_->Create(
259      instance_->pp_instance(), ac, AudioCallbackTrampoline, this);
260  core_interface_->ReleaseResource(ac);
261  ac = 0;
262
263  audio_callback_event_.Reset();
264  test_done_ = false;
265
266  audio_callback_method_ = &TestAudio::AudioCallbackTest;
267  ASSERT_TRUE(audio_interface_->StartPlayback(audio));
268
269  // Wait for the audio callback to be called.
270  audio_callback_event_.Wait();
271
272  ASSERT_TRUE(audio_interface_->StopPlayback(audio));
273
274  // Repeat one more |StartPlayback| & |StopPlayback| cycle, and verify again
275  // that the callback function was invoked.
276  audio_callback_event_.Reset();
277  ASSERT_TRUE(audio_interface_->StartPlayback(audio));
278
279  // Wait for the audio callback to be called.
280  audio_callback_event_.Wait();
281  ASSERT_TRUE(audio_interface_->StopPlayback(audio));
282  test_done_ = true;
283
284  // If any more audio callbacks are generated, we should crash (which is good).
285  audio_callback_method_ = NULL;
286
287  core_interface_->ReleaseResource(audio);
288
289  PASS();
290}
291
292// This is the same as |TestAudioCallback1()|, except that it uses
293// PPB_Audio_1_0.
294std::string TestAudio::TestAudioCallback4() {
295  PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 1024);
296  ASSERT_TRUE(ac);
297  audio_callback_method_ = NULL;
298  PP_Resource audio = audio_interface_1_0_->Create(
299      instance_->pp_instance(), ac, AudioCallbackTrampoline1_0, this);
300  core_interface_->ReleaseResource(ac);
301  ac = 0;
302
303  audio_callback_event_.Reset();
304  test_done_ = false;
305
306  audio_callback_method_ = &TestAudio::AudioCallbackTest;
307  ASSERT_TRUE(audio_interface_1_0_->StartPlayback(audio));
308
309  // Wait for the audio callback to be called.
310  audio_callback_event_.Wait();
311  ASSERT_TRUE(audio_interface_1_0_->StopPlayback(audio));
312  test_done_ = true;
313
314  // If any more audio callbacks are generated, we should crash (which is good).
315  audio_callback_method_ = NULL;
316
317  core_interface_->ReleaseResource(audio);
318
319  PASS();
320}
321
322// TODO(raymes): Test that actually playback happens correctly, etc.
323
324static void Crash() {
325  *static_cast<volatile unsigned*>(NULL) = 0xdeadbeef;
326}
327
328// static
329void TestAudio::AudioCallbackTrampoline(void* sample_buffer,
330                                        uint32_t buffer_size_in_bytes,
331                                        PP_TimeDelta latency,
332                                        void* user_data) {
333  TestAudio* thiz = static_cast<TestAudio*>(user_data);
334
335  // Crash if on the main thread.
336  if (thiz->core_interface_->IsMainThread())
337    Crash();
338
339  AudioCallbackMethod method = thiz->audio_callback_method_;
340  (thiz->*method)(sample_buffer, buffer_size_in_bytes, latency);
341}
342
343// static
344void TestAudio::AudioCallbackTrampoline1_0(void* sample_buffer,
345                                           uint32_t buffer_size_in_bytes,
346                                           void* user_data) {
347  AudioCallbackTrampoline(sample_buffer, buffer_size_in_bytes, 0.0, user_data);
348}
349
350void TestAudio::AudioCallbackTrivial(void* sample_buffer,
351                                     uint32_t buffer_size_in_bytes,
352                                     PP_TimeDelta latency) {
353  if (latency < 0)
354    Crash();
355
356  memset(sample_buffer, 0, buffer_size_in_bytes);
357}
358
359void TestAudio::AudioCallbackTest(void* sample_buffer,
360                                  uint32_t buffer_size_in_bytes,
361                                  PP_TimeDelta latency) {
362  if (test_done_ || latency < 0)
363    Crash();
364
365  memset(sample_buffer, 0, buffer_size_in_bytes);
366  audio_callback_event_.Signal();
367}
368
369PP_Resource TestAudio::CreateAudioConfig(
370    PP_AudioSampleRate sample_rate,
371    uint32_t requested_sample_frame_count) {
372  uint32_t frame_count = audio_config_interface_->RecommendSampleFrameCount(
373      instance_->pp_instance(), sample_rate, requested_sample_frame_count);
374  return audio_config_interface_->CreateStereo16Bit(
375      instance_->pp_instance(), sample_rate, frame_count);
376}
377