// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "base/basictypes.h" #include "media/audio/audio_output.h" #include "media/audio/simple_sources.h" #include "testing/gtest/include/gtest/gtest.h" namespace { // This class allows to find out if the callbacks are occurring as // expected and if any error has been reported. class TestSourceBasic : public AudioOutputStream::AudioSourceCallback { public: explicit TestSourceBasic() : callback_count_(0), had_error_(0), was_closed_(0) { } // AudioSourceCallback::OnMoreData implementation: virtual size_t OnMoreData(AudioOutputStream* stream, void* dest, size_t max_size) { ++callback_count_; // Touch the first byte to make sure memory is good. if (max_size) reinterpret_cast(dest)[0] = 1; return max_size; } // AudioSourceCallback::OnClose implementation: virtual void OnClose(AudioOutputStream* stream) { ++was_closed_; } // AudioSourceCallback::OnError implementation: virtual void OnError(AudioOutputStream* stream, int code) { ++had_error_; } // Returns how many times OnMoreData() has been called. int callback_count() const { return callback_count_; } // Returns how many times the OnError callback was called. int had_error() const { return had_error_; } void set_error(bool error) { had_error_ += error ? 1 : 0; } // Returns how many times the OnClose callback was called. int was_closed() const { return was_closed_; } private: int callback_count_; int had_error_; int was_closed_; }; bool IsRunningHeadless() { return (0 != ::GetEnvironmentVariableW(L"CHROME_HEADLESS", NULL, 0)); } } // namespace. // Specializes TestSourceBasic to detect that the AudioStream is using // double buffering correctly. class TestSourceDoubleBuffer : public TestSourceBasic { public: TestSourceDoubleBuffer() { buffer_address_[0] = NULL; buffer_address_[1] = NULL; } // Override of TestSourceBasic::OnMoreData. virtual size_t OnMoreData(AudioOutputStream* stream, void* dest, size_t max_size) { // Call the base, which increments the callback_count_. TestSourceBasic::OnMoreData(stream, dest, max_size); if (callback_count() % 2) { set_error(!CompareExistingIfNotNULL(1, dest)); } else { set_error(!CompareExistingIfNotNULL(0, dest)); } if (callback_count() > 2) { set_error(buffer_address_[0] == buffer_address_[1]); } return max_size; } private: bool CompareExistingIfNotNULL(size_t index, void* address) { void*& entry = buffer_address_[index]; if (!entry) entry = address; return (entry == address); } void* buffer_address_[2]; }; // Specializes TestSourceBasic to simulate a source that blocks for some time // in the OnMoreData callback. class TestSourceLaggy : public TestSourceBasic { public: TestSourceLaggy(int laggy_after_buffer, int lag_in_ms) : laggy_after_buffer_(laggy_after_buffer), lag_in_ms_(lag_in_ms) { } virtual size_t OnMoreData(AudioOutputStream* stream, void* dest, size_t max_size) { // Call the base, which increments the callback_count_. TestSourceBasic::OnMoreData(stream, dest, max_size); if (callback_count() > 2) { ::Sleep(lag_in_ms_); } return max_size; } private: int laggy_after_buffer_; int lag_in_ms_; }; // ============================================================================ // Validate that the AudioManager::AUDIO_MOCK callbacks work. TEST(WinAudioTest, MockStreamBasicCallbacks) { AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_MOCK, 2, 8000, 8); ASSERT_TRUE(NULL != oas); EXPECT_TRUE(oas->Open(256)); TestSourceBasic source; oas->Start(&source); EXPECT_GT(source.callback_count(), 0); oas->Stop(); oas->Close(); EXPECT_EQ(0, source.had_error()); EXPECT_EQ(1, source.was_closed()); } // Validate that the SineWaveAudioSource writes the expected values for // the FORMAT_16BIT_MONO. The values are carefully selected so rounding issues // do not affect the result. We also test that AudioManager::GetLastMockBuffer // works. TEST(WinAudioTest, SineWaveAudio16MonoTest) { const size_t samples = 1024; const size_t bytes_per_sample = 2; const int freq = 200; SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, freq, AudioManager::kTelephoneSampleRate); AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_MOCK, 1, AudioManager::kTelephoneSampleRate, bytes_per_sample * 2); ASSERT_TRUE(NULL != oas); EXPECT_TRUE(oas->Open(samples * bytes_per_sample)); oas->Start(&source); oas->Stop(); oas->Close(); const int16* last_buffer = reinterpret_cast(audio_man->GetLastMockBuffer()); ASSERT_TRUE(NULL != last_buffer); size_t half_period = AudioManager::kTelephoneSampleRate / (freq * 2); // Spot test positive incursion of sine wave. EXPECT_EQ(0, last_buffer[0]); EXPECT_EQ(5126, last_buffer[1]); EXPECT_TRUE(last_buffer[1] < last_buffer[2]); EXPECT_TRUE(last_buffer[2] < last_buffer[3]); // Spot test negative incursion of sine wave. EXPECT_EQ(0, last_buffer[half_period]); EXPECT_EQ(-5126, last_buffer[half_period + 1]); EXPECT_TRUE(last_buffer[half_period + 1] > last_buffer[half_period + 2]); EXPECT_TRUE(last_buffer[half_period + 2] > last_buffer[half_period + 3]); } // =========================================================================== // Validation of AudioManager::AUDIO_PCM_LINEAR // // The tests tend to fail in the build bots when somebody connects to them via // via remote-desktop because it installs an audio device that fails to open // at some point, possibly when the connection goes idle. So that is why we // skipped them in headless mode. // Test that can it be created and closed. TEST(WinAudioTest, PCMWaveStreamGetAndClose) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 2, 8000, 16); ASSERT_TRUE(NULL != oas); oas->Close(); } // Test that it can be opened and closed. TEST(WinAudioTest, PCMWaveStreamOpenAndClose) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 2, 8000, 16); ASSERT_TRUE(NULL != oas); EXPECT_TRUE(oas->Open(1024)); oas->Close(); } // Test that it uses the double buffers correctly. Because it uses the actual // audio device, you might hear a short pop noise for a short time. TEST(WinAudioTest, PCMWaveStreamDoubleBuffer) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, 16000, 16); ASSERT_TRUE(NULL != oas); TestSourceDoubleBuffer test_double_buffer; EXPECT_TRUE(oas->Open(512)); oas->Start(&test_double_buffer); ::Sleep(300); EXPECT_GT(test_double_buffer.callback_count(), 2); EXPECT_FALSE(test_double_buffer.had_error()); oas->Stop(); ::Sleep(1000); oas->Close(); } // Test potential deadlock situations if the source is slow or blocks for some // time. The actual EXPECT_GT are mostly meaningless and the real test is that // the test completes in reasonable time. TEST(WinAudioTest, PCMWaveSlowSource) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, 16000, 16); ASSERT_TRUE(NULL != oas); TestSourceLaggy test_laggy(2, 90); EXPECT_TRUE(oas->Open(512)); // The test parameters cause a callback every 32 ms and the source is // sleeping for 90 ms, so it is guaranteed that we run out of ready buffers. oas->Start(&test_laggy); ::Sleep(1000); EXPECT_GT(test_laggy.callback_count(), 2); EXPECT_FALSE(test_laggy.had_error()); oas->Stop(); ::Sleep(1000); oas->Close(); } // This test produces actual audio for 1.5 seconds on the default wave // device at 44.1K s/sec. Parameters have been chosen carefully so you should // not hear pops or noises while the sound is playing. TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44Kss) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, AudioManager::kAudioCDSampleRate, 16); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, 200.0, AudioManager::kAudioCDSampleRate); size_t bytes_100_ms = (AudioManager::kAudioCDSampleRate / 10) * 2; EXPECT_TRUE(oas->Open(bytes_100_ms)); oas->SetVolume(1.0, 1.0); oas->Start(&source); ::Sleep(1500); oas->Stop(); oas->Close(); } // This test produces actual audio for for 1.5 seconds on the default wave // device at 22K s/sec. Parameters have been chosen carefully so you should // not hear pops or noises while the sound is playing. The audio also should // sound with a lower volume than PCMWaveStreamPlay200HzTone44Kss. TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) { if (IsRunningHeadless()) return; AudioManager* audio_man = AudioManager::GetAudioManager(); ASSERT_TRUE(NULL != audio_man); if (!audio_man->HasAudioDevices()) return; AudioOutputStream* oas = audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, AudioManager::kAudioCDSampleRate/2, 16); ASSERT_TRUE(NULL != oas); SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, 200.0, AudioManager::kAudioCDSampleRate/2); size_t bytes_100_ms = (AudioManager::kAudioCDSampleRate / 20) * 2; EXPECT_TRUE(oas->Open(bytes_100_ms)); oas->SetVolume(0.5, 0.5); oas->Start(&source); ::Sleep(1500); // Test that the volume is within the set limits. double left_volume = 0.0; double right_volume = 0.0; oas->GetVolume(&left_volume, &right_volume); EXPECT_LT(left_volume, 0.51); EXPECT_GT(left_volume, 0.49); EXPECT_LT(right_volume, 0.51); EXPECT_GT(right_volume, 0.49); oas->Stop(); oas->Close(); }