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 <windows.h>
6#include <mmsystem.h>
7
8#include "base/basictypes.h"
9#include "base/environment.h"
10#include "base/file_util.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/message_loop/message_loop.h"
13#include "base/path_service.h"
14#include "base/test/test_timeouts.h"
15#include "base/win/scoped_com_initializer.h"
16#include "media/audio/audio_io.h"
17#include "media/audio/audio_manager_base.h"
18#include "media/audio/win/audio_low_latency_input_win.h"
19#include "media/audio/win/core_audio_util_win.h"
20#include "media/base/seekable_buffer.h"
21#include "testing/gmock/include/gmock/gmock.h"
22#include "testing/gtest/include/gtest/gtest.h"
23
24using base::win::ScopedCOMInitializer;
25using ::testing::_;
26using ::testing::AnyNumber;
27using ::testing::AtLeast;
28using ::testing::Gt;
29using ::testing::NotNull;
30
31namespace media {
32
33ACTION_P3(CheckCountAndPostQuitTask, count, limit, loop) {
34  if (++*count >= limit) {
35    loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
36  }
37}
38
39class MockAudioInputCallback : public AudioInputStream::AudioInputCallback {
40 public:
41  MOCK_METHOD4(OnData,
42               void(AudioInputStream* stream,
43                    const AudioBus* src,
44                    uint32 hardware_delay_bytes,
45                    double volume));
46  MOCK_METHOD1(OnError, void(AudioInputStream* stream));
47};
48
49class FakeAudioInputCallback : public AudioInputStream::AudioInputCallback {
50 public:
51  FakeAudioInputCallback()
52      : error_(false),
53        data_event_(false, false),
54        num_received_audio_frames_(0) {}
55
56  bool error() const { return error_; }
57  int num_received_audio_frames() const { return num_received_audio_frames_; }
58
59  // Waits until OnData() is called on another thread.
60  void WaitForData() {
61    data_event_.Wait();
62  }
63
64  virtual void OnData(AudioInputStream* stream,
65                      const AudioBus* src,
66                      uint32 hardware_delay_bytes,
67                      double volume) OVERRIDE {
68    EXPECT_NE(hardware_delay_bytes, 0u);
69    num_received_audio_frames_ += src->frames();
70    data_event_.Signal();
71  }
72
73  virtual void OnError(AudioInputStream* stream) OVERRIDE {
74    error_ = true;
75  }
76
77 private:
78  int num_received_audio_frames_;
79  base::WaitableEvent data_event_;
80  bool error_;
81
82  DISALLOW_COPY_AND_ASSIGN(FakeAudioInputCallback);
83};
84
85// This audio sink implementation should be used for manual tests only since
86// the recorded data is stored on a raw binary data file.
87class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback {
88 public:
89  // Allocate space for ~10 seconds of data @ 48kHz in stereo:
90  // 2 bytes per sample, 2 channels, 10ms @ 48kHz, 10 seconds <=> 1920000 bytes.
91  static const size_t kMaxBufferSize = 2 * 2 * 480 * 100 * 10;
92
93  explicit WriteToFileAudioSink(const char* file_name, int bits_per_sample)
94      : bits_per_sample_(bits_per_sample),
95        buffer_(0, kMaxBufferSize),
96        bytes_to_write_(0) {
97    base::FilePath file_path;
98    EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_path));
99    file_path = file_path.AppendASCII(file_name);
100    binary_file_ = base::OpenFile(file_path, "wb");
101    DLOG_IF(ERROR, !binary_file_) << "Failed to open binary PCM data file.";
102    VLOG(0) << ">> Output file: " << file_path.value() << " has been created.";
103    VLOG(0) << "bits_per_sample_:" << bits_per_sample_;
104  }
105
106  virtual ~WriteToFileAudioSink() {
107    size_t bytes_written = 0;
108    while (bytes_written < bytes_to_write_) {
109      const uint8* chunk;
110      int chunk_size;
111
112      // Stop writing if no more data is available.
113      if (!buffer_.GetCurrentChunk(&chunk, &chunk_size))
114        break;
115
116      // Write recorded data chunk to the file and prepare for next chunk.
117      fwrite(chunk, 1, chunk_size, binary_file_);
118      buffer_.Seek(chunk_size);
119      bytes_written += chunk_size;
120    }
121    base::CloseFile(binary_file_);
122  }
123
124  // AudioInputStream::AudioInputCallback implementation.
125  virtual void OnData(AudioInputStream* stream,
126                      const AudioBus* src,
127                      uint32 hardware_delay_bytes,
128                      double volume) {
129    EXPECT_EQ(bits_per_sample_, 16);
130    const int num_samples = src->frames() * src->channels();
131    scoped_ptr<int16> interleaved(new int16[num_samples]);
132    const int bytes_per_sample = sizeof(*interleaved);
133    src->ToInterleaved(src->frames(), bytes_per_sample, interleaved.get());
134
135    // Store data data in a temporary buffer to avoid making blocking
136    // fwrite() calls in the audio callback. The complete buffer will be
137    // written to file in the destructor.
138    const int size = bytes_per_sample * num_samples;
139    if (buffer_.Append((const uint8*)interleaved.get(), size)) {
140      bytes_to_write_ += size;
141    }
142  }
143
144  virtual void OnError(AudioInputStream* stream) {}
145
146 private:
147  int bits_per_sample_;
148  media::SeekableBuffer buffer_;
149  FILE* binary_file_;
150  size_t bytes_to_write_;
151};
152
153// Convenience method which ensures that we are not running on the build
154// bots and that at least one valid input device can be found. We also
155// verify that we are not running on XP since the low-latency (WASAPI-
156// based) version requires Windows Vista or higher.
157static bool CanRunAudioTests(AudioManager* audio_man) {
158  if (!CoreAudioUtil::IsSupported()) {
159    LOG(WARNING) << "This tests requires Windows Vista or higher.";
160    return false;
161  }
162  // TODO(henrika): note that we use Wave today to query the number of
163  // existing input devices.
164  bool input = audio_man->HasAudioInputDevices();
165  LOG_IF(WARNING, !input) << "No input device detected.";
166  return input;
167}
168
169// Convenience method which creates a default AudioInputStream object but
170// also allows the user to modify the default settings.
171class AudioInputStreamWrapper {
172 public:
173  explicit AudioInputStreamWrapper(AudioManager* audio_manager)
174      : com_init_(ScopedCOMInitializer::kMTA),
175        audio_man_(audio_manager),
176        default_params_(
177            audio_manager->GetInputStreamParameters(
178                  AudioManagerBase::kDefaultDeviceId)) {
179    EXPECT_EQ(format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
180    frames_per_buffer_ = default_params_.frames_per_buffer();
181    // We expect the default buffer size to be a 10ms buffer.
182    EXPECT_EQ(frames_per_buffer_, sample_rate() / 100);
183  }
184
185  ~AudioInputStreamWrapper() {}
186
187  // Creates AudioInputStream object using default parameters.
188  AudioInputStream* Create() {
189    return CreateInputStream();
190  }
191
192  // Creates AudioInputStream object using non-default parameters where the
193  // frame size is modified.
194  AudioInputStream* Create(int frames_per_buffer) {
195    frames_per_buffer_ = frames_per_buffer;
196    return CreateInputStream();
197  }
198
199  AudioParameters::Format format() const { return default_params_.format(); }
200  int channels() const {
201    return ChannelLayoutToChannelCount(default_params_.channel_layout());
202  }
203  int bits_per_sample() const { return default_params_.bits_per_sample(); }
204  int sample_rate() const { return default_params_.sample_rate(); }
205  int frames_per_buffer() const { return frames_per_buffer_; }
206
207 private:
208  AudioInputStream* CreateInputStream() {
209    AudioInputStream* ais = audio_man_->MakeAudioInputStream(
210        AudioParameters(format(), default_params_.channel_layout(),
211                        default_params_.input_channels(),
212                        sample_rate(), bits_per_sample(), frames_per_buffer_,
213                        default_params_.effects()),
214        AudioManagerBase::kDefaultDeviceId);
215    EXPECT_TRUE(ais);
216    return ais;
217  }
218
219  ScopedCOMInitializer com_init_;
220  AudioManager* audio_man_;
221  const AudioParameters default_params_;
222  int frames_per_buffer_;
223};
224
225// Convenience method which creates a default AudioInputStream object.
226static AudioInputStream* CreateDefaultAudioInputStream(
227    AudioManager* audio_manager) {
228  AudioInputStreamWrapper aisw(audio_manager);
229  AudioInputStream* ais = aisw.Create();
230  return ais;
231}
232
233class ScopedAudioInputStream {
234 public:
235  explicit ScopedAudioInputStream(AudioInputStream* stream)
236      : stream_(stream) {}
237
238  ~ScopedAudioInputStream() {
239    if (stream_)
240      stream_->Close();
241  }
242
243  void Close() {
244    if (stream_)
245      stream_->Close();
246    stream_ = NULL;
247  }
248
249  AudioInputStream* operator->() {
250    return stream_;
251  }
252
253  AudioInputStream* get() const { return stream_; }
254
255  void Reset(AudioInputStream* new_stream) {
256    Close();
257    stream_ = new_stream;
258  }
259
260 private:
261  AudioInputStream* stream_;
262
263  DISALLOW_COPY_AND_ASSIGN(ScopedAudioInputStream);
264};
265
266// Verify that we can retrieve the current hardware/mixing sample rate
267// for all available input devices.
268TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) {
269  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
270  if (!CanRunAudioTests(audio_manager.get()))
271    return;
272
273  ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA);
274
275  // Retrieve a list of all available input devices.
276  media::AudioDeviceNames device_names;
277  audio_manager->GetAudioInputDeviceNames(&device_names);
278
279  // Scan all available input devices and repeat the same test for all of them.
280  for (media::AudioDeviceNames::const_iterator it = device_names.begin();
281       it != device_names.end(); ++it) {
282    // Retrieve the hardware sample rate given a specified audio input device.
283    AudioParameters params = WASAPIAudioInputStream::GetInputStreamParameters(
284        it->unique_id);
285    EXPECT_GE(params.sample_rate(), 0);
286  }
287}
288
289// Test Create(), Close() calling sequence.
290TEST(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) {
291  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
292  if (!CanRunAudioTests(audio_manager.get()))
293    return;
294  ScopedAudioInputStream ais(
295      CreateDefaultAudioInputStream(audio_manager.get()));
296  ais.Close();
297}
298
299// Test Open(), Close() calling sequence.
300TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) {
301  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
302  if (!CanRunAudioTests(audio_manager.get()))
303    return;
304  ScopedAudioInputStream ais(
305      CreateDefaultAudioInputStream(audio_manager.get()));
306  EXPECT_TRUE(ais->Open());
307  ais.Close();
308}
309
310// Test Open(), Start(), Close() calling sequence.
311TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) {
312  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
313  if (!CanRunAudioTests(audio_manager.get()))
314    return;
315  ScopedAudioInputStream ais(
316      CreateDefaultAudioInputStream(audio_manager.get()));
317  EXPECT_TRUE(ais->Open());
318  MockAudioInputCallback sink;
319  ais->Start(&sink);
320  ais.Close();
321}
322
323// Test Open(), Start(), Stop(), Close() calling sequence.
324TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) {
325  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
326  if (!CanRunAudioTests(audio_manager.get()))
327    return;
328  ScopedAudioInputStream ais(
329      CreateDefaultAudioInputStream(audio_manager.get()));
330  EXPECT_TRUE(ais->Open());
331  MockAudioInputCallback sink;
332  ais->Start(&sink);
333  ais->Stop();
334  ais.Close();
335}
336
337// Test some additional calling sequences.
338TEST(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) {
339  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
340  if (!CanRunAudioTests(audio_manager.get()))
341    return;
342  ScopedAudioInputStream ais(
343      CreateDefaultAudioInputStream(audio_manager.get()));
344  WASAPIAudioInputStream* wais =
345      static_cast<WASAPIAudioInputStream*>(ais.get());
346
347  // Open(), Open() should fail the second time.
348  EXPECT_TRUE(ais->Open());
349  EXPECT_FALSE(ais->Open());
350
351  MockAudioInputCallback sink;
352
353  // Start(), Start() is a valid calling sequence (second call does nothing).
354  ais->Start(&sink);
355  EXPECT_TRUE(wais->started());
356  ais->Start(&sink);
357  EXPECT_TRUE(wais->started());
358
359  // Stop(), Stop() is a valid calling sequence (second call does nothing).
360  ais->Stop();
361  EXPECT_FALSE(wais->started());
362  ais->Stop();
363  EXPECT_FALSE(wais->started());
364  ais.Close();
365}
366
367TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
368  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
369  if (!CanRunAudioTests(audio_manager.get()))
370    return;
371
372  int count = 0;
373  base::MessageLoopForUI loop;
374
375  // 10 ms packet size.
376
377  // Create default WASAPI input stream which records in stereo using
378  // the shared mixing rate. The default buffer size is 10ms.
379  AudioInputStreamWrapper aisw(audio_manager.get());
380  ScopedAudioInputStream ais(aisw.Create());
381  EXPECT_TRUE(ais->Open());
382
383  MockAudioInputCallback sink;
384
385  // Derive the expected size in bytes of each recorded packet.
386  uint32 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
387      (aisw.bits_per_sample() / 8);
388
389  // We use 10ms packets and will run the test until ten packets are received.
390  // All should contain valid packets of the same size and a valid delay
391  // estimate.
392  EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _))
393      .Times(AtLeast(10))
394      .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
395  ais->Start(&sink);
396  loop.Run();
397  ais->Stop();
398
399  // Store current packet size (to be used in the subsequent tests).
400  int frames_per_buffer_10ms = aisw.frames_per_buffer();
401
402  ais.Close();
403
404  // 20 ms packet size.
405
406  count = 0;
407  ais.Reset(aisw.Create(2 * frames_per_buffer_10ms));
408  EXPECT_TRUE(ais->Open());
409  bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
410      (aisw.bits_per_sample() / 8);
411
412  EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _))
413      .Times(AtLeast(10))
414      .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
415  ais->Start(&sink);
416  loop.Run();
417  ais->Stop();
418  ais.Close();
419
420  // 5 ms packet size.
421
422  count = 0;
423  ais.Reset(aisw.Create(frames_per_buffer_10ms / 2));
424  EXPECT_TRUE(ais->Open());
425  bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
426    (aisw.bits_per_sample() / 8);
427
428  EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _))
429      .Times(AtLeast(10))
430      .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
431  ais->Start(&sink);
432  loop.Run();
433  ais->Stop();
434  ais.Close();
435}
436
437// Test that we can capture a stream in loopback.
438TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) {
439  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
440  if (!audio_manager->HasAudioOutputDevices() || !CoreAudioUtil::IsSupported())
441    return;
442
443  AudioParameters params = audio_manager->GetInputStreamParameters(
444      AudioManagerBase::kLoopbackInputDeviceId);
445  EXPECT_EQ(params.effects(), 0);
446
447  AudioParameters output_params =
448      audio_manager->GetOutputStreamParameters(std::string());
449  EXPECT_EQ(params.sample_rate(), output_params.sample_rate());
450  EXPECT_EQ(params.channel_layout(), output_params.channel_layout());
451
452  ScopedAudioInputStream stream(audio_manager->MakeAudioInputStream(
453      params, AudioManagerBase::kLoopbackInputDeviceId));
454  ASSERT_TRUE(stream->Open());
455  FakeAudioInputCallback sink;
456  stream->Start(&sink);
457  ASSERT_FALSE(sink.error());
458
459  sink.WaitForData();
460  stream.Close();
461
462  EXPECT_GT(sink.num_received_audio_frames(), 0);
463  EXPECT_FALSE(sink.error());
464}
465
466// This test is intended for manual tests and should only be enabled
467// when it is required to store the captured data on a local file.
468// By default, GTest will print out YOU HAVE 1 DISABLED TEST.
469// To include disabled tests in test execution, just invoke the test program
470// with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
471// environment variable to a value greater than 0.
472TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
473  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
474  if (!CanRunAudioTests(audio_manager.get()))
475    return;
476
477  // Name of the output PCM file containing captured data. The output file
478  // will be stored in the directory containing 'media_unittests.exe'.
479  // Example of full name: \src\build\Debug\out_stereo_10sec.pcm.
480  const char* file_name = "out_stereo_10sec.pcm";
481
482  AudioInputStreamWrapper aisw(audio_manager.get());
483  ScopedAudioInputStream ais(aisw.Create());
484  EXPECT_TRUE(ais->Open());
485
486  VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]";
487  WriteToFileAudioSink file_sink(file_name, aisw.bits_per_sample());
488  VLOG(0) << ">> Speak into the default microphone while recording.";
489  ais->Start(&file_sink);
490  base::PlatformThread::Sleep(TestTimeouts::action_timeout());
491  ais->Stop();
492  VLOG(0) << ">> Recording has stopped.";
493  ais.Close();
494}
495
496}  // namespace media
497