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.
5 #include "base/basictypes.h"
6 #include "base/environment.h"
7 #include "base/file_util.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/path_service.h"
10 #include "base/synchronization/lock.h"
11 #include "base/test/test_timeouts.h"
12 #include "base/time/time.h"
13 #include "build/build_config.h"
14 #include "media/audio/audio_io.h"
15 #include "media/audio/audio_manager_base.h"
16 #include "media/audio/fake_audio_log_factory.h"
17 #include "media/base/seekable_buffer.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
22 #include "media/audio/alsa/audio_manager_alsa.h"
23 #elif defined(OS_MACOSX)
24 #include "media/audio/mac/audio_manager_mac.h"
26 #include "media/audio/win/audio_manager_win.h"
27 #include "media/audio/win/core_audio_util_win.h"
28 #elif defined(OS_ANDROID)
29 #include "media/audio/android/audio_manager_android.h"
31 #include "media/audio/fake_audio_manager.h"
37 typedef AudioManagerAlsa AudioManagerAnyPlatform
;
38 #elif defined(OS_MACOSX)
39 typedef AudioManagerMac AudioManagerAnyPlatform
;
41 typedef AudioManagerWin AudioManagerAnyPlatform
;
42 #elif defined(OS_ANDROID)
43 typedef AudioManagerAndroid AudioManagerAnyPlatform
;
45 typedef FakeAudioManager AudioManagerAnyPlatform
;
48 // Limits the number of delay measurements we can store in an array and
49 // then write to file at end of the WASAPIAudioInputOutputFullDuplex test.
50 static const size_t kMaxDelayMeasurements
= 1000;
52 // Name of the output text file. The output file will be stored in the
53 // directory containing media_unittests.exe.
54 // Example: \src\build\Debug\audio_delay_values_ms.txt.
55 // See comments for the WASAPIAudioInputOutputFullDuplex test for more details
56 // about the file format.
57 static const char kDelayValuesFileName
[] = "audio_delay_values_ms.txt";
59 // Contains delay values which are reported during the full-duplex test.
60 // Total delay = |buffer_delay_ms| + |input_delay_ms| + |output_delay_ms|.
61 struct AudioDelayState
{
69 // Time in milliseconds since last delay report. Typical value is ~10 [ms].
72 // Size of internal sync buffer. Typical value is ~0 [ms].
75 // Reported capture/input delay. Typical value is ~10 [ms].
78 // Reported render/output delay. Typical value is ~40 [ms].
82 // This class mocks the platform specific audio manager and overrides
83 // the GetMessageLoop() method to ensure that we can run our tests on
84 // the main thread instead of the audio thread.
85 class MockAudioManager
: public AudioManagerAnyPlatform
{
87 MockAudioManager() : AudioManagerAnyPlatform(&fake_audio_log_factory_
) {}
88 virtual ~MockAudioManager() {}
90 virtual scoped_refptr
<base::SingleThreadTaskRunner
> GetTaskRunner() OVERRIDE
{
91 return base::MessageLoop::current()->message_loop_proxy();
95 FakeAudioLogFactory fake_audio_log_factory_
;
96 DISALLOW_COPY_AND_ASSIGN(MockAudioManager
);
99 // Test fixture class.
100 class AudioLowLatencyInputOutputTest
: public testing::Test
{
102 AudioLowLatencyInputOutputTest() {}
104 virtual ~AudioLowLatencyInputOutputTest() {}
106 AudioManager
* audio_manager() { return &mock_audio_manager_
; }
107 base::MessageLoopForUI
* message_loop() { return &message_loop_
; }
109 // Convenience method which ensures that we are not running on the build
110 // bots and that at least one valid input and output device can be found.
111 bool CanRunAudioTests() {
112 bool input
= audio_manager()->HasAudioInputDevices();
113 bool output
= audio_manager()->HasAudioOutputDevices();
114 LOG_IF(WARNING
, !input
) << "No input device detected.";
115 LOG_IF(WARNING
, !output
) << "No output device detected.";
116 return input
&& output
;
120 base::MessageLoopForUI message_loop_
;
121 MockAudioManager mock_audio_manager_
;
123 DISALLOW_COPY_AND_ASSIGN(AudioLowLatencyInputOutputTest
);
126 // This audio source/sink implementation should be used for manual tests
127 // only since delay measurements are stored on an output text file.
128 // All incoming/recorded audio packets are stored in an intermediate media
129 // buffer which the renderer reads from when it needs audio for playout.
130 // The total effect is that recorded audio is played out in loop back using
131 // a sync buffer as temporary storage.
132 class FullDuplexAudioSinkSource
133 : public AudioInputStream::AudioInputCallback
,
134 public AudioOutputStream::AudioSourceCallback
{
136 FullDuplexAudioSinkSource(int sample_rate
,
137 int samples_per_packet
,
139 : sample_rate_(sample_rate
),
140 samples_per_packet_(samples_per_packet
),
142 input_elements_to_write_(0),
143 output_elements_to_write_(0),
144 previous_write_time_(base::TimeTicks::Now()) {
145 // Size in bytes of each audio frame (4 bytes for 16-bit stereo PCM).
146 frame_size_
= (16 / 8) * channels_
;
148 // Start with the smallest possible buffer size. It will be increased
149 // dynamically during the test if required.
151 new media::SeekableBuffer(0, samples_per_packet_
* frame_size_
));
153 frames_to_ms_
= static_cast<double>(1000.0 / sample_rate_
);
154 delay_states_
.reset(new AudioDelayState
[kMaxDelayMeasurements
]);
157 virtual ~FullDuplexAudioSinkSource() {
158 // Get complete file path to output file in the directory containing
159 // media_unittests.exe. Example: src/build/Debug/audio_delay_values_ms.txt.
160 base::FilePath file_name
;
161 EXPECT_TRUE(PathService::Get(base::DIR_EXE
, &file_name
));
162 file_name
= file_name
.AppendASCII(kDelayValuesFileName
);
164 FILE* text_file
= base::OpenFile(file_name
, "wt");
165 DLOG_IF(ERROR
, !text_file
) << "Failed to open log file.";
166 VLOG(0) << ">> Output file " << file_name
.value() << " has been created.";
168 // Write the array which contains time-stamps, buffer size and
169 // audio delays values to a text file.
170 size_t elements_written
= 0;
171 while (elements_written
<
172 std::min(input_elements_to_write_
, output_elements_to_write_
)) {
173 const AudioDelayState state
= delay_states_
[elements_written
];
174 fprintf(text_file
, "%d %d %d %d\n",
176 state
.buffer_delay_ms
,
177 state
.input_delay_ms
,
178 state
.output_delay_ms
);
182 base::CloseFile(text_file
);
185 // AudioInputStream::AudioInputCallback.
186 virtual void OnData(AudioInputStream
* stream
,
187 const uint8
* src
, uint32 size
,
188 uint32 hardware_delay_bytes
,
189 double volume
) OVERRIDE
{
190 base::AutoLock
lock(lock_
);
192 // Update three components in the AudioDelayState for this recorded
194 const base::TimeTicks now_time
= base::TimeTicks::Now();
195 const int diff
= (now_time
- previous_write_time_
).InMilliseconds();
196 previous_write_time_
= now_time
;
197 if (input_elements_to_write_
< kMaxDelayMeasurements
) {
198 delay_states_
[input_elements_to_write_
].delta_time_ms
= diff
;
199 delay_states_
[input_elements_to_write_
].buffer_delay_ms
=
200 BytesToMilliseconds(buffer_
->forward_bytes());
201 delay_states_
[input_elements_to_write_
].input_delay_ms
=
202 BytesToMilliseconds(hardware_delay_bytes
);
203 ++input_elements_to_write_
;
206 // Store the captured audio packet in a seekable media buffer.
207 if (!buffer_
->Append(src
, size
)) {
208 // An attempt to write outside the buffer limits has been made.
209 // Double the buffer capacity to ensure that we have a buffer large
210 // enough to handle the current sample test scenario.
211 buffer_
->set_forward_capacity(2 * buffer_
->forward_capacity());
216 virtual void OnError(AudioInputStream
* stream
) OVERRIDE
{}
218 // AudioOutputStream::AudioSourceCallback.
219 virtual int OnMoreData(AudioBus
* audio_bus
,
220 AudioBuffersState buffers_state
) OVERRIDE
{
221 base::AutoLock
lock(lock_
);
223 // Update one component in the AudioDelayState for the packet
224 // which is about to be played out.
225 if (output_elements_to_write_
< kMaxDelayMeasurements
) {
226 int output_delay_bytes
= buffers_state
.hardware_delay_bytes
;
228 // Special fix for Windows in combination with Wave where the
229 // pending bytes field of the audio buffer state is used to
231 if (!CoreAudioUtil::IsSupported()) {
232 output_delay_bytes
= buffers_state
.pending_bytes
;
235 delay_states_
[output_elements_to_write_
].output_delay_ms
=
236 BytesToMilliseconds(output_delay_bytes
);
237 ++output_elements_to_write_
;
242 // Read the data from the seekable media buffer which contains
243 // captured data at the same size and sample rate as the output side.
244 if (buffer_
->GetCurrentChunk(&source
, &size
) && size
> 0) {
245 EXPECT_EQ(channels_
, audio_bus
->channels());
246 size
= std::min(audio_bus
->frames() * frame_size_
, size
);
247 EXPECT_EQ(static_cast<size_t>(size
) % sizeof(*audio_bus
->channel(0)), 0U);
248 audio_bus
->FromInterleaved(
249 source
, size
/ frame_size_
, frame_size_
/ channels_
);
251 return size
/ frame_size_
;
257 virtual void OnError(AudioOutputStream
* stream
) OVERRIDE
{}
260 // Converts from bytes to milliseconds taking the sample rate and size
261 // of an audio frame into account.
262 int BytesToMilliseconds(uint32 delay_bytes
) const {
263 return static_cast<int>((delay_bytes
/ frame_size_
) * frames_to_ms_
+ 0.5);
268 scoped_ptr
<media::SeekableBuffer
> buffer_
;
270 int samples_per_packet_
;
273 double frames_to_ms_
;
274 scoped_ptr
<AudioDelayState
[]> delay_states_
;
275 size_t input_elements_to_write_
;
276 size_t output_elements_to_write_
;
277 base::TimeTicks previous_write_time_
;
280 class AudioInputStreamTraits
{
282 typedef AudioInputStream StreamType
;
284 static AudioParameters
GetDefaultAudioStreamParameters(
285 AudioManager
* audio_manager
) {
286 return audio_manager
->GetInputStreamParameters(
287 AudioManagerBase::kDefaultDeviceId
);
290 static StreamType
* CreateStream(AudioManager
* audio_manager
,
291 const AudioParameters
& params
) {
292 return audio_manager
->MakeAudioInputStream(params
,
293 AudioManagerBase::kDefaultDeviceId
);
297 class AudioOutputStreamTraits
{
299 typedef AudioOutputStream StreamType
;
301 static AudioParameters
GetDefaultAudioStreamParameters(
302 AudioManager
* audio_manager
) {
303 return audio_manager
->GetDefaultOutputStreamParameters();
306 static StreamType
* CreateStream(AudioManager
* audio_manager
,
307 const AudioParameters
& params
) {
308 return audio_manager
->MakeAudioOutputStream(params
, std::string());
312 // Traits template holding a trait of StreamType. It encapsulates
313 // AudioInputStream and AudioOutputStream stream types.
314 template <typename StreamTraits
>
315 class StreamWrapper
{
317 typedef typename
StreamTraits::StreamType StreamType
;
319 explicit StreamWrapper(AudioManager
* audio_manager
)
321 audio_manager_(audio_manager
),
322 format_(AudioParameters::AUDIO_PCM_LOW_LATENCY
),
323 #if defined(OS_ANDROID)
324 channel_layout_(CHANNEL_LAYOUT_MONO
),
326 channel_layout_(CHANNEL_LAYOUT_STEREO
),
328 bits_per_sample_(16) {
329 // Use the preferred sample rate.
330 const AudioParameters
& params
=
331 StreamTraits::GetDefaultAudioStreamParameters(audio_manager_
);
332 sample_rate_
= params
.sample_rate();
334 // Use the preferred buffer size. Note that the input side uses the same
335 // size as the output side in this implementation.
336 samples_per_packet_
= params
.frames_per_buffer();
339 virtual ~StreamWrapper() {}
341 // Creates an Audio[Input|Output]Stream stream object using default
343 StreamType
* Create() {
344 return CreateStream();
347 int channels() const {
348 return ChannelLayoutToChannelCount(channel_layout_
);
350 int bits_per_sample() const { return bits_per_sample_
; }
351 int sample_rate() const { return sample_rate_
; }
352 int samples_per_packet() const { return samples_per_packet_
; }
355 StreamType
* CreateStream() {
356 StreamType
* stream
= StreamTraits::CreateStream(audio_manager_
,
357 AudioParameters(format_
, channel_layout_
, sample_rate_
,
358 bits_per_sample_
, samples_per_packet_
));
363 AudioManager
* audio_manager_
;
364 AudioParameters::Format format_
;
365 ChannelLayout channel_layout_
;
366 int bits_per_sample_
;
368 int samples_per_packet_
;
371 typedef StreamWrapper
<AudioInputStreamTraits
> AudioInputStreamWrapper
;
372 typedef StreamWrapper
<AudioOutputStreamTraits
> AudioOutputStreamWrapper
;
374 // This test is intended for manual tests and should only be enabled
375 // when it is required to make a real-time test of audio in full duplex and
376 // at the same time create a text file which contains measured delay values.
377 // The file can later be analyzed off line using e.g. MATLAB.
379 // D=load('audio_delay_values_ms.txt');
381 // plot(x, D(:,2), x, D(:,3), x, D(:,4), x, D(:,2)+D(:,3)+D(:,4));
382 // axis([0, max(x), 0, max(D(:,2)+D(:,3)+D(:,4))+10]);
383 // legend('buffer delay','input delay','output delay','total delay');
384 // xlabel('time [msec]')
385 // ylabel('delay [msec]')
386 // title('Full-duplex audio delay measurement');
387 TEST_F(AudioLowLatencyInputOutputTest
, DISABLED_FullDuplexDelayMeasurement
) {
388 if (!CanRunAudioTests())
391 AudioInputStreamWrapper
aisw(audio_manager());
392 AudioInputStream
* ais
= aisw
.Create();
395 AudioOutputStreamWrapper
aosw(audio_manager());
396 AudioOutputStream
* aos
= aosw
.Create();
399 // This test only supports identical parameters in both directions.
400 // TODO(henrika): it is possible to cut delay here by using different
401 // buffer sizes for input and output.
402 if (aisw
.sample_rate() != aosw
.sample_rate() ||
403 aisw
.samples_per_packet() != aosw
.samples_per_packet() ||
404 aisw
.channels()!= aosw
.channels() ||
405 aisw
.bits_per_sample() != aosw
.bits_per_sample()) {
406 LOG(ERROR
) << "This test requires symmetric input and output parameters. "
407 "Ensure that sample rate and number of channels are identical in "
414 EXPECT_TRUE(ais
->Open());
415 EXPECT_TRUE(aos
->Open());
417 FullDuplexAudioSinkSource
full_duplex(
418 aisw
.sample_rate(), aisw
.samples_per_packet(), aisw
.channels());
420 VLOG(0) << ">> You should now be able to hear yourself in loopback...";
421 DVLOG(0) << " sample_rate : " << aisw
.sample_rate();
422 DVLOG(0) << " samples_per_packet: " << aisw
.samples_per_packet();
423 DVLOG(0) << " channels : " << aisw
.channels();
425 ais
->Start(&full_duplex
);
426 aos
->Start(&full_duplex
);
428 // Wait for approximately 10 seconds. The user shall hear his own voice
429 // in loop back during this time. At the same time, delay recordings are
430 // performed and stored in the output text file.
431 message_loop()->PostDelayedTask(FROM_HERE
,
432 base::MessageLoop::QuitClosure(), TestTimeouts::action_timeout());
433 message_loop()->Run();
438 // All Close() operations that run on the mocked audio thread,
439 // should be synchronous and not post additional close tasks to
440 // mocked the audio thread. Hence, there is no need to call
441 // message_loop()->RunUntilIdle() after the Close() methods.