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 "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"
20
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"
29#elif defined(OS_WIN)
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"
34#else
35#include "media/audio/fake_audio_manager.h"
36#endif
37
38namespace media {
39
40#if defined(USE_PULSEAUDIO)
41typedef AudioManagerPulse AudioManagerAnyPlatform;
42#elif defined(USE_ALSA)
43typedef AudioManagerAlsa AudioManagerAnyPlatform;
44#elif defined(USE_CRAS)
45typedef AudioManagerCras AudioManagerAnyPlatform;
46#elif defined(OS_MACOSX)
47typedef AudioManagerMac AudioManagerAnyPlatform;
48#elif defined(OS_WIN)
49typedef AudioManagerWin AudioManagerAnyPlatform;
50#elif defined(OS_ANDROID)
51typedef AudioManagerAndroid AudioManagerAnyPlatform;
52#else
53typedef FakeAudioManager AudioManagerAnyPlatform;
54#endif
55
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.
58static const size_t kMaxDelayMeasurements = 1000;
59
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.
65static const char kDelayValuesFileName[] = "audio_delay_values_ms.txt";
66
67// Contains delay values which are reported during the full-duplex test.
68// Total delay = |buffer_delay_ms| + |input_delay_ms| + |output_delay_ms|.
69struct AudioDelayState {
70  AudioDelayState()
71      : delta_time_ms(0),
72        buffer_delay_ms(0),
73        input_delay_ms(0),
74        output_delay_ms(0) {
75  }
76
77  // Time in milliseconds since last delay report. Typical value is ~10 [ms].
78  int delta_time_ms;
79
80  // Size of internal sync buffer. Typical value is ~0 [ms].
81  int buffer_delay_ms;
82
83  // Reported capture/input delay. Typical value is ~10 [ms].
84  int input_delay_ms;
85
86  // Reported render/output delay. Typical value is ~40 [ms].
87  int output_delay_ms;
88};
89
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.
93class MockAudioManager : public AudioManagerAnyPlatform {
94 public:
95  MockAudioManager() : AudioManagerAnyPlatform(&fake_audio_log_factory_) {}
96  virtual ~MockAudioManager() {}
97
98  virtual scoped_refptr<base::SingleThreadTaskRunner> GetTaskRunner() OVERRIDE {
99    return base::MessageLoop::current()->message_loop_proxy();
100  }
101
102 private:
103  FakeAudioLogFactory fake_audio_log_factory_;
104  DISALLOW_COPY_AND_ASSIGN(MockAudioManager);
105};
106
107// Test fixture class.
108class AudioLowLatencyInputOutputTest : public testing::Test {
109 protected:
110  AudioLowLatencyInputOutputTest() {}
111
112  virtual ~AudioLowLatencyInputOutputTest() {}
113
114  AudioManager* audio_manager() { return &mock_audio_manager_; }
115  base::MessageLoopForUI* message_loop() { return &message_loop_; }
116
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;
125  }
126
127 private:
128  base::MessageLoopForUI message_loop_;
129  MockAudioManager mock_audio_manager_;
130
131  DISALLOW_COPY_AND_ASSIGN(AudioLowLatencyInputOutputTest);
132};
133
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.
140class FullDuplexAudioSinkSource
141    : public AudioInputStream::AudioInputCallback,
142      public AudioOutputStream::AudioSourceCallback {
143 public:
144  FullDuplexAudioSinkSource(int sample_rate,
145                            int samples_per_packet,
146                            int channels)
147    : sample_rate_(sample_rate),
148      samples_per_packet_(samples_per_packet),
149      channels_(channels),
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_;
155
156    // Start with the smallest possible buffer size. It will be increased
157    // dynamically during the test if required.
158    buffer_.reset(
159        new media::SeekableBuffer(0, samples_per_packet_ * frame_size_));
160
161    frames_to_ms_ = static_cast<double>(1000.0 / sample_rate_);
162    delay_states_.reset(new AudioDelayState[kMaxDelayMeasurements]);
163  }
164
165  virtual ~FullDuplexAudioSinkSource() {
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);
171
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.";
175
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",
183              state.delta_time_ms,
184              state.buffer_delay_ms,
185              state.input_delay_ms,
186              state.output_delay_ms);
187      ++elements_written;
188    }
189
190    base::CloseFile(text_file);
191  }
192
193  // AudioInputStream::AudioInputCallback.
194  virtual void OnData(AudioInputStream* stream,
195                      const AudioBus* src,
196                      uint32 hardware_delay_bytes,
197                      double volume) OVERRIDE {
198    base::AutoLock lock(lock_);
199
200    // Update three components in the AudioDelayState for this recorded
201    // audio packet.
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_;
212    }
213
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());
221    //   buffer_->Clear();
222    // }
223  }
224
225  virtual void OnError(AudioInputStream* stream) OVERRIDE {}
226
227  // AudioOutputStream::AudioSourceCallback.
228  virtual int OnMoreData(AudioBus* audio_bus,
229                         AudioBuffersState buffers_state) OVERRIDE {
230    base::AutoLock lock(lock_);
231
232    // Update one component in the AudioDelayState for the packet
233    // which is about to be played out.
234    if (output_elements_to_write_ < kMaxDelayMeasurements) {
235      int output_delay_bytes = buffers_state.hardware_delay_bytes;
236#if defined(OS_WIN)
237      // Special fix for Windows in combination with Wave where the
238      // pending bytes field of the audio buffer state is used to
239      // report the delay.
240      if (!CoreAudioUtil::IsSupported()) {
241        output_delay_bytes = buffers_state.pending_bytes;
242      }
243#endif
244      delay_states_[output_elements_to_write_].output_delay_ms =
245          BytesToMilliseconds(output_delay_bytes);
246      ++output_elements_to_write_;
247    }
248
249    int size;
250    const uint8* source;
251    // Read the data from the seekable media buffer which contains
252    // captured data at the same size and sample rate as the output side.
253    if (buffer_->GetCurrentChunk(&source, &size) && size > 0) {
254      EXPECT_EQ(channels_, audio_bus->channels());
255      size = std::min(audio_bus->frames() * frame_size_, size);
256      EXPECT_EQ(static_cast<size_t>(size) % sizeof(*audio_bus->channel(0)), 0U);
257      audio_bus->FromInterleaved(
258          source, size / frame_size_, frame_size_ / channels_);
259      buffer_->Seek(size);
260      return size / frame_size_;
261    }
262
263    return 0;
264  }
265
266  virtual void OnError(AudioOutputStream* stream) OVERRIDE {}
267
268 protected:
269  // Converts from bytes to milliseconds taking the sample rate and size
270  // of an audio frame into account.
271  int BytesToMilliseconds(uint32 delay_bytes) const {
272    return static_cast<int>((delay_bytes / frame_size_) * frames_to_ms_ + 0.5);
273  }
274
275 private:
276  base::Lock lock_;
277  scoped_ptr<media::SeekableBuffer> buffer_;
278  int sample_rate_;
279  int samples_per_packet_;
280  int channels_;
281  int frame_size_;
282  double frames_to_ms_;
283  scoped_ptr<AudioDelayState[]> delay_states_;
284  size_t input_elements_to_write_;
285  size_t output_elements_to_write_;
286  base::TimeTicks previous_write_time_;
287};
288
289class AudioInputStreamTraits {
290 public:
291  typedef AudioInputStream StreamType;
292
293  static AudioParameters GetDefaultAudioStreamParameters(
294      AudioManager* audio_manager) {
295    return audio_manager->GetInputStreamParameters(
296        AudioManagerBase::kDefaultDeviceId);
297  }
298
299  static StreamType* CreateStream(AudioManager* audio_manager,
300      const AudioParameters& params) {
301    return audio_manager->MakeAudioInputStream(params,
302      AudioManagerBase::kDefaultDeviceId);
303  }
304};
305
306class AudioOutputStreamTraits {
307 public:
308  typedef AudioOutputStream StreamType;
309
310  static AudioParameters GetDefaultAudioStreamParameters(
311      AudioManager* audio_manager) {
312    return audio_manager->GetDefaultOutputStreamParameters();
313  }
314
315  static StreamType* CreateStream(AudioManager* audio_manager,
316      const AudioParameters& params) {
317    return audio_manager->MakeAudioOutputStream(params, std::string());
318  }
319};
320
321// Traits template holding a trait of StreamType. It encapsulates
322// AudioInputStream and AudioOutputStream stream types.
323template <typename StreamTraits>
324class StreamWrapper {
325 public:
326  typedef typename StreamTraits::StreamType StreamType;
327
328  explicit StreamWrapper(AudioManager* audio_manager)
329      :
330        audio_manager_(audio_manager),
331        format_(AudioParameters::AUDIO_PCM_LOW_LATENCY),
332#if defined(OS_ANDROID)
333        channel_layout_(CHANNEL_LAYOUT_MONO),
334#else
335        channel_layout_(CHANNEL_LAYOUT_STEREO),
336#endif
337        bits_per_sample_(16) {
338    // Use the preferred sample rate.
339    const AudioParameters& params =
340        StreamTraits::GetDefaultAudioStreamParameters(audio_manager_);
341    sample_rate_ = params.sample_rate();
342
343    // Use the preferred buffer size. Note that the input side uses the same
344    // size as the output side in this implementation.
345    samples_per_packet_ = params.frames_per_buffer();
346  }
347
348  virtual ~StreamWrapper() {}
349
350  // Creates an Audio[Input|Output]Stream stream object using default
351  // parameters.
352  StreamType* Create() {
353    return CreateStream();
354  }
355
356  int channels() const {
357    return ChannelLayoutToChannelCount(channel_layout_);
358  }
359  int bits_per_sample() const { return bits_per_sample_; }
360  int sample_rate() const { return sample_rate_; }
361  int samples_per_packet() const { return samples_per_packet_; }
362
363 private:
364  StreamType* CreateStream() {
365    StreamType* stream = StreamTraits::CreateStream(audio_manager_,
366        AudioParameters(format_, channel_layout_, sample_rate_,
367            bits_per_sample_, samples_per_packet_));
368    EXPECT_TRUE(stream);
369    return stream;
370  }
371
372  AudioManager* audio_manager_;
373  AudioParameters::Format format_;
374  ChannelLayout channel_layout_;
375  int bits_per_sample_;
376  int sample_rate_;
377  int samples_per_packet_;
378};
379
380typedef StreamWrapper<AudioInputStreamTraits> AudioInputStreamWrapper;
381typedef StreamWrapper<AudioOutputStreamTraits> AudioOutputStreamWrapper;
382
383// This test is intended for manual tests and should only be enabled
384// when it is required to make a real-time test of audio in full duplex and
385// at the same time create a text file which contains measured delay values.
386// The file can later be analyzed off line using e.g. MATLAB.
387// MATLAB example:
388//   D=load('audio_delay_values_ms.txt');
389//   x=cumsum(D(:,1));
390//   plot(x, D(:,2), x, D(:,3), x, D(:,4), x, D(:,2)+D(:,3)+D(:,4));
391//   axis([0, max(x), 0, max(D(:,2)+D(:,3)+D(:,4))+10]);
392//   legend('buffer delay','input delay','output delay','total delay');
393//   xlabel('time [msec]')
394//   ylabel('delay [msec]')
395//   title('Full-duplex audio delay measurement');
396TEST_F(AudioLowLatencyInputOutputTest, DISABLED_FullDuplexDelayMeasurement) {
397  if (!CanRunAudioTests())
398    return;
399
400  AudioInputStreamWrapper aisw(audio_manager());
401  AudioInputStream* ais = aisw.Create();
402  EXPECT_TRUE(ais);
403
404  AudioOutputStreamWrapper aosw(audio_manager());
405  AudioOutputStream* aos = aosw.Create();
406  EXPECT_TRUE(aos);
407
408  // This test only supports identical parameters in both directions.
409  // TODO(henrika): it is possible to cut delay here by using different
410  // buffer sizes for input and output.
411  if (aisw.sample_rate() != aosw.sample_rate() ||
412      aisw.samples_per_packet() != aosw.samples_per_packet() ||
413      aisw.channels()!= aosw.channels() ||
414      aisw.bits_per_sample() != aosw.bits_per_sample()) {
415    LOG(ERROR) << "This test requires symmetric input and output parameters. "
416        "Ensure that sample rate and number of channels are identical in "
417        "both directions";
418    aos->Close();
419    ais->Close();
420    return;
421  }
422
423  EXPECT_TRUE(ais->Open());
424  EXPECT_TRUE(aos->Open());
425
426  FullDuplexAudioSinkSource full_duplex(
427      aisw.sample_rate(), aisw.samples_per_packet(), aisw.channels());
428
429  VLOG(0) << ">> You should now be able to hear yourself in loopback...";
430  DVLOG(0) << "   sample_rate       : " << aisw.sample_rate();
431  DVLOG(0) << "   samples_per_packet: " << aisw.samples_per_packet();
432  DVLOG(0) << "   channels          : " << aisw.channels();
433
434  ais->Start(&full_duplex);
435  aos->Start(&full_duplex);
436
437  // Wait for approximately 10 seconds. The user shall hear his own voice
438  // in loop back during this time. At the same time, delay recordings are
439  // performed and stored in the output text file.
440  message_loop()->PostDelayedTask(FROM_HERE,
441      base::MessageLoop::QuitClosure(), TestTimeouts::action_timeout());
442  message_loop()->Run();
443
444  aos->Stop();
445  ais->Stop();
446
447  // All Close() operations that run on the mocked audio thread,
448  // should be synchronous and not post additional close tasks to
449  // mocked the audio thread. Hence, there is no need to call
450  // message_loop()->RunUntilIdle() after the Close() methods.
451  aos->Close();
452  ais->Close();
453}
454
455}  // namespace media
456