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