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/files/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"
21 #if defined(USE_PULSEAUDIO)
22 #include "media/audio/pulse/audio_manager_pulse.h"
23 #elif defined(USE_ALSA)
24 #include "media/audio/alsa/audio_manager_alsa.h"
25 #elif defined(USE_CRAS)
26 #include "media/audio/cras/audio_manager_cras.h"
27 #elif defined(OS_MACOSX)
28 #include "media/audio/mac/audio_manager_mac.h"
30 #include "media/audio/win/audio_manager_win.h"
31 #include "media/audio/win/core_audio_util_win.h"
32 #elif defined(OS_ANDROID)
33 #include "media/audio/android/audio_manager_android.h"
35 #include "media/audio/fake_audio_manager.h"
40 #if defined(USE_PULSEAUDIO)
41 typedef AudioManagerPulse AudioManagerAnyPlatform;
42 #elif defined(USE_ALSA)
43 typedef AudioManagerAlsa AudioManagerAnyPlatform;
44 #elif defined(USE_CRAS)
45 typedef AudioManagerCras AudioManagerAnyPlatform;
46 #elif defined(OS_MACOSX)
47 typedef AudioManagerMac AudioManagerAnyPlatform;
49 typedef AudioManagerWin AudioManagerAnyPlatform;
50 #elif defined(OS_ANDROID)
51 typedef AudioManagerAndroid AudioManagerAnyPlatform;
53 typedef FakeAudioManager AudioManagerAnyPlatform;
56 // Limits the number of delay measurements we can store in an array and
57 // then write to file at end of the WASAPIAudioInputOutputFullDuplex test.
58 static const size_t kMaxDelayMeasurements = 1000;
60 // Name of the output text file. The output file will be stored in the
61 // directory containing media_unittests.exe.
62 // Example: \src\build\Debug\audio_delay_values_ms.txt.
63 // See comments for the WASAPIAudioInputOutputFullDuplex test for more details
64 // about the file format.
65 static const char kDelayValuesFileName[] = "audio_delay_values_ms.txt";
67 // Contains delay values which are reported during the full-duplex test.
68 // Total delay = |buffer_delay_ms| + |input_delay_ms| + |output_delay_ms|.
69 struct AudioDelayState {
77 // Time in milliseconds since last delay report. Typical value is ~10 [ms].
80 // Size of internal sync buffer. Typical value is ~0 [ms].
83 // Reported capture/input delay. Typical value is ~10 [ms].
86 // Reported render/output delay. Typical value is ~40 [ms].
90 // This class mocks the platform specific audio manager and overrides
91 // the GetMessageLoop() method to ensure that we can run our tests on
92 // the main thread instead of the audio thread.
93 class MockAudioManager : public AudioManagerAnyPlatform {
95 MockAudioManager() : AudioManagerAnyPlatform(&fake_audio_log_factory_) {}
96 ~MockAudioManager() override {}
98 scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() override {
99 return base::MessageLoop::current()->message_loop_proxy();
103 FakeAudioLogFactory fake_audio_log_factory_;
104 DISALLOW_COPY_AND_ASSIGN(MockAudioManager);
107 // Test fixture class.
108 class AudioLowLatencyInputOutputTest : public testing::Test {
110 AudioLowLatencyInputOutputTest() {}
112 ~AudioLowLatencyInputOutputTest() override {}
114 AudioManager* audio_manager() { return &mock_audio_manager_; }
115 base::MessageLoopForUI* message_loop() { return &message_loop_; }
117 // Convenience method which ensures that we are not running on the build
118 // bots and that at least one valid input and output device can be found.
119 bool CanRunAudioTests() {
120 bool input = audio_manager()->HasAudioInputDevices();
121 bool output = audio_manager()->HasAudioOutputDevices();
122 LOG_IF(WARNING, !input) << "No input device detected.";
123 LOG_IF(WARNING, !output) << "No output device detected.";
124 return input && output;
128 base::MessageLoopForUI message_loop_;
129 MockAudioManager mock_audio_manager_;
131 DISALLOW_COPY_AND_ASSIGN(AudioLowLatencyInputOutputTest);
134 // This audio source/sink implementation should be used for manual tests
135 // only since delay measurements are stored on an output text file.
136 // All incoming/recorded audio packets are stored in an intermediate media
137 // buffer which the renderer reads from when it needs audio for playout.
138 // The total effect is that recorded audio is played out in loop back using
139 // a sync buffer as temporary storage.
140 class FullDuplexAudioSinkSource
141 : public AudioInputStream::AudioInputCallback,
142 public AudioOutputStream::AudioSourceCallback {
144 FullDuplexAudioSinkSource(int sample_rate,
145 int samples_per_packet,
147 : sample_rate_(sample_rate),
148 samples_per_packet_(samples_per_packet),
150 input_elements_to_write_(0),
151 output_elements_to_write_(0),
152 previous_write_time_(base::TimeTicks::Now()) {
153 // Size in bytes of each audio frame (4 bytes for 16-bit stereo PCM).
154 frame_size_ = (16 / 8) * channels_;
156 // Start with the smallest possible buffer size. It will be increased
157 // dynamically during the test if required.
159 new media::SeekableBuffer(0, samples_per_packet_ * frame_size_));
161 frames_to_ms_ = static_cast<double>(1000.0 / sample_rate_);
162 delay_states_.reset(new AudioDelayState[kMaxDelayMeasurements]);
165 ~FullDuplexAudioSinkSource() override {
166 // Get complete file path to output file in the directory containing
167 // media_unittests.exe. Example: src/build/Debug/audio_delay_values_ms.txt.
168 base::FilePath file_name;
169 EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_name));
170 file_name = file_name.AppendASCII(kDelayValuesFileName);
172 FILE* text_file = base::OpenFile(file_name, "wt");
173 DLOG_IF(ERROR, !text_file) << "Failed to open log file.";
174 VLOG(0) << ">> Output file " << file_name.value() << " has been created.";
176 // Write the array which contains time-stamps, buffer size and
177 // audio delays values to a text file.
178 size_t elements_written = 0;
179 while (elements_written <
180 std::min(input_elements_to_write_, output_elements_to_write_)) {
181 const AudioDelayState state = delay_states_[elements_written];
182 fprintf(text_file, "%d %d %d %d\n",
184 state.buffer_delay_ms,
185 state.input_delay_ms,
186 state.output_delay_ms);
190 base::CloseFile(text_file);
193 // AudioInputStream::AudioInputCallback.
194 void OnData(AudioInputStream* stream,
196 uint32 hardware_delay_bytes,
197 double volume) override {
198 base::AutoLock lock(lock_);
200 // Update three components in the AudioDelayState for this recorded
202 const base::TimeTicks now_time = base::TimeTicks::Now();
203 const int diff = (now_time - previous_write_time_).InMilliseconds();
204 previous_write_time_ = now_time;
205 if (input_elements_to_write_ < kMaxDelayMeasurements) {
206 delay_states_[input_elements_to_write_].delta_time_ms = diff;
207 delay_states_[input_elements_to_write_].buffer_delay_ms =
208 BytesToMilliseconds(buffer_->forward_bytes());
209 delay_states_[input_elements_to_write_].input_delay_ms =
210 BytesToMilliseconds(hardware_delay_bytes);
211 ++input_elements_to_write_;
214 // TODO(henrika): fix this and use AudioFifo instead.
215 // Store the captured audio packet in a seekable media buffer.
216 // if (!buffer_->Append(src, size)) {
217 // An attempt to write outside the buffer limits has been made.
218 // Double the buffer capacity to ensure that we have a buffer large
219 // enough to handle the current sample test scenario.
220 // buffer_->set_forward_capacity(2 * buffer_->forward_capacity());
225 void OnError(AudioInputStream* stream) override {}
227 // AudioOutputStream::AudioSourceCallback.
228 int OnMoreData(AudioBus* audio_bus, uint32 total_bytes_delay) override {
229 base::AutoLock lock(lock_);
231 // Update one component in the AudioDelayState for the packet
232 // which is about to be played out.
233 if (output_elements_to_write_ < kMaxDelayMeasurements) {
234 delay_states_[output_elements_to_write_].output_delay_ms =
235 BytesToMilliseconds(total_bytes_delay);
236 ++output_elements_to_write_;
241 // Read the data from the seekable media buffer which contains
242 // captured data at the same size and sample rate as the output side.
243 if (buffer_->GetCurrentChunk(&source, &size) && size > 0) {
244 EXPECT_EQ(channels_, audio_bus->channels());
245 size = std::min(audio_bus->frames() * frame_size_, size);
246 EXPECT_EQ(static_cast<size_t>(size) % sizeof(*audio_bus->channel(0)), 0U);
247 audio_bus->FromInterleaved(
248 source, size / frame_size_, frame_size_ / channels_);
250 return size / frame_size_;
256 void OnError(AudioOutputStream* stream) override {}
259 // Converts from bytes to milliseconds taking the sample rate and size
260 // of an audio frame into account.
261 int BytesToMilliseconds(uint32 delay_bytes) const {
262 return static_cast<int>((delay_bytes / frame_size_) * frames_to_ms_ + 0.5);
267 scoped_ptr<media::SeekableBuffer> buffer_;
269 int samples_per_packet_;
272 double frames_to_ms_;
273 scoped_ptr<AudioDelayState[]> delay_states_;
274 size_t input_elements_to_write_;
275 size_t output_elements_to_write_;
276 base::TimeTicks previous_write_time_;
279 class AudioInputStreamTraits {
281 typedef AudioInputStream StreamType;
283 static AudioParameters GetDefaultAudioStreamParameters(
284 AudioManager* audio_manager) {
285 return audio_manager->GetInputStreamParameters(
286 AudioManagerBase::kDefaultDeviceId);
289 static StreamType* CreateStream(AudioManager* audio_manager,
290 const AudioParameters& params) {
291 return audio_manager->MakeAudioInputStream(params,
292 AudioManagerBase::kDefaultDeviceId);
296 class AudioOutputStreamTraits {
298 typedef AudioOutputStream StreamType;
300 static AudioParameters GetDefaultAudioStreamParameters(
301 AudioManager* audio_manager) {
302 return audio_manager->GetDefaultOutputStreamParameters();
305 static StreamType* CreateStream(AudioManager* audio_manager,
306 const AudioParameters& params) {
307 return audio_manager->MakeAudioOutputStream(params, std::string());
311 // Traits template holding a trait of StreamType. It encapsulates
312 // AudioInputStream and AudioOutputStream stream types.
313 template <typename StreamTraits>
314 class StreamWrapper {
316 typedef typename StreamTraits::StreamType StreamType;
318 explicit StreamWrapper(AudioManager* audio_manager)
320 audio_manager_(audio_manager),
321 format_(AudioParameters::AUDIO_PCM_LOW_LATENCY),
322 #if defined(OS_ANDROID)
323 channel_layout_(CHANNEL_LAYOUT_MONO),
325 channel_layout_(CHANNEL_LAYOUT_STEREO),
327 bits_per_sample_(16) {
328 // Use the preferred sample rate.
329 const AudioParameters& params =
330 StreamTraits::GetDefaultAudioStreamParameters(audio_manager_);
331 sample_rate_ = params.sample_rate();
333 // Use the preferred buffer size. Note that the input side uses the same
334 // size as the output side in this implementation.
335 samples_per_packet_ = params.frames_per_buffer();
338 virtual ~StreamWrapper() {}
340 // Creates an Audio[Input|Output]Stream stream object using default
342 StreamType* Create() {
343 return CreateStream();
346 int channels() const {
347 return ChannelLayoutToChannelCount(channel_layout_);
349 int bits_per_sample() const { return bits_per_sample_; }
350 int sample_rate() const { return sample_rate_; }
351 int samples_per_packet() const { return samples_per_packet_; }
354 StreamType* CreateStream() {
355 StreamType* stream = StreamTraits::CreateStream(audio_manager_,
356 AudioParameters(format_, channel_layout_, sample_rate_,
357 bits_per_sample_, samples_per_packet_));
362 AudioManager* audio_manager_;
363 AudioParameters::Format format_;
364 ChannelLayout channel_layout_;
365 int bits_per_sample_;
367 int samples_per_packet_;
370 typedef StreamWrapper<AudioInputStreamTraits> AudioInputStreamWrapper;
371 typedef StreamWrapper<AudioOutputStreamTraits> AudioOutputStreamWrapper;
373 // This test is intended for manual tests and should only be enabled
374 // when it is required to make a real-time test of audio in full duplex and
375 // at the same time create a text file which contains measured delay values.
376 // The file can later be analyzed off line using e.g. MATLAB.
378 // D=load('audio_delay_values_ms.txt');
380 // plot(x, D(:,2), x, D(:,3), x, D(:,4), x, D(:,2)+D(:,3)+D(:,4));
381 // axis([0, max(x), 0, max(D(:,2)+D(:,3)+D(:,4))+10]);
382 // legend('buffer delay','input delay','output delay','total delay');
383 // xlabel('time [msec]')
384 // ylabel('delay [msec]')
385 // title('Full-duplex audio delay measurement');
386 TEST_F(AudioLowLatencyInputOutputTest, DISABLED_FullDuplexDelayMeasurement) {
387 if (!CanRunAudioTests())
390 AudioInputStreamWrapper aisw(audio_manager());
391 AudioInputStream* ais = aisw.Create();
394 AudioOutputStreamWrapper aosw(audio_manager());
395 AudioOutputStream* aos = aosw.Create();
398 // This test only supports identical parameters in both directions.
399 // TODO(henrika): it is possible to cut delay here by using different
400 // buffer sizes for input and output.
401 if (aisw.sample_rate() != aosw.sample_rate() ||
402 aisw.samples_per_packet() != aosw.samples_per_packet() ||
403 aisw.channels()!= aosw.channels() ||
404 aisw.bits_per_sample() != aosw.bits_per_sample()) {
405 LOG(ERROR) << "This test requires symmetric input and output parameters. "
406 "Ensure that sample rate and number of channels are identical in "
413 EXPECT_TRUE(ais->Open());
414 EXPECT_TRUE(aos->Open());
416 FullDuplexAudioSinkSource full_duplex(
417 aisw.sample_rate(), aisw.samples_per_packet(), aisw.channels());
419 VLOG(0) << ">> You should now be able to hear yourself in loopback...";
420 DVLOG(0) << " sample_rate : " << aisw.sample_rate();
421 DVLOG(0) << " samples_per_packet: " << aisw.samples_per_packet();
422 DVLOG(0) << " channels : " << aisw.channels();
424 ais->Start(&full_duplex);
425 aos->Start(&full_duplex);
427 // Wait for approximately 10 seconds. The user shall hear his own voice
428 // in loop back during this time. At the same time, delay recordings are
429 // performed and stored in the output text file.
430 message_loop()->PostDelayedTask(FROM_HERE,
431 base::MessageLoop::QuitClosure(), TestTimeouts::action_timeout());
432 message_loop()->Run();
437 // All Close() operations that run on the mocked audio thread,
438 // should be synchronous and not post additional close tasks to
439 // mocked the audio thread. Hence, there is no need to call
440 // message_loop()->RunUntilIdle() after the Close() methods.