summaryrefslogtreecommitdiffstats
path: root/media/audio/win
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-24 18:00:32 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-08-24 18:00:32 +0000
commit1328d55d94cf7b973da1a0a884735c8cbdeb2423 (patch)
treebbd05bc64668be46fc3804b8ce46d59ac8b44769 /media/audio/win
parent21f659de19e3ea874a0958372a311746a0e764c2 (diff)
downloadchromium_src-1328d55d94cf7b973da1a0a884735c8cbdeb2423.zip
chromium_src-1328d55d94cf7b973da1a0a884735c8cbdeb2423.tar.gz
chromium_src-1328d55d94cf7b973da1a0a884735c8cbdeb2423.tar.bz2
Providing unplayed bytes in audio hardware buffer
BUG=20007 TEST=WinAudioTest.PCMWaveStreamPendingBytes We used to guess what is in the hardware audio buffer to perform audio sync. But due the triple buffering and other platforms has other buffering scheme we need to be able to know how many bytes are left in the hardware buffer. We provide this data via AudioCallback::OnMoreData(), so we can predict when the requested buffer will be played. Review URL: http://codereview.chromium.org/174243 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@24114 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/audio/win')
-rw-r--r--media/audio/win/audio_output_win_unittest.cc76
-rw-r--r--media/audio/win/waveout_output_win.cc18
-rw-r--r--media/audio/win/waveout_output_win.h3
3 files changed, 86 insertions, 11 deletions
diff --git a/media/audio/win/audio_output_win_unittest.cc b/media/audio/win/audio_output_win_unittest.cc
index 2393957..38a32f8 100644
--- a/media/audio/win/audio_output_win_unittest.cc
+++ b/media/audio/win/audio_output_win_unittest.cc
@@ -9,8 +9,17 @@
#include "base/file_util.h"
#include "media/audio/audio_output.h"
#include "media/audio/simple_sources.h"
+#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::DoAll;
+using ::testing::InSequence;
+using ::testing::NiceMock;
+using ::testing::NotNull;
+using ::testing::Return;
+
namespace {
const wchar_t kAudioFile1_16b_m_16K[]
@@ -27,7 +36,7 @@ class TestSourceBasic : public AudioOutputStream::AudioSourceCallback {
}
// AudioSourceCallback::OnMoreData implementation:
virtual size_t OnMoreData(AudioOutputStream* stream,
- void* dest, size_t max_size) {
+ void* dest, size_t max_size, int pending_bytes) {
++callback_count_;
// Touch the first byte to make sure memory is good.
if (max_size)
@@ -83,9 +92,9 @@ class TestSourceTripleBuffer : public TestSourceBasic {
}
// Override of TestSourceBasic::OnMoreData.
virtual size_t OnMoreData(AudioOutputStream* stream,
- void* dest, size_t max_size) {
+ void* dest, size_t max_size, int pending_bytes) {
// Call the base, which increments the callback_count_.
- TestSourceBasic::OnMoreData(stream, dest, max_size);
+ TestSourceBasic::OnMoreData(stream, dest, max_size, 0);
if (callback_count() % kNumBuffers == 2) {
set_error(!CompareExistingIfNotNULL(2, dest));
} else if (callback_count() % kNumBuffers == 1) {
@@ -119,9 +128,9 @@ class TestSourceLaggy : public TestSourceBasic {
: laggy_after_buffer_(laggy_after_buffer), lag_in_ms_(lag_in_ms) {
}
virtual size_t OnMoreData(AudioOutputStream* stream,
- void* dest, size_t max_size) {
+ void* dest, size_t max_size, int pending_bytes) {
// Call the base, which increments the callback_count_.
- TestSourceBasic::OnMoreData(stream, dest, max_size);
+ TestSourceBasic::OnMoreData(stream, dest, max_size, 0);
if (callback_count() > kNumBuffers) {
::Sleep(lag_in_ms_);
}
@@ -132,6 +141,14 @@ class TestSourceLaggy : public TestSourceBasic {
int lag_in_ms_;
};
+class MockAudioSource : public AudioOutputStream::AudioSourceCallback {
+ public:
+ MOCK_METHOD4(OnMoreData, size_t(AudioOutputStream* stream, void* dest,
+ size_t max_size, int pending_bytes));
+ MOCK_METHOD1(OnClose, void(AudioOutputStream* stream));
+ MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code));
+};
+
// Helper class to memory map an entire file. The mapping is read-only. Don't
// use for gigabyte-sized files. Attempts to write to this memory generate
// memory access violations.
@@ -479,12 +496,12 @@ TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) {
if (!audio_man->HasAudioDevices())
return;
AudioOutputStream* oas =
- audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1,
- AudioManager::kAudioCDSampleRate, 16);
+ 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);
+ 200.0, AudioManager::kAudioCDSampleRate);
size_t bytes_100_ms = (AudioManager::kAudioCDSampleRate / 10) * 2;
EXPECT_TRUE(oas->Open(bytes_100_ms));
@@ -506,3 +523,46 @@ TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) {
oas->Close();
}
+// Check that the pending bytes value is correct what the stream starts.
+TEST(WinAudioTest, PCMWaveStreamPendingBytes) {
+ 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);
+
+ NiceMock<MockAudioSource> source;
+ size_t bytes_100_ms = (AudioManager::kAudioCDSampleRate / 10) * 2;
+ EXPECT_TRUE(oas->Open(bytes_100_ms));
+
+ // We expect the amount of pending bytes will reaching 2 times of
+ // |bytes_100_ms| because the audio output stream has a triple buffer scheme.
+ // And then we will try to provide zero data so the amount of pending bytes
+ // will go down and eventually read zero.
+ InSequence s;
+ EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, 0))
+ .WillOnce(Return(bytes_100_ms));
+ EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, bytes_100_ms))
+ .WillOnce(Return(bytes_100_ms));
+ EXPECT_CALL(source, OnMoreData(oas, NotNull(),
+ bytes_100_ms, 2 * bytes_100_ms))
+ .WillOnce(Return(bytes_100_ms));
+ EXPECT_CALL(source, OnMoreData(oas, NotNull(),
+ bytes_100_ms, 2 * bytes_100_ms))
+ .WillOnce(Return(0));
+ EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, bytes_100_ms))
+ .WillOnce(Return(0));
+ EXPECT_CALL(source, OnMoreData(oas, NotNull(), bytes_100_ms, 0))
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(0));
+
+ oas->Start(&source);
+ ::Sleep(500);
+ oas->Stop();
+ oas->Close();
+}
diff --git a/media/audio/win/waveout_output_win.cc b/media/audio/win/waveout_output_win.cc
index 0b4cc6f..21532b7 100644
--- a/media/audio/win/waveout_output_win.cc
+++ b/media/audio/win/waveout_output_win.cc
@@ -58,7 +58,8 @@ PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream(
buffer_(NULL),
buffer_size_(0),
volume_(1),
- channels_(channels) {
+ channels_(channels),
+ pending_bytes_(0) {
format_.wFormatTag = WAVE_FORMAT_PCM;
format_.nChannels = channels > 2 ? 2 : channels;
format_.nSamplesPerSec = sampling_rate;
@@ -144,6 +145,7 @@ void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) {
WAVEHDR* buffer = buffer_;
for (int ix = 0; ix != kNumBuffers; ++ix) {
QueueNextPacket(buffer); // Read more data.
+ pending_bytes_ += buffer->dwBufferLength;
buffer = GetNextBuffer(buffer);
}
buffer = buffer_;
@@ -235,8 +237,12 @@ void PCMWaveOutAudioOutputStream::HandleError(MMRESULT error) {
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.
+ // If we are down sampling to a smaller number of channels, we need to
+ // scale up the amount of pending bytes.
// TODO(fbarchard): Handle used 0 by queueing more.
- size_t used = callback_->OnMoreData(this, buffer->lpData, buffer_size_);
+ int scaled_pending_bytes = pending_bytes_ * channels_ / format_.nChannels;
+ size_t used = callback_->OnMoreData(this, buffer->lpData, buffer_size_,
+ scaled_pending_bytes);
if (used <= buffer_size_) {
buffer->dwBufferLength = used * format_.nChannels / channels_;
if (channels_ > 2 && format_.nChannels == 2) {
@@ -248,7 +254,6 @@ void PCMWaveOutAudioOutputStream::QueueNextPacket(WAVEHDR *buffer) {
format_.nChannels, format_.wBitsPerSample >> 3,
volume_);
}
-
} else {
HandleError(0);
return;
@@ -282,6 +287,11 @@ void PCMWaveOutAudioOutputStream::WaveCallback(HWAVEOUT hwo, UINT msg,
// Not sure if ever hit this but just in case.
return;
}
+
+ // Before we queue the next packet, we need to adjust the number of pending
+ // bytes since the last write to hardware.
+ obj->pending_bytes_ -= buffer->dwBufferLength;
+
obj->QueueNextPacket(buffer);
// Time to send the buffer to the audio driver. Since we are reusing
@@ -290,6 +300,8 @@ void PCMWaveOutAudioOutputStream::WaveCallback(HWAVEOUT hwo, UINT msg,
if (result != MMSYSERR_NOERROR)
obj->HandleError(result);
+ obj->pending_bytes_ += buffer->dwBufferLength;
+
} else if (msg == WOM_CLOSE) {
// We can be closed before calling Start, so it is possible to have a
// null callback at this point.
diff --git a/media/audio/win/waveout_output_win.h b/media/audio/win/waveout_output_win.h
index e072851..4f090ba 100644
--- a/media/audio/win/waveout_output_win.h
+++ b/media/audio/win/waveout_output_win.h
@@ -89,6 +89,9 @@ class PCMWaveOutAudioOutputStream : public AudioOutputStream {
// Channels from 0 to 6.
int channels_;
+ // Number of bytes yet to be played in the hardware buffer.
+ int pending_bytes_;
+
// The id assigned by the operating system to the selected wave output
// hardware device. Usually this is just -1 which means 'default device'.
UINT device_id_;