Home | History | Annotate | Download | only in audio
      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/file_util.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/path_service.h"
     11 #include "base/synchronization/lock.h"
     12 #include "base/test/test_timeouts.h"
     13 #include "base/time/time.h"
     14 #include "build/build_config.h"
     15 #include "media/audio/audio_io.h"
     16 #include "media/audio/audio_manager_base.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(OS_LINUX) || defined(OS_OPENBSD)
     22 #include "media/audio/linux/audio_manager_linux.h"
     23 #elif defined(OS_MACOSX)
     24 #include "media/audio/mac/audio_manager_mac.h"
     25 #elif defined(OS_WIN)
     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"
     30 #endif
     31 
     32 namespace media {
     33 
     34 #if defined(OS_LINUX) || defined(OS_OPENBSD)
     35 typedef AudioManagerLinux AudioManagerAnyPlatform;
     36 #elif defined(OS_MACOSX)
     37 typedef AudioManagerMac AudioManagerAnyPlatform;
     38 #elif defined(OS_WIN)
     39 typedef AudioManagerWin AudioManagerAnyPlatform;
     40 #elif defined(OS_ANDROID)
     41 typedef AudioManagerAndroid AudioManagerAnyPlatform;
     42 #endif
     43 
     44 // Limits the number of delay measurements we can store in an array and
     45 // then write to file at end of the WASAPIAudioInputOutputFullDuplex test.
     46 static const size_t kMaxDelayMeasurements = 1000;
     47 
     48 // Name of the output text file. The output file will be stored in the
     49 // directory containing media_unittests.exe.
     50 // Example: \src\build\Debug\audio_delay_values_ms.txt.
     51 // See comments for the WASAPIAudioInputOutputFullDuplex test for more details
     52 // about the file format.
     53 static const char kDelayValuesFileName[] = "audio_delay_values_ms.txt";
     54 
     55 // Contains delay values which are reported during the full-duplex test.
     56 // Total delay = |buffer_delay_ms| + |input_delay_ms| + |output_delay_ms|.
     57 struct AudioDelayState {
     58   AudioDelayState()
     59       : delta_time_ms(0),
     60         buffer_delay_ms(0),
     61         input_delay_ms(0),
     62         output_delay_ms(0) {
     63   }
     64 
     65   // Time in milliseconds since last delay report. Typical value is ~10 [ms].
     66   int delta_time_ms;
     67 
     68   // Size of internal sync buffer. Typical value is ~0 [ms].
     69   int buffer_delay_ms;
     70 
     71   // Reported capture/input delay. Typical value is ~10 [ms].
     72   int input_delay_ms;
     73 
     74   // Reported render/output delay. Typical value is ~40 [ms].
     75   int output_delay_ms;
     76 };
     77 
     78 // This class mocks the platform specific audio manager and overrides
     79 // the GetMessageLoop() method to ensure that we can run our tests on
     80 // the main thread instead of the audio thread.
     81 class MockAudioManager : public AudioManagerAnyPlatform {
     82  public:
     83   MockAudioManager() {}
     84   virtual ~MockAudioManager() {}
     85 
     86   virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() OVERRIDE {
     87     return base::MessageLoop::current()->message_loop_proxy();
     88   }
     89 
     90  private:
     91   DISALLOW_COPY_AND_ASSIGN(MockAudioManager);
     92 };
     93 
     94 // Test fixture class.
     95 class AudioLowLatencyInputOutputTest : public testing::Test {
     96  protected:
     97   AudioLowLatencyInputOutputTest() {}
     98 
     99   virtual ~AudioLowLatencyInputOutputTest() {}
    100 
    101   AudioManager* audio_manager() { return &mock_audio_manager_; }
    102   base::MessageLoopForUI* message_loop() { return &message_loop_; }
    103 
    104   // Convenience method which ensures that we are not running on the build
    105   // bots and that at least one valid input and output device can be found.
    106   bool CanRunAudioTests() {
    107     bool input = audio_manager()->HasAudioInputDevices();
    108     bool output = audio_manager()->HasAudioOutputDevices();
    109     LOG_IF(WARNING, !input) << "No input device detected.";
    110     LOG_IF(WARNING, !output) << "No output device detected.";
    111     return input && output;
    112   }
    113 
    114  private:
    115   base::MessageLoopForUI message_loop_;
    116   MockAudioManager mock_audio_manager_;
    117 
    118   DISALLOW_COPY_AND_ASSIGN(AudioLowLatencyInputOutputTest);
    119 };
    120 
    121 // This audio source/sink implementation should be used for manual tests
    122 // only since delay measurements are stored on an output text file.
    123 // All incoming/recorded audio packets are stored in an intermediate media
    124 // buffer which the renderer reads from when it needs audio for playout.
    125 // The total effect is that recorded audio is played out in loop back using
    126 // a sync buffer as temporary storage.
    127 class FullDuplexAudioSinkSource
    128     : public AudioInputStream::AudioInputCallback,
    129       public AudioOutputStream::AudioSourceCallback {
    130  public:
    131   FullDuplexAudioSinkSource(int sample_rate,
    132                             int samples_per_packet,
    133                             int channels)
    134     : sample_rate_(sample_rate),
    135       samples_per_packet_(samples_per_packet),
    136       channels_(channels),
    137       input_elements_to_write_(0),
    138       output_elements_to_write_(0),
    139       previous_write_time_(base::TimeTicks::Now()) {
    140     // Size in bytes of each audio frame (4 bytes for 16-bit stereo PCM).
    141     frame_size_ = (16 / 8) * channels_;
    142 
    143     // Start with the smallest possible buffer size. It will be increased
    144     // dynamically during the test if required.
    145     buffer_.reset(
    146         new media::SeekableBuffer(0, samples_per_packet_ * frame_size_));
    147 
    148     frames_to_ms_ = static_cast<double>(1000.0 / sample_rate_);
    149     delay_states_.reset(new AudioDelayState[kMaxDelayMeasurements]);
    150   }
    151 
    152   virtual ~FullDuplexAudioSinkSource() {
    153     // Get complete file path to output file in the directory containing
    154     // media_unittests.exe. Example: src/build/Debug/audio_delay_values_ms.txt.
    155     base::FilePath file_name;
    156     EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_name));
    157     file_name = file_name.AppendASCII(kDelayValuesFileName);
    158 
    159     FILE* text_file = file_util::OpenFile(file_name, "wt");
    160     DLOG_IF(ERROR, !text_file) << "Failed to open log file.";
    161     LOG(INFO) << ">> Output file " << file_name.value() << " has been created.";
    162 
    163     // Write the array which contains time-stamps, buffer size and
    164     // audio delays values to a text file.
    165     size_t elements_written = 0;
    166     while (elements_written <
    167         std::min(input_elements_to_write_, output_elements_to_write_)) {
    168       const AudioDelayState state = delay_states_[elements_written];
    169       fprintf(text_file, "%d %d %d %d\n",
    170               state.delta_time_ms,
    171               state.buffer_delay_ms,
    172               state.input_delay_ms,
    173               state.output_delay_ms);
    174       ++elements_written;
    175     }
    176 
    177     file_util::CloseFile(text_file);
    178   }
    179 
    180   // AudioInputStream::AudioInputCallback.
    181   virtual void OnData(AudioInputStream* stream,
    182                       const uint8* src, uint32 size,
    183                       uint32 hardware_delay_bytes,
    184                       double volume) OVERRIDE {
    185     base::AutoLock lock(lock_);
    186 
    187     // Update three components in the AudioDelayState for this recorded
    188     // audio packet.
    189     const base::TimeTicks now_time = base::TimeTicks::Now();
    190     const int diff = (now_time - previous_write_time_).InMilliseconds();
    191     previous_write_time_ = now_time;
    192     if (input_elements_to_write_ < kMaxDelayMeasurements) {
    193       delay_states_[input_elements_to_write_].delta_time_ms = diff;
    194       delay_states_[input_elements_to_write_].buffer_delay_ms =
    195           BytesToMilliseconds(buffer_->forward_bytes());
    196       delay_states_[input_elements_to_write_].input_delay_ms =
    197           BytesToMilliseconds(hardware_delay_bytes);
    198       ++input_elements_to_write_;
    199     }
    200 
    201     // Store the captured audio packet in a seekable media buffer.
    202     if (!buffer_->Append(src, size)) {
    203       // An attempt to write outside the buffer limits has been made.
    204       // Double the buffer capacity to ensure that we have a buffer large
    205       // enough to handle the current sample test scenario.
    206       buffer_->set_forward_capacity(2 * buffer_->forward_capacity());
    207       buffer_->Clear();
    208     }
    209   }
    210 
    211   virtual void OnClose(AudioInputStream* stream) OVERRIDE {}
    212   virtual void OnError(AudioInputStream* stream) OVERRIDE {}
    213 
    214   // AudioOutputStream::AudioSourceCallback.
    215   virtual int OnMoreData(AudioBus* audio_bus,
    216                          AudioBuffersState buffers_state) OVERRIDE {
    217     base::AutoLock lock(lock_);
    218 
    219     // Update one component in the AudioDelayState for the packet
    220     // which is about to be played out.
    221     if (output_elements_to_write_ < kMaxDelayMeasurements) {
    222       int output_delay_bytes = buffers_state.hardware_delay_bytes;
    223 #if defined(OS_WIN)
    224       // Special fix for Windows in combination with Wave where the
    225       // pending bytes field of the audio buffer state is used to
    226       // report the delay.
    227       if (!CoreAudioUtil::IsSupported()) {
    228         output_delay_bytes = buffers_state.pending_bytes;
    229       }
    230 #endif
    231       delay_states_[output_elements_to_write_].output_delay_ms =
    232           BytesToMilliseconds(output_delay_bytes);
    233       ++output_elements_to_write_;
    234     }
    235 
    236     int size;
    237     const uint8* source;
    238     // Read the data from the seekable media buffer which contains
    239     // captured data at the same size and sample rate as the output side.
    240     if (buffer_->GetCurrentChunk(&source, &size) && size > 0) {
    241       EXPECT_EQ(channels_, audio_bus->channels());
    242       size = std::min(audio_bus->frames() * frame_size_, size);
    243       EXPECT_EQ(static_cast<size_t>(size) % sizeof(*audio_bus->channel(0)), 0U);
    244       audio_bus->FromInterleaved(
    245           source, size / frame_size_, frame_size_ / channels_);
    246       buffer_->Seek(size);
    247       return size / frame_size_;
    248     }
    249 
    250     return 0;
    251   }
    252 
    253   virtual int OnMoreIOData(AudioBus* source,
    254                            AudioBus* dest,
    255                            AudioBuffersState buffers_state) OVERRIDE {
    256     NOTREACHED();
    257     return 0;
    258   }
    259 
    260   virtual void OnError(AudioOutputStream* stream) OVERRIDE {}
    261 
    262  protected:
    263   // Converts from bytes to milliseconds taking the sample rate and size
    264   // of an audio frame into account.
    265   int BytesToMilliseconds(uint32 delay_bytes) const {
    266     return static_cast<int>((delay_bytes / frame_size_) * frames_to_ms_ + 0.5);
    267   }
    268 
    269  private:
    270   base::Lock lock_;
    271   scoped_ptr<media::SeekableBuffer> buffer_;
    272   int sample_rate_;
    273   int samples_per_packet_;
    274   int channels_;
    275   int frame_size_;
    276   double frames_to_ms_;
    277   scoped_ptr<AudioDelayState[]> delay_states_;
    278   size_t input_elements_to_write_;
    279   size_t output_elements_to_write_;
    280   base::TimeTicks previous_write_time_;
    281 };
    282 
    283 class AudioInputStreamTraits {
    284  public:
    285   typedef AudioInputStream StreamType;
    286 
    287   static AudioParameters GetDefaultAudioStreamParameters(
    288       AudioManager* audio_manager) {
    289     return audio_manager->GetInputStreamParameters(
    290         AudioManagerBase::kDefaultDeviceId);
    291   }
    292 
    293   static StreamType* CreateStream(AudioManager* audio_manager,
    294       const AudioParameters& params) {
    295     return audio_manager->MakeAudioInputStream(params,
    296       AudioManagerBase::kDefaultDeviceId);
    297   }
    298 };
    299 
    300 class AudioOutputStreamTraits {
    301  public:
    302   typedef AudioOutputStream StreamType;
    303 
    304   static AudioParameters GetDefaultAudioStreamParameters(
    305       AudioManager* audio_manager) {
    306     return audio_manager->GetDefaultOutputStreamParameters();
    307   }
    308 
    309   static StreamType* CreateStream(AudioManager* audio_manager,
    310       const AudioParameters& params) {
    311     return audio_manager->MakeAudioOutputStream(params, std::string());
    312   }
    313 };
    314 
    315 // Traits template holding a trait of StreamType. It encapsulates
    316 // AudioInputStream and AudioOutputStream stream types.
    317 template <typename StreamTraits>
    318 class StreamWrapper {
    319  public:
    320   typedef typename StreamTraits::StreamType StreamType;
    321 
    322   explicit StreamWrapper(AudioManager* audio_manager)
    323       :
    324         audio_manager_(audio_manager),
    325         format_(AudioParameters::AUDIO_PCM_LOW_LATENCY),
    326 #if defined(OS_ANDROID)
    327         channel_layout_(CHANNEL_LAYOUT_MONO),
    328 #else
    329         channel_layout_(CHANNEL_LAYOUT_STEREO),
    330 #endif
    331         bits_per_sample_(16) {
    332     // Use the preferred sample rate.
    333     const AudioParameters& params =
    334         StreamTraits::GetDefaultAudioStreamParameters(audio_manager_);
    335     sample_rate_ = params.sample_rate();
    336 
    337     // Use the preferred buffer size. Note that the input side uses the same
    338     // size as the output side in this implementation.
    339     samples_per_packet_ = params.frames_per_buffer();
    340   }
    341 
    342   virtual ~StreamWrapper() {}
    343 
    344   // Creates an Audio[Input|Output]Stream stream object using default
    345   // parameters.
    346   StreamType* Create() {
    347     return CreateStream();
    348   }
    349 
    350   int channels() const {
    351     return ChannelLayoutToChannelCount(channel_layout_);
    352   }
    353   int bits_per_sample() const { return bits_per_sample_; }
    354   int sample_rate() const { return sample_rate_; }
    355   int samples_per_packet() const { return samples_per_packet_; }
    356 
    357  private:
    358   StreamType* CreateStream() {
    359     StreamType* stream = StreamTraits::CreateStream(audio_manager_,
    360         AudioParameters(format_, channel_layout_, sample_rate_,
    361             bits_per_sample_, samples_per_packet_));
    362     EXPECT_TRUE(stream);
    363     return stream;
    364   }
    365 
    366   AudioManager* audio_manager_;
    367   AudioParameters::Format format_;
    368   ChannelLayout channel_layout_;
    369   int bits_per_sample_;
    370   int sample_rate_;
    371   int samples_per_packet_;
    372 };
    373 
    374 typedef StreamWrapper<AudioInputStreamTraits> AudioInputStreamWrapper;
    375 typedef StreamWrapper<AudioOutputStreamTraits> AudioOutputStreamWrapper;
    376 
    377 // This test is intended for manual tests and should only be enabled
    378 // when it is required to make a real-time test of audio in full duplex and
    379 // at the same time create a text file which contains measured delay values.
    380 // The file can later be analyzed off line using e.g. MATLAB.
    381 // MATLAB example:
    382 //   D=load('audio_delay_values_ms.txt');
    383 //   x=cumsum(D(:,1));
    384 //   plot(x, D(:,2), x, D(:,3), x, D(:,4), x, D(:,2)+D(:,3)+D(:,4));
    385 //   axis([0, max(x), 0, max(D(:,2)+D(:,3)+D(:,4))+10]);
    386 //   legend('buffer delay','input delay','output delay','total delay');
    387 //   xlabel('time [msec]')
    388 //   ylabel('delay [msec]')
    389 //   title('Full-duplex audio delay measurement');
    390 TEST_F(AudioLowLatencyInputOutputTest, DISABLED_FullDuplexDelayMeasurement) {
    391   if (!CanRunAudioTests())
    392     return;
    393 
    394   AudioInputStreamWrapper aisw(audio_manager());
    395   AudioInputStream* ais = aisw.Create();
    396   EXPECT_TRUE(ais);
    397 
    398   AudioOutputStreamWrapper aosw(audio_manager());
    399   AudioOutputStream* aos = aosw.Create();
    400   EXPECT_TRUE(aos);
    401 
    402   // This test only supports identical parameters in both directions.
    403   // TODO(henrika): it is possible to cut delay here by using different
    404   // buffer sizes for input and output.
    405   if (aisw.sample_rate() != aosw.sample_rate() ||
    406       aisw.samples_per_packet() != aosw.samples_per_packet() ||
    407       aisw.channels()!= aosw.channels() ||
    408       aisw.bits_per_sample() != aosw.bits_per_sample()) {
    409     LOG(ERROR) << "This test requires symmetric input and output parameters. "
    410         "Ensure that sample rate and number of channels are identical in "
    411         "both directions";
    412     aos->Close();
    413     ais->Close();
    414     return;
    415   }
    416 
    417   EXPECT_TRUE(ais->Open());
    418   EXPECT_TRUE(aos->Open());
    419 
    420   FullDuplexAudioSinkSource full_duplex(
    421       aisw.sample_rate(), aisw.samples_per_packet(), aisw.channels());
    422 
    423   LOG(INFO) << ">> You should now be able to hear yourself in loopback...";
    424   DLOG(INFO) << "   sample_rate       : " << aisw.sample_rate();
    425   DLOG(INFO) << "   samples_per_packet: " << aisw.samples_per_packet();
    426   DLOG(INFO) << "   channels          : " << aisw.channels();
    427 
    428   ais->Start(&full_duplex);
    429   aos->Start(&full_duplex);
    430 
    431   // Wait for approximately 10 seconds. The user shall hear his own voice
    432   // in loop back during this time. At the same time, delay recordings are
    433   // performed and stored in the output text file.
    434   message_loop()->PostDelayedTask(FROM_HERE,
    435       base::MessageLoop::QuitClosure(), TestTimeouts::action_timeout());
    436   message_loop()->Run();
    437 
    438   aos->Stop();
    439   ais->Stop();
    440 
    441   // All Close() operations that run on the mocked audio thread,
    442   // should be synchronous and not post additional close tasks to
    443   // mocked the audio thread. Hence, there is no need to call
    444   // message_loop()->RunUntilIdle() after the Close() methods.
    445   aos->Close();
    446   ais->Close();
    447 }
    448 
    449 }  // namespace media
    450