diff options
author | cpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-13 23:57:32 +0000 |
---|---|---|
committer | cpu@google.com <cpu@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-01-13 23:57:32 +0000 |
commit | 999ebd77596b7e9ee3c13fef014d3b2d6637d559 (patch) | |
tree | 7de7fe03640a512272955cf9f3274035ae7be144 /media | |
parent | 7cecfb2fa8bd8bb4254f8d83ad0ce5d23961f98c (diff) | |
download | chromium_src-999ebd77596b7e9ee3c13fef014d3b2d6637d559.zip chromium_src-999ebd77596b7e9ee3c13fef014d3b2d6637d559.tar.gz chromium_src-999ebd77596b7e9ee3c13fef014d3b2d6637d559.tar.bz2 |
Low level windows audio support (part 2 of 2)
- Implementation using the WaveXXXX windows API
- Unit tests for the implementation and for the previous CL that
had the sine wave simple source.
This is the harder one :) I tried to add comments to illuminate the thinking.
Review URL: http://codereview.chromium.org/17403
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@7988 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/audio/win/audio_output_win_unittest.cc | 217 | ||||
-rw-r--r-- | media/audio/win/waveout_output_win.cc | 255 | ||||
-rw-r--r-- | media/audio/win/waveout_output_win.h | 106 | ||||
-rw-r--r-- | media/build/media.vcproj | 20 |
4 files changed, 589 insertions, 9 deletions
diff --git a/media/audio/win/audio_output_win_unittest.cc b/media/audio/win/audio_output_win_unittest.cc index 4d3874d..9b88c0c 100644 --- a/media/audio/win/audio_output_win_unittest.cc +++ b/media/audio/win/audio_output_win_unittest.cc @@ -2,28 +2,42 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <windows.h> + +#include "base/basictypes.h" +#include "base/logging.h" #include "media/audio/audio_output.h" +#include "media/audio/simple_sources.h" #include "testing/gtest/include/gtest/gtest.h" -class SimpleSource8bitStereo : public AudioOutputStream::AudioSourceCallback { +// 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 SimpleSource8bitStereo(size_t bytes_to_write) - : byte_count_(bytes_to_write), callback_count_(0) { + 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_; - return byte_count_; + // Touch the first byte to make sure memory is good. + if (max_size) + 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. @@ -31,23 +45,208 @@ class SimpleSource8bitStereo : public AudioOutputStream::AudioSourceCallback { 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: - size_t byte_count_; int callback_count_; + int had_error_; + int was_closed_; +}; + +// 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]; }; -// Validate that the AudioManager::AUDIO_MOCK works. -TEST(WinAudioTest, MockStream) { - AudioManager* audio_man = GetAudioManager(); + + +// ============================================================================ +// 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)); - SimpleSource8bitStereo source(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<const int16*>(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 + +// Test that can it be created and closed. +TEST(WinAudioTest, PCMWaveStreamGetAndClose) { + AudioManager* audio_man = AudioManager::GetAudioManager(); + ASSERT_TRUE(NULL != audio_man); + 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) { + AudioManager* audio_man = AudioManager::GetAudioManager(); + ASSERT_TRUE(NULL != audio_man); + 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) { + AudioManager* audio_man = AudioManager::GetAudioManager(); + ASSERT_TRUE(NULL != audio_man); + 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(); +} + +// 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) { + AudioManager* audio_man = AudioManager::GetAudioManager(); + ASSERT_TRUE(NULL != audio_man); + 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->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. +TEST(WinAudioTest, PCMWaveStreamPlay200HzTone22Kss) { + AudioManager* audio_man = AudioManager::GetAudioManager(); + ASSERT_TRUE(NULL != audio_man); + 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->Start(&source); + ::Sleep(1500); + oas->Stop(); + oas->Close(); +} diff --git a/media/audio/win/waveout_output_win.cc b/media/audio/win/waveout_output_win.cc new file mode 100644 index 0000000..ae0ff54 --- /dev/null +++ b/media/audio/win/waveout_output_win.cc @@ -0,0 +1,255 @@ +// 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 <windows.h> +#include <mmsystem.h> +#pragma comment(lib, "winmm.lib") + +#include "media/audio/win/waveout_output_win.h" + +#include "base/basictypes.h" +#include "base/logging.h" +#include "media/audio/audio_output.h" +#include "media/audio/win/audio_manager_win.h" + +// Some general thoughts about the waveOut API which is badly documented : +// - We use CALLBACK_FUNCTION mode in which XP secretly creates two threads +// named _MixerCallbackThread and _waveThread which have real-time priority. +// The callbacks occur in _waveThread. +// - Windows does not provide a way to query if the device is playing or paused +// thus it forces you to maintain state, which naturally is not exactly +// synchronized to the actual device state. +// - Some functions, like waveOutReset cannot be called in the callback thread +// or called in any random state because they deadlock. This results in a +// non- instantaneous Stop() method. waveOutPrepareHeader seems to be in the +// same boat. +// - waveOutReset() will forcefully kill the _waveThread so it is important +// to make sure we are not executing inside the audio source's OnMoreData() +// or that we take locks inside WaveCallback() or QueueNextPacket(). + +namespace { + +// We settled for a double buffering scheme. It seems to strike a good balance +// between how fast data needs to be provided versus memory usage. +const size_t kNumBuffers = 2; + +// 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) { + return reinterpret_cast<WAVEHDR*>(current->dwUser); +} + +} // namespace + +PCMWaveOutAudioOutputStream::PCMWaveOutAudioOutputStream( + AudioManagerWin* manager, int channels, int sampling_rate, + char bits_per_sample, UINT device_id) + : state_(PCMA_BRAND_NEW), + manager_(manager), + device_id_(device_id), + waveout_(NULL), + callback_(NULL), + buffer_(NULL), + buffer_size_(0) { + format_.wFormatTag = WAVE_FORMAT_PCM; + format_.nChannels = channels; + format_.nSamplesPerSec = sampling_rate; + format_.wBitsPerSample = bits_per_sample; + format_.cbSize = 0; + // The next are computed from above. + format_.nBlockAlign = (format_.nChannels * format_.wBitsPerSample) / 8; + format_.nAvgBytesPerSec = format_.nBlockAlign * format_.nSamplesPerSec; + // The event is auto-reset. + stopped_event_.Set(::CreateEventW(NULL, FALSE, FALSE, NULL)); +} + +PCMWaveOutAudioOutputStream::~PCMWaveOutAudioOutputStream() { + DCHECK(NULL == waveout_); +} + +bool PCMWaveOutAudioOutputStream::Open(size_t buffer_size) { + if (state_ != PCMA_BRAND_NEW) + 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_, + reinterpret_cast<DWORD_PTR>(WaveCallback), + reinterpret_cast<DWORD_PTR>(this), + CALLBACK_FUNCTION); + if (result != MMSYSERR_NOERROR) + return false; + + // If we don't have a packet size we use 100ms. + if (!buffer_size) + buffer_size = format_.nAvgBytesPerSec / 10; + + SetupBuffers(buffer_size); + buffer_size_ = buffer_size; + state_ = PCMA_READY; + return true; +} + +void PCMWaveOutAudioOutputStream::SetupBuffers(size_t rq_size) { + WAVEHDR* last = NULL; + WAVEHDR* first = NULL; + for (int ix = 0; ix != kNumBuffers; ++ix) { + size_t sz = sizeof(WAVEHDR) + rq_size; + buffer_ = reinterpret_cast<WAVEHDR*>(new char[sz]); + buffer_->lpData = reinterpret_cast<char*>(buffer_) + sizeof(WAVEHDR); + buffer_->dwBufferLength = rq_size; + buffer_->dwBytesRecorded = 0; + buffer_->dwUser = reinterpret_cast<DWORD_PTR>(last); + buffer_->dwFlags = WHDR_DONE; + buffer_->dwLoops = 0; + if (ix == 0) + first = buffer_; + last = buffer_; + // Tell windows sound drivers about our buffers. Not documented what + // this does but we can guess that causes the OS to keep a reference to + // the memory pages so the driver can use them without worries. + ::waveOutPrepareHeader(waveout_, buffer_, sizeof(WAVEHDR)); + } + // Fix the first buffer to point to the last one. + first->dwUser = reinterpret_cast<DWORD_PTR>(last); +} + +void PCMWaveOutAudioOutputStream::FreeBuffers() { + WAVEHDR* current = buffer_; + for (int ix = 0; ix != kNumBuffers; ++ix) { + WAVEHDR* next = GetNextBuffer(current); + ::waveOutUnprepareHeader(waveout_, current, sizeof(WAVEHDR)); + delete[] reinterpret_cast<char*>(current); + current = next; + } + buffer_ = NULL; +} + +// Initially we ask the source to fill up both audio buffers. If we don't do +// this then we would always get the driver callback when it is about to run +// samples and that would leave too little time to react. +void PCMWaveOutAudioOutputStream::Start(AudioSourceCallback* callback) { + if (state_ != PCMA_READY) + return; + callback_ = callback; + state_ = PCMA_PLAYING; + WAVEHDR* buffer = buffer_; + for (int ix = 0; ix != kNumBuffers; ++ix) { + QueueNextPacket(buffer); + buffer = GetNextBuffer(buffer); + } +} + +// Stopping is tricky. First, no buffer should be locked by the audio driver +// or else the waveOutReset will deadlock and secondly, the callback should not +// be inside the AudioSource's OnMoreData because waveOutReset() forcefully +// kills the callback thread. +void PCMWaveOutAudioOutputStream::Stop() { + if (state_ != PCMA_PLAYING) + return; + state_ = PCMA_STOPPING; + // Wait for the callback to finish, it will signal us when ready to be reset. + if (WAIT_OBJECT_0 != ::WaitForSingleObject(stopped_event_, INFINITE)) { + HandleError(::GetLastError()); + return; + } + state_ = PCMA_STOPPED; + MMRESULT res = ::waveOutReset(waveout_); + if (res != MMSYSERR_NOERROR) { + state_ = PCMA_PLAYING; + HandleError(res); + } +} + +// We can Close in any state except that trying to close a stream that is +// playing Windows generates an error, which we propagate to the source. +void PCMWaveOutAudioOutputStream::Close() { + if (waveout_) { + // waveOutClose generates a callback with WOM_CLOSE id in the same thread. + MMRESULT res = ::waveOutClose(waveout_); + if (res != MMSYSERR_NOERROR) { + HandleError(res); + return; + } + state_ = PCMA_CLOSED; + waveout_ = NULL; + FreeBuffers(); + } + // Tell the audio manager that we have been released. This can result in + // the manager destroying us in-place so this needs to be the last thing + // we do on this function. + manager_->ReleaseStream(this); +} + +// TODO(cpu): Implement SetVolume and GetVolume. +void PCMWaveOutAudioOutputStream::SetVolume(double left_level, + double right_level) { + if (state_ != PCMA_READY) + return; +} + +void PCMWaveOutAudioOutputStream::GetVolume(double* left_level, + double* right_level) { + if (state_ != PCMA_READY) + return; +} + +void PCMWaveOutAudioOutputStream::HandleError(MMRESULT error) { + DLOG(WARNING) << "PCMWaveOutAudio error " << error; + callback_->OnError(this, 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. + 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 +// when it is done playing a buffer. Since we use double buffering it is +// convenient to think of |buffer| as free and GetNextBuffer(buffer) as in +// use by the driver. +void PCMWaveOutAudioOutputStream::WaveCallback(HWAVEOUT hwo, UINT msg, + DWORD_PTR instance, + DWORD_PTR param1, DWORD_PTR) { + PCMWaveOutAudioOutputStream* obj = + reinterpret_cast<PCMWaveOutAudioOutputStream*>(instance); + + if (msg == WOM_DONE) { + // WOM_DONE indicates that the driver is done with our buffer, we can + // either ask the source for more data or check if we need to stop playing. + WAVEHDR* buffer = reinterpret_cast<WAVEHDR*>(param1); + buffer->dwFlags = WHDR_DONE; + + if (obj->state_ == PCMA_STOPPING) { + // The main thread has called Stop() and is waiting to issue waveOutReset + // which will kill this thread. We should not enter AudioSourceCallback + // code anymore. + ::SetEvent(obj->stopped_event_); + return; + } else if (obj->state_ == PCMA_STOPPED) { + // Not sure if ever hit this but just in case. + return; + } + obj->QueueNextPacket(buffer); + } else if (msg == WOM_CLOSE) { + // We can be closed before calling Start, so it is possible to have a + // null callback at this point. + if (obj->callback_) + obj->callback_->OnClose(obj); + } +} + diff --git a/media/audio/win/waveout_output_win.h b/media/audio/win/waveout_output_win.h new file mode 100644 index 0000000..bdf35b3 --- /dev/null +++ b/media/audio/win/waveout_output_win.h @@ -0,0 +1,106 @@ +// 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. + +#ifndef MEDIA_AUDIO_WAVEOUT_OUTPUT_WIN_H_ +#define MEDIA_AUDIO_WAVEOUT_OUTPUT_WIN_H_ + +#include <windows.h> +#include <mmsystem.h> + +#include "base/basictypes.h" +#include "base/scoped_handle_win.h" +#include "media/audio/audio_output.h" + +class AudioManagerWin; + +// Implements PCM audio output support for Windows using the WaveXXX API. +// While not as nice as the DirectSound-based API, it should work in all target +// operating systems regardless or DirectX version installed. It is known that +// in some machines WaveXXX based audio is better while in others DirectSound +// is better. +// +// Important: the OnXXXX functions in AudioSourceCallback are called by more +// than one thread so it is important to have some form of synchronization if +// you are keeping state in it. +class PCMWaveOutAudioOutputStream : public AudioOutputStream { + public: + // The ctor takes all the usual parameters, plus |manager| which is the + // 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, + char bits_per_sample, UINT device_id); + virtual ~PCMWaveOutAudioOutputStream(); + + // Implementation of AudioOutputStream. + virtual bool Open(size_t packet_size); + virtual void Close(); + virtual void Start(AudioSourceCallback* callback); + virtual void Stop(); + virtual void SetVolume(double left_level, double right_level); + virtual void GetVolume(double* left_level, double* right_level); + + // Sends a buffer to the audio driver for playback. + void QueueNextPacket(WAVEHDR* buffer); + + private: + enum State { + PCMA_BRAND_NEW, // Initial state. + PCMA_READY, // Device obtained and ready to play. + PCMA_PLAYING, // Playing audio. + PCMA_STOPPING, // Trying to stop, waiting for callback to finish. + PCMA_STOPPED, // Stopped. Device was reset. + PCMA_CLOSED // Device has been released. + }; + + // Windows calls us back to feed more data to the device here. See msdn + // documentation for 'waveOutProc' for details about the parameters. + static void CALLBACK WaveCallback(HWAVEOUT hwo, UINT msg, DWORD_PTR instance, + DWORD_PTR param1, DWORD_PTR param2); + + // If windows reports an error this function handles it and passes it to + // the attached AudioSourceCallback::OnError(). + void HandleError(MMRESULT error); + // Allocates and prepares the memory that will be used for playback. Only + // two buffers are created. + void SetupBuffers(size_t rq_size); + // Deallocates the memory allocated in SetupBuffers. + void FreeBuffers(); + + // Reader beware. Visual C has stronger guarantees on volatile vars than + // most people expect. In fact, it has release semantics on write and + // acquire semantics on reads. See the msdn documentation. + volatile State state_; + + // The audio manager that created this output stream. We notify it when + // we close so it can release its own resources. + AudioManagerWin* manager_; + + // We use the callback mostly to periodically request more audio data. + AudioSourceCallback* callback_; + + // The size in bytes of each audio buffer, we usually have two of these. + size_t buffer_size_; + + // 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_; + + // Windows native structure to encode the format parameters. + WAVEFORMATEX format_; + + // Handle to the instance of the wave device. + HWAVEOUT waveout_; + + // Pointer to the first allocated audio buffer. This object owns it. + WAVEHDR* buffer_; + + // An event that is signaled when the callback thread is ready to stop. + ScopedHandle stopped_event_; + + DISALLOW_COPY_AND_ASSIGN(PCMWaveOutAudioOutputStream); +}; + +#endif // MEDIA_AUDIO_WAVEOUT_OUTPUT_WIN_H_ + diff --git a/media/build/media.vcproj b/media/build/media.vcproj index b3fb4f6..23b58cfd 100644 --- a/media/build/media.vcproj +++ b/media/build/media.vcproj @@ -165,6 +165,10 @@ Name="audio" > <File + RelativePath="..\audio\win\audio_manager_win.h" + > + </File> + <File RelativePath="..\audio\audio_output.h" > </File> @@ -172,6 +176,22 @@ RelativePath="..\audio\win\audio_output_win.cc" > </File> + <File + RelativePath="..\audio\simple_sources.h" + > + </File> + <File + RelativePath="..\audio\win\simple_sources_win.cc" + > + </File> + <File + RelativePath="..\audio\win\waveout_output_win.cc" + > + </File> + <File + RelativePath="..\audio\win\waveout_output_win.h" + > + </File> </Filter> </Files> <Globals> |