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