diff options
author | cpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-16 18:17:25 +0000 |
---|---|---|
committer | cpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-04-16 18:17:25 +0000 |
commit | 54e1d52455272e0af32b4c6080f1914f6f2c4f95 (patch) | |
tree | 753f2b2f72faed1e1470b57d33c8a82c2dcafbb6 /media | |
parent | 4156e1f4ca5aff52e21f6711243ba118c1b7f792 (diff) | |
download | chromium_src-54e1d52455272e0af32b4c6080f1914f6f2c4f95.zip chromium_src-54e1d52455272e0af32b4c6080f1914f6f2c4f95.tar.gz chromium_src-54e1d52455272e0af32b4c6080f1914f6f2c4f95.tar.bz2 |
Windows low level audio small changes
- A fix to reported deadlock: don't start the real-time thread until the inital buffers are filled
- A new unitest that uses a slow source, the idea is to try to repro the deadlock
- Implements GetVolume and SetVolume
Review URL: http://codereview.chromium.org/67154
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@13856 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/audio/win/audio_output_win_unittest.cc | 69 | ||||
-rw-r--r-- | media/audio/win/waveout_output_win.cc | 47 |
2 files changed, 99 insertions, 17 deletions
diff --git a/media/audio/win/audio_output_win_unittest.cc b/media/audio/win/audio_output_win_unittest.cc index d0db853..d9ed2f9 100644 --- a/media/audio/win/audio_output_win_unittest.cc +++ b/media/audio/win/audio_output_win_unittest.cc @@ -20,7 +20,6 @@ class TestSourceBasic : public AudioOutputStream::AudioSourceCallback { had_error_(0), was_closed_(0) { } - // AudioSourceCallback::OnMoreData implementation: virtual size_t OnMoreData(AudioOutputStream* stream, void* dest, size_t max_size) { @@ -30,22 +29,18 @@ class TestSourceBasic : public AudioOutputStream::AudioSourceCallback { reinterpret_cast<char*>(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_; @@ -54,7 +49,6 @@ class TestSourceBasic : public AudioOutputStream::AudioSourceCallback { 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_; @@ -72,7 +66,6 @@ bool IsRunningHeadless() { } // namespace. - // Specializes TestSourceBasic to detect that the AudioStream is using // double buffering correctly. class TestSourceDoubleBuffer : public TestSourceBasic { @@ -108,6 +101,26 @@ class TestSourceDoubleBuffer : public TestSourceBasic { 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_; +}; // ============================================================================ @@ -231,6 +244,32 @@ TEST(WinAudioTest, PCMWaveStreamDoubleBuffer) { 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. @@ -251,6 +290,7 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44Kss) { 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(); @@ -259,7 +299,8 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44Kss) { // 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. +// 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; @@ -277,8 +318,20 @@ TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) { 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(); } + diff --git a/media/audio/win/waveout_output_win.cc b/media/audio/win/waveout_output_win.cc index 59efa4e..9a3f581 100644 --- a/media/audio/win/waveout_output_win.cc +++ b/media/audio/win/waveout_output_win.cc @@ -34,6 +34,10 @@ namespace { // between how fast data needs to be provided versus memory usage. const size_t kNumBuffers = 2; +// The maximum volume for a PCM device on windows. Volume is logarithmic so the +// perceived volume increase sounds linear. +const double kMaxVolumeLevel = 65535.0; + // Our sound buffers are allocated once and kept in a linked list using the // the WAVEHDR::dwUser variable. The last buffer points to the first buffer. WAVEHDR* GetNextBuffer(WAVEHDR* current) { @@ -137,6 +141,16 @@ void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) { QueueNextPacket(buffer); buffer = GetNextBuffer(buffer); } + buffer = buffer_; + // Send the buffers to the audio driver. + for (int ix = 0; ix != kNumBuffers; ++ix) { + MMRESULT result = ::waveOutWrite(waveout_, buffer, sizeof(WAVEHDR)); + if (result != MMSYSERR_NOERROR) { + HandleError(result); + break; + } + buffer = GetNextBuffer(buffer); + } } // Stopping is tricky. First, no buffer should be locked by the audio driver @@ -180,17 +194,32 @@ void PCMWaveOutAudioOutputStream::Close() { manager_->ReleaseStream(this); } -// TODO(cpu): Implement SetVolume and GetVolume. void PCMWaveOutAudioOutputStream::SetVolume(double left_level, double right_level) { - if (state_ != PCMA_READY) + if (!waveout_) + return; + uint16 left = static_cast<uint16>(left_level * kMaxVolumeLevel); + uint16 right = static_cast<uint16>(right_level * kMaxVolumeLevel); + DWORD volume_packed = MAKELONG(left, right); + MMRESULT res = ::waveOutSetVolume(waveout_, volume_packed); + if (res != MMSYSERR_NOERROR) { + HandleError(res); return; + } } void PCMWaveOutAudioOutputStream::GetVolume(double* left_level, double* right_level) { - if (state_ != PCMA_READY) + if (!waveout_) + return; + DWORD volume_packed = 0; + MMRESULT res = ::waveOutGetVolume(waveout_, &volume_packed); + if (res != MMSYSERR_NOERROR) { + HandleError(res); return; + } + *left_level = double(LOWORD(volume_packed)) / kMaxVolumeLevel; + *right_level = double(HIWORD(volume_packed)) / kMaxVolumeLevel; } size_t PCMWaveOutAudioOutputStream::GetNumBuffers() { @@ -206,19 +235,13 @@ void PCMWaveOutAudioOutputStream::QueueNextPacket(WAVEHDR *buffer) { // Call the source which will fill our buffer with pleasant sounds and // return to us how many bytes were used. size_t used = callback_->OnMoreData(this, buffer->lpData, buffer_size_); - if (used <= buffer_size_) { buffer->dwBufferLength = used; } else { HandleError(0); return; } - // Time to queue the buffer to the audio driver. Since we are reusing - // the same buffers we can get away without calling waveOutPrepareHeader. buffer->dwFlags = WHDR_PREPARED; - MMRESULT result = ::waveOutWrite(waveout_, buffer, sizeof(WAVEHDR)); - if (result != MMSYSERR_NOERROR) - HandleError(result); } // Windows call us back in this function when some events happen. Most notably @@ -248,6 +271,12 @@ void PCMWaveOutAudioOutputStream::WaveCallback(HWAVEOUT hwo, UINT msg, return; } obj->QueueNextPacket(buffer); + // Time to send the buffer to the audio driver. Since we are reusing + // the same buffers we can get away without calling waveOutPrepareHeader. + MMRESULT result = ::waveOutWrite(hwo, buffer, sizeof(WAVEHDR)); + if (result != MMSYSERR_NOERROR) + obj->HandleError(result); + } else if (msg == WOM_CLOSE) { // We can be closed before calling Start, so it is possible to have a // null callback at this point. |