summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorcpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-16 18:17:25 +0000
committercpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2009-04-16 18:17:25 +0000
commit54e1d52455272e0af32b4c6080f1914f6f2c4f95 (patch)
tree753f2b2f72faed1e1470b57d33c8a82c2dcafbb6 /media
parent4156e1f4ca5aff52e21f6711243ba118c1b7f792 (diff)
downloadchromium_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.cc69
-rw-r--r--media/audio/win/waveout_output_win.cc47
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.