diff options
-rw-r--r-- | chrome/common/render_messages.h | 4 | ||||
-rw-r--r-- | media/audio/audio_output.h | 28 | ||||
-rw-r--r-- | media/audio/linux/alsa_output_unittest.cc | 2 | ||||
-rw-r--r-- | media/audio/win/audio_output_win.cc | 6 | ||||
-rw-r--r-- | media/audio/win/audio_output_win_unittest.cc | 34 | ||||
-rw-r--r-- | media/audio/win/waveout_output_win.cc | 19 | ||||
-rw-r--r-- | media/audio/win/waveout_output_win.h | 7 |
7 files changed, 71 insertions, 29 deletions
diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h index fc4a994..3bd76e5 100644 --- a/chrome/common/render_messages.h +++ b/chrome/common/render_messages.h @@ -1744,8 +1744,8 @@ struct ParamTraits<AudioManager::Format> { case AudioManager::AUDIO_PCM_LINEAR: format = L"AUDIO_PCM_LINEAR"; break; - case AudioManager::AUDIO_PCM_DELTA: - format = L"AUDIO_PCM_DELTA"; + case AudioManager::AUDIO_PCM_LOW_LATENCY: + format = L"AUDIO_PCM_LOW_LATENCY"; break; case AudioManager::AUDIO_MOCK: format = L"AUDIO_MOCK"; diff --git a/media/audio/audio_output.h b/media/audio/audio_output.h index 29712bf..2d249d7 100644 --- a/media/audio/audio_output.h +++ b/media/audio/audio_output.h @@ -72,9 +72,11 @@ class AudioOutputStream { // Open the stream. |packet_size| is the requested buffer allocation which // the audio source thinks it can usually fill without blocking. Internally - // two buffers of |packet_size| size are created, one will be locked for - // playback and one will be ready to be filled in the call to + // two or three buffers of |packet_size| size are created, one will be + // locked for playback and one will be ready to be filled in the call to // AudioSourceCallback::OnMoreData(). + // The number of buffers is controlled by AUDIO_PCM_LOW_LATENCY. See more + // information below. // // TODO(ajwong): Streams are not reusable, so try to move packet_size into the // constructor. @@ -111,11 +113,10 @@ class AudioOutputStream { class AudioManager { public: enum Format { - AUDIO_PCM_LINEAR = 0, // Pulse code modulation means 'raw' amplitude - // samples. - AUDIO_PCM_DELTA, // Delta-encoded pulse code modulation. - AUDIO_MOCK, // Creates a dummy AudioOutputStream object. - AUDIO_LAST_FORMAT // Only used for validation of format. + AUDIO_PCM_LINEAR = 0, // PCM is 'raw' amplitude samples. + AUDIO_PCM_LOW_LATENCY, // Linear PCM, low latency requested. + AUDIO_MOCK, // Creates a dummy AudioOutputStream object. + AUDIO_LAST_FORMAT // Only used for validation of format. }; // Telephone quality sample rate, mostly for speech-only audio. @@ -129,13 +130,18 @@ class AudioManager { // guarantee that the existing devices support all formats and sample rates. virtual bool HasAudioDevices() = 0; - // Factory for all the supported stream formats. At this moment |channels| - // can be 1 (mono) or 2 (stereo). The |sample_rate| is in hertz and can be - // any value supported by the underlying platform. For some future formats - // the |sample_rate| and |bits_per_sample| can take special values. + // Factory for all the supported stream formats. The |channels| can be 1 to 5. + // The |sample_rate| is in hertz and can be any value supported by the + // platform. For some future formats the |sample_rate| and |bits_per_sample| + // can take special values. // Returns NULL if the combination of the parameters is not supported, or if // we have reached some other platform specific limit. // + // AUDIO_PCM_LOW_LATENCY can be passed to this method and it has two effects: + // 1- Instead of triple buffered the audio will be double buffered. + // 2- A low latency driver or alternative audio subsystem will be used when + // available. + // // Do not free the returned AudioOutputStream. It is owned by AudioManager. virtual AudioOutputStream* MakeAudioStream(Format format, int channels, int sample_rate, diff --git a/media/audio/linux/alsa_output_unittest.cc b/media/audio/linux/alsa_output_unittest.cc index 70949b5..b426301 100644 --- a/media/audio/linux/alsa_output_unittest.cc +++ b/media/audio/linux/alsa_output_unittest.cc @@ -193,7 +193,7 @@ TEST_F(AlsaPcmOutputStreamTest, ConstructedState) { // Bad format. test_stream_ = new AlsaPcmOutputStream(kTestDeviceName, - AudioManager::AUDIO_PCM_DELTA, + AudioManager::AUDIO_LAST_FORMAT, kTestChannels, kTestSampleRate, kTestBitsPerSample, diff --git a/media/audio/win/audio_output_win.cc b/media/audio/win/audio_output_win.cc index f7eac01..ebfd915 100644 --- a/media/audio/win/audio_output_win.cc +++ b/media/audio/win/audio_output_win.cc @@ -54,7 +54,11 @@ AudioOutputStream* AudioManagerWin::MakeAudioStream(Format format, int channels, if (format == AUDIO_MOCK) { return FakeAudioOutputStream::MakeFakeStream(); } else if (format == AUDIO_PCM_LINEAR) { - return new PCMWaveOutAudioOutputStream(this, channels, sample_rate, + return new PCMWaveOutAudioOutputStream(this, channels, sample_rate, 3, + bits_per_sample, WAVE_MAPPER); + } else if (format == AUDIO_PCM_LOW_LATENCY) { + // TODO(cpu): waveout cannot hit 20ms latency. Use other method. + return new PCMWaveOutAudioOutputStream(this, channels, sample_rate, 2, bits_per_sample, WAVE_MAPPER); } return NULL; diff --git a/media/audio/win/audio_output_win_unittest.cc b/media/audio/win/audio_output_win_unittest.cc index c0acfe3..70e9329 100644 --- a/media/audio/win/audio_output_win_unittest.cc +++ b/media/audio/win/audio_output_win_unittest.cc @@ -523,6 +523,36 @@ TEST(WinAudioTest, PCMWaveStreamPlayTwice200HzTone44Kss) { oas->Close(); } +// With the low latency mode, we have two buffers instead of 3 and we +// should be able to handle 20ms buffers at 44KHz. See also the SyncSocketBasic +// test below. +// TODO(cpu): right now the best we can do is 50ms before it sounds choppy. +TEST(WinAudioTest, PCMWaveStreamPlay200HzTone44KssLowLatency) { + 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_LOW_LATENCY, 1, + AudioManager::kAudioCDSampleRate, 16); + ASSERT_TRUE(NULL != oas); + + SineWaveAudioSource source(SineWaveAudioSource::FORMAT_16BIT_LINEAR_PCM, 1, + 200.0, AudioManager::kAudioCDSampleRate); + size_t bytes_50_ms = (AudioManager::kAudioCDSampleRate / 20) * sizeof(uint16); + + EXPECT_TRUE(oas->Open(bytes_50_ms)); + oas->SetVolume(1.0); + + // Play the wave for .8 seconds. + oas->Start(&source); + ::Sleep(800); + oas->Stop(); + oas->Close(); +} + // Check that the pending bytes value is correct what the stream starts. TEST(WinAudioTest, PCMWaveStreamPendingBytes) { if (IsRunningHeadless()) @@ -640,6 +670,8 @@ DWORD __stdcall SyncSocketThread(void* context) { // The emphasis is to test low-latency with buffers less than 100ms. With // the waveout api it seems not possible to go below 50ms. In this test // you should hear a continous 200Hz tone. +// +// TODO(cpu): This actually sounds choppy most of the time. Fix it. TEST(WinAudioTest, SyncSocketBasic) { if (IsRunningHeadless()) return; @@ -651,7 +683,7 @@ TEST(WinAudioTest, SyncSocketBasic) { int sample_rate = AudioManager::kAudioCDSampleRate; AudioOutputStream* oas = - audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LINEAR, 1, + audio_man->MakeAudioStream(AudioManager::AUDIO_PCM_LOW_LATENCY, 1, sample_rate, 16); ASSERT_TRUE(NULL != oas); diff --git a/media/audio/win/waveout_output_win.cc b/media/audio/win/waveout_output_win.cc index 0611da1..e0fdd1e 100644 --- a/media/audio/win/waveout_output_win.cc +++ b/media/audio/win/waveout_output_win.cc @@ -30,12 +30,6 @@ // or that we take locks inside WaveCallback() or QueueNextPacket(). namespace { - -// We settled for a triple buffering scheme. It seems to strike a good balance -// between how fast data needs to be provided versus memory usage. -// Double buffering is insufficient for Vista and produces stutter. -const size_t kNumBuffers = 3; - // Sixty four MB is the maximum buffer size per AudioOutputStream. const size_t kMaxOpenBufferSize = 1024 * 1024 * 64; @@ -48,13 +42,14 @@ WAVEHDR* GetNextBuffer(WAVEHDR* current) { } // namespace PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream( - AudioManagerWin* manager, int channels, int sampling_rate, + AudioManagerWin* manager, int channels, int sampling_rate, int num_buffers, char bits_per_sample, UINT device_id) : state_(PCMA_BRAND_NEW), manager_(manager), device_id_(device_id), waveout_(NULL), callback_(NULL), + num_buffers_(num_buffers), buffer_(NULL), buffer_size_(0), volume_(1), @@ -81,6 +76,8 @@ bool PCMWaveOutAudioOutputStream::Open(size_t buffer_size) { return false; if (buffer_size > kMaxOpenBufferSize) return false; + if (num_buffers_ < 2 || num_buffers_ > 5) + return false; // Open the device. We'll be getting callback in WaveCallback function. They // occur in a magic, time-critical thread that windows creates. MMRESULT result = ::waveOutOpen(&waveout_, device_id_, &format_, @@ -102,7 +99,7 @@ bool PCMWaveOutAudioOutputStream::Open(size_t buffer_size) { void PCMWaveOutAudioOutputStream::SetupBuffers(size_t rq_size) { WAVEHDR* last = NULL; WAVEHDR* first = NULL; - for (int ix = 0; ix != kNumBuffers; ++ix) { + for (int ix = 0; ix != num_buffers_; ++ix) { size_t sz = sizeof(WAVEHDR) + rq_size; buffer_ = reinterpret_cast<WAVEHDR*>(new char[sz]); buffer_->lpData = reinterpret_cast<char*>(buffer_) + sizeof(WAVEHDR); @@ -125,7 +122,7 @@ void PCMWaveOutAudioOutputStream::SetupBuffers(size_t rq_size) { void PCMWaveOutAudioOutputStream::FreeBuffers() { WAVEHDR* current = buffer_; - for (int ix = 0; ix != kNumBuffers; ++ix) { + for (int ix = 0; ix != num_buffers_; ++ix) { WAVEHDR* next = GetNextBuffer(current); ::waveOutUnprepareHeader(waveout_, current, sizeof(WAVEHDR)); delete[] reinterpret_cast<char*>(current); @@ -144,7 +141,7 @@ void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) { state_ = PCMA_PLAYING; pending_bytes_ = 0; WAVEHDR* buffer = buffer_; - for (int ix = 0; ix != kNumBuffers; ++ix) { + for (int ix = 0; ix != num_buffers_; ++ix) { QueueNextPacket(buffer); // Read more data. pending_bytes_ += buffer->dwBufferLength; buffer = GetNextBuffer(buffer); @@ -157,7 +154,7 @@ void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) { } // Send the buffers to the audio driver. Note that the device is paused // so we avoid entering the callback method while still here. - for (int ix = 0; ix != kNumBuffers; ++ix) { + for (int ix = 0; ix != num_buffers_; ++ix) { result = ::waveOutWrite(waveout_, buffer, sizeof(WAVEHDR)); if (result != MMSYSERR_NOERROR) { HandleError(result); diff --git a/media/audio/win/waveout_output_win.h b/media/audio/win/waveout_output_win.h index d965d9f..d587c8c 100644 --- a/media/audio/win/waveout_output_win.h +++ b/media/audio/win/waveout_output_win.h @@ -29,7 +29,7 @@ class PCMWaveOutAudioOutputStream : public AudioOutputStream { // the audio manager who is creating this object and |device_id| which // is provided by the operating system. PCMWaveOutAudioOutputStream(AudioManagerWin* manager, - int channels, int sampling_rate, + int channels, int sampling_rate, int num_buffers, char bits_per_sample, UINT device_id); virtual ~PCMWaveOutAudioOutputStream(); @@ -80,6 +80,9 @@ class PCMWaveOutAudioOutputStream : public AudioOutputStream { // We use the callback mostly to periodically request more audio data. AudioSourceCallback* callback_; + // The number of buffers of size |buffer_size_| each to use. + const int num_buffers_; + // The size in bytes of each audio buffer, we usually have two of these. size_t buffer_size_; @@ -87,7 +90,7 @@ class PCMWaveOutAudioOutputStream : public AudioOutputStream { float volume_; // Channels from 0 to 6. - int channels_; + const int channels_; // Number of bytes yet to be played in the hardware buffer. int pending_bytes_; |