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/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 
     38 namespace media {
     39 
     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;
     48 #elif defined(OS_WIN)
     49 typedef AudioManagerWin AudioManagerAnyPlatform;
     50 #elif defined(OS_ANDROID)
     51 typedef AudioManagerAndroid AudioManagerAnyPlatform;
     52 #else
     53 typedef 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.
     58 static 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.
     65 static 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|.
     69 struct 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.
     93 class 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.
    108 class 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.
    140 class 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 
    289 class 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 
    306 class 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.
    323 template <typename StreamTraits>
    324 class 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 
    380 typedef StreamWrapper<AudioInputStreamTraits> AudioInputStreamWrapper;
    381 typedef 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');
    396 TEST_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