diff --git a/aegisub/Makefile.am b/aegisub/Makefile.am index 461620cdc..64f350ea9 100644 --- a/aegisub/Makefile.am +++ b/aegisub/Makefile.am @@ -147,6 +147,7 @@ EXTRA_aegisub_SOURCES = \ font_file_lister.cpp \ $(FONT_LISTER) \ audio_player_dsound.cpp \ + audio_player_dsound2.cpp \ audio_player_portaudio.cpp \ audio_player_pulse.cpp \ audio_provider_avs.cpp \ diff --git a/aegisub/audio_player.cpp b/aegisub/audio_player.cpp index 32b4ba587..82eb53396 100644 --- a/aegisub/audio_player.cpp +++ b/aegisub/audio_player.cpp @@ -44,6 +44,7 @@ #endif #ifdef WITH_DIRECTSOUND #include "audio_player_dsound.h" +#include "audio_player_dsound2.h" #endif #ifdef WITH_OPENAL #include "audio_player_openal.h" @@ -157,7 +158,8 @@ void AudioPlayerFactoryManager::RegisterProviders() { RegisterFactory(new AlsaPlayerFactory(),_T("ALSA")); #endif #ifdef WITH_DIRECTSOUND - RegisterFactory(new DirectSoundPlayerFactory(),_T("DirectSound")); + RegisterFactory(new DirectSoundPlayerFactory(),_T("DirectSound-old")); + RegisterFactory(new DirectSoundPlayer2Factory(),_T("DirectSound")); #endif #ifdef WITH_OPENAL RegisterFactory(new OpenALPlayerFactory(),_T("OpenAL")); diff --git a/aegisub/audio_player_dsound2.cpp b/aegisub/audio_player_dsound2.cpp new file mode 100644 index 000000000..45fd84245 --- /dev/null +++ b/aegisub/audio_player_dsound2.cpp @@ -0,0 +1,723 @@ +// Copyright (c) 2008, Niels Martin Hansen +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Aegisub Group nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// ----------------------------------------------------------------------------- +// +// AEGISUB +// +// Website: http://www.aegisub.net/ +// Contact: mailto:jiifurusu@gmail.com +// + + +/////////// +// Headers +#ifdef WITH_DIRECTSOUND + +#include +#include +#include +#include +#include "include/aegisub/audio_provider.h" +#include "utils.h" +#include "main.h" +#include "frame_main.h" +#include "audio_player_dsound2.h" + + +struct COMInitialization { + bool inited; + + COMInitialization() + { + inited = false; + } + + ~COMInitialization() + { + if (inited) CoUninitialize(); + } + + void Init() + { + if (!inited) + { + if (FAILED(CoInitialize(NULL))) + throw std::exception(); + inited = true; + } + } +}; + + +template +struct COMObjectRetainer { + T *obj; + + COMObjectRetainer() + { + obj = 0; + } + + COMObjectRetainer(T *_obj) + { + obj = _obj; + } + + ~COMObjectRetainer() + { + if (obj) obj->Release(); + } + + T * operator -> () + { + return obj; + } +}; + + +class DirectSoundPlayer2Thread { + static unsigned int __stdcall ThreadProc(void *parameter); + void Run(); + + DWORD FillAndUnlockBuffers(void *buf1, DWORD buf1sz, void *buf2, DWORD buf2sz, int64_t &input_frame, IDirectSoundBuffer8 *bfr); + + void CheckError(); + + HANDLE thread_handle; + + // Used to signal state-changes to thread + HANDLE + event_start_playback, + event_stop_playback, + event_update_end_time, + event_set_volume, + event_kill_self; + + // Thread communicating back + HANDLE + thread_running, + is_playing, + error_happened; + + wxChar *error_message; + double volume; + int64_t start_frame; + int64_t end_frame; + + DWORD last_playback_restart; + + AudioProvider *provider; + +public: + DirectSoundPlayer2Thread(AudioProvider *provider); + ~DirectSoundPlayer2Thread(); + + void Play(int64_t start, int64_t count); + void Stop(); + void SetEndFrame(int64_t new_end_frame); + void SetVolume(double new_volume); + + bool IsPlaying(); + int64_t GetStartFrame(); + int64_t GetCurrentFrame(); + int64_t GetEndFrame(); + double GetVolume(); +}; + + +unsigned int __stdcall DirectSoundPlayer2Thread::ThreadProc(void *parameter) +{ + static_cast(parameter)->Run(); + return 0; +} + + +#define WANTED_LATENCY 100 +#define BUFFER_LENGTH 5 +// The buffer will hold BUFFER_LENGTH times WANTED_LATENCY milliseconds of audio + + +void DirectSoundPlayer2Thread::Run() +{ +#define REPORT_ERROR(msg) { error_message = _T("DirectSoundPlayer2Thread: ") _T(msg); SetEvent(error_happened); return; } + + COMInitialization COM_library; + try { COM_library.Init(); } + catch (std::exception e) + REPORT_ERROR("Could not initialise COM") + + + // Create DirectSound object + COMObjectRetainer ds; + if (FAILED(DirectSoundCreate8(&DSDEVID_DefaultPlayback, &ds.obj, NULL))) + REPORT_ERROR("Cound not create DirectSound object") + + + // Ensure we can get interesting wave formats (unless we have PRIORITY we can only use a standard 8 bit format) + ds->SetCooperativeLevel((HWND)static_cast(wxApp::GetInstance())->frame->GetHandle(), DSSCL_PRIORITY); + + // Describe the wave format + WAVEFORMATEX waveFormat; + waveFormat.wFormatTag = WAVE_FORMAT_PCM; + waveFormat.nSamplesPerSec = provider->GetSampleRate(); + waveFormat.nChannels = provider->GetChannels(); + waveFormat.wBitsPerSample = provider->GetBytesPerSample() * 8; + waveFormat.nBlockAlign = waveFormat.nChannels * waveFormat.wBitsPerSample / 8; + waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign; + waveFormat.cbSize = sizeof(waveFormat); + + // And the buffer itself + int aim = waveFormat.nAvgBytesPerSec * (WANTED_LATENCY*BUFFER_LENGTH)/1000; + int min = DSBSIZE_MIN; + int max = DSBSIZE_MAX; + DWORD bufSize = MIN(MAX(min,aim),max); // size of entier playback buffer + DSBUFFERDESC desc; + desc.dwSize = sizeof(DSBUFFERDESC); + desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; + desc.dwBufferBytes = bufSize; + desc.dwReserved = 0; + desc.lpwfxFormat = &waveFormat; + desc.guid3DAlgorithm = GUID_NULL; + + // And then create the buffer + IDirectSoundBuffer *bfr7 = 0; + if FAILED(ds->CreateSoundBuffer(&desc, &bfr7, 0)) + REPORT_ERROR("Could not create buffer") + + // But it's an old version interface we get, query it for the DSound8 interface + COMObjectRetainer bfr; + if (FAILED(bfr7->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*)&bfr.obj))) + REPORT_ERROR("Buffer doesn't support version 8 interface") + bfr7->Release(); + bfr7 = 0; + + //wxLogDebug(_T("DirectSoundPlayer2: Created buffer of %d bytes, supposed to be %d milliseconds or %d frames"), bufSize, WANTED_LATENCY*BUFFER_LENGTH, bufSize/provider->GetBytesPerSample()); + + + // Now we're ready to roll! + SetEvent(thread_running); + bool running = true; + + HANDLE events_to_wait[] = { + event_start_playback, + event_stop_playback, + event_update_end_time, + event_set_volume, + event_kill_self + }; + + int64_t next_input_frame = 0; + DWORD buffer_offset = 0; + bool playback_should_be_running = false; + + while (running) + { + DWORD wait_result = WaitForMultipleObjects(sizeof(events_to_wait)/sizeof(HANDLE), events_to_wait, FALSE, WANTED_LATENCY); + + switch (wait_result) + { + case WAIT_OBJECT_0+0: + { + // Start or restart playback + bfr->Stop(); + ResetEvent(is_playing); + + next_input_frame = start_frame; + + DWORD buf_size; // size of buffer locked for filling + void *buf; + buffer_offset = 0; + + HRESULT res = bfr->Lock(buffer_offset, 0, &buf, &buf_size, 0, 0, DSBLOCK_ENTIREBUFFER); + while (FAILED(res)) // yes, while, so I can break out of it without a goto! + { + if (res == DSERR_BUFFERLOST) + { + // Try to regain the buffer + if (SUCCEEDED(bfr->Restore()) && + SUCCEEDED(bfr->Lock(buffer_offset, 0, &buf, &buf_size, 0, 0, DSBLOCK_ENTIREBUFFER))) + { + //wxLogDebug(_T("DirectSoundPlayer2: Lost and restored buffer")); + break; + } + + REPORT_ERROR("Lost buffer and could not restore it.") + } + + REPORT_ERROR("Could not lock buffer for playback.") + } + + buffer_offset += FillAndUnlockBuffers(buf, buf_size, 0, 0, next_input_frame, bfr.obj); + if (buffer_offset >= bufSize) buffer_offset -= bufSize; + + if (FAILED(bfr->SetCurrentPosition(0))) + REPORT_ERROR("Could not reset playback buffer cursor before playback.") + + if (FAILED(bfr->Play(0, 0, DSBPLAY_LOOPING))) + REPORT_ERROR("Could not start looping playback.") + + SetEvent(is_playing); + playback_should_be_running = true; + + break; + } + + case WAIT_OBJECT_0+1: + { + // Stop playing + bfr->Stop(); + ResetEvent(is_playing); + playback_should_be_running = false; + break; + } + + case WAIT_OBJECT_0+2: + { + // Set end frame + if (end_frame <= next_input_frame) + { + bfr->Stop(); + ResetEvent(is_playing); + playback_should_be_running = false; + } + break; + } + + case WAIT_OBJECT_0+3: + { + // Change volume + // We aren't thread safe right now, filling the buffers grabs volume directly + // from the field set by the controlling thread, but it shouldn't be a major + // problem if race conditions do occur, just some momentary distortion. + break; + } + + case WAIT_OBJECT_0+4: + { + // Perform suicide + bfr->Stop(); + ResetEvent(is_playing); + playback_should_be_running = false; + running = false; + break; + } + + case WAIT_TIMEOUT: + { + // Time to fill more into buffer + + if (!playback_should_be_running) + break; + + DWORD status; + if (FAILED(bfr->GetStatus(&status))) + REPORT_ERROR("Could not get playback buffer status") + + if (!(status & DSBSTATUS_LOOPING)) + { + // Not really what we expected... + bfr->Stop(); + ResetEvent(is_playing); + playback_should_be_running = false; + break; + } + + DWORD play_cursor; + if (FAILED(bfr->GetCurrentPosition(&play_cursor, 0))) + REPORT_ERROR("Could not get play cursor position for filling buffer.") + + int bytes_needed = (int)play_cursor - (int)buffer_offset; + if (bytes_needed < 0) bytes_needed += (int)bufSize; + + DWORD buf1sz, buf2sz; + void *buf1, *buf2; + + HRESULT res = bfr->Lock(buffer_offset, bytes_needed, &buf1, &buf1sz, &buf2, &buf2sz, 0); + while (FAILED(res)) // yes, while, so I can break out of it without a goto! + { + if (res == DSERR_BUFFERLOST) + { + // Try to regain the buffer + // When the buffer was lost the entire contents was lost too, so we have to start over + if (SUCCEEDED(bfr->Restore()) && + SUCCEEDED(bfr->Lock(0, bufSize, &buf1, &buf1sz, &buf2, &buf2sz, 0)) && + SUCCEEDED(bfr->Play(0, 0, DSBPLAY_LOOPING))) + { + wxLogDebug(_T("DirectSoundPlayer2: Lost and restored buffer")); + break; + } + + REPORT_ERROR("Lost buffer and could not restore it.") + } + + REPORT_ERROR("Could not lock buffer for filling.") + } + + buffer_offset += FillAndUnlockBuffers(buf1, buf1sz, buf2, buf2sz, next_input_frame, bfr.obj); + if (buffer_offset >= bufSize) buffer_offset -= bufSize; + + break; + } + + default: + REPORT_ERROR("Something bad happened while waiting on events in playback loop, either the wait failed or an event object was abandoned.") + break; + } + } + +#undef REPORT_ERROR +} + + +DWORD DirectSoundPlayer2Thread::FillAndUnlockBuffers(void *buf1, DWORD buf1sz, void *buf2, DWORD buf2sz, int64_t &input_frame, IDirectSoundBuffer8 *bfr) +{ + // Assume buffers have been locked and are ready to be filled + + DWORD bytes_per_frame = provider->GetChannels() * provider->GetBytesPerSample(); + DWORD buf1szf = buf1sz / bytes_per_frame; + DWORD buf2szf = buf2sz / bytes_per_frame; + + if (input_frame >= end_frame) + { + // Silence + + if (buf1) + memset(buf1, 0, buf1sz); + + if (buf2) + memset(buf2, 0, buf2sz); + + input_frame += buf1szf + buf2szf; + + bfr->Unlock(buf1, buf1sz, buf2, buf2sz); // should be checking for success + + return buf1sz + buf2sz; + } + + if (buf1 && buf1sz) + { + if (buf1szf + input_frame > end_frame) + { + buf1szf = end_frame - input_frame; + buf1sz = buf1szf * bytes_per_frame; + buf2szf = 0; + buf2sz = 0; + } + + provider->GetAudioWithVolume(buf1, input_frame, buf1szf, volume); + + input_frame += buf1szf; + } + + if (buf2 && buf2sz) + { + if (buf2szf + input_frame > end_frame) + { + buf2szf = end_frame - input_frame; + buf2sz = buf2szf * bytes_per_frame; + } + + provider->GetAudioWithVolume(buf2, input_frame, buf2szf, volume); + + input_frame += buf2szf; + } + + bfr->Unlock(buf1, buf1sz, buf2, buf2sz); // bad? should check for success + + return buf1sz + buf2sz; +} + + +void DirectSoundPlayer2Thread::CheckError() +{ + switch (WaitForSingleObject(error_happened, 0)) + { + case WAIT_OBJECT_0: + throw error_message; + + case WAIT_ABANDONED: + throw _T("The DirectShowPlayer2Thread error signal event was abandoned, somehow. This should not happen."); + + case WAIT_FAILED: + throw _T("Failed checking state of DirectShowPlayer2Thread error signal event."); + + case WAIT_TIMEOUT: + default: + return; + } +} + + +DirectSoundPlayer2Thread::DirectSoundPlayer2Thread(AudioProvider *provider) +{ + event_start_playback = CreateEvent(0, FALSE, FALSE, 0); + event_stop_playback = CreateEvent(0, FALSE, FALSE, 0); + event_update_end_time = CreateEvent(0, FALSE, FALSE, 0); + event_set_volume = CreateEvent(0, FALSE, FALSE, 0); + event_kill_self = CreateEvent(0, FALSE, FALSE, 0); + + thread_running = CreateEvent(0, TRUE, FALSE, 0); + is_playing = CreateEvent(0, TRUE, FALSE, 0); + error_happened = CreateEvent(0, TRUE, FALSE, 0); + + error_message = 0; + volume = 1.0; + start_frame = 0; + end_frame = 0; + + this->provider = provider; + + thread_handle = (HANDLE)_beginthreadex(0, 0, ThreadProc, this, 0, 0); + + if (!thread_handle) + throw _T("Failed creating playback thread in DirectSoundPlayer2. This is bad."); + + CheckError(); + + WaitForSingleObject(thread_running, INFINITE); +} + + +DirectSoundPlayer2Thread::~DirectSoundPlayer2Thread() +{ + SetEvent(event_kill_self); + WaitForSingleObject(thread_handle, INFINITE); +} + + +void DirectSoundPlayer2Thread::Play(int64_t start, int64_t count) +{ + CheckError(); + + start_frame = start; + end_frame = start+count; + SetEvent(event_start_playback); + + last_playback_restart = GetTickCount(); +} + + +void DirectSoundPlayer2Thread::Stop() +{ + CheckError(); + + SetEvent(event_stop_playback); +} + + +void DirectSoundPlayer2Thread::SetEndFrame(int64_t new_end_frame) +{ + CheckError(); + + end_frame = new_end_frame; + SetEvent(event_update_end_time); +} + + +void DirectSoundPlayer2Thread::SetVolume(double new_volume) +{ + CheckError(); + + volume = new_volume; + SetEvent(event_set_volume); +} + + +bool DirectSoundPlayer2Thread::IsPlaying() +{ + CheckError(); + + switch (WaitForSingleObject(is_playing, 0)) + { + case WAIT_ABANDONED: + throw _T("The DirectShowPlayer2Thread playback state event was abandoned, somehow. This should not happen."); + + case WAIT_FAILED: + throw _T("Failed checking state of DirectShowPlayer2Thread playback state event."); + + case WAIT_OBJECT_0: + return true; + + case WAIT_TIMEOUT: + default: + return false; + } +} + + +int64_t DirectSoundPlayer2Thread::GetStartFrame() +{ + CheckError(); + + return start_frame; +} + + +int64_t DirectSoundPlayer2Thread::GetCurrentFrame() +{ + CheckError(); + + if (!IsPlaying()) return 0; + + DWORD milliseconds_elapsed = GetTickCount() - last_playback_restart; + + return start_frame + milliseconds_elapsed * provider->GetSampleRate() / 1000; +} + + +int64_t DirectSoundPlayer2Thread::GetEndFrame() +{ + CheckError(); + + return end_frame; +} + + +double DirectSoundPlayer2Thread::GetVolume() +{ + CheckError(); + + return volume; +} + + + + +DirectSoundPlayer2::DirectSoundPlayer2() +{ + thread = 0; +} + + +DirectSoundPlayer2::~DirectSoundPlayer2() +{ + CloseStream(); +} + + +void DirectSoundPlayer2::OpenStream() +{ + if (thread) return; + thread = new DirectSoundPlayer2Thread(GetProvider()); +} + + +void DirectSoundPlayer2::CloseStream() +{ + if (!thread) return; + + delete thread; + thread = 0; +} + + +void DirectSoundPlayer2::SetProvider(AudioProvider *provider) +{ + if (thread && provider != GetProvider()) + { + delete thread; + thread = new DirectSoundPlayer2Thread(provider); + } + + AudioPlayer::SetProvider(provider); +} + + +void DirectSoundPlayer2::Play(int64_t start,int64_t count) +{ + OpenStream(); + thread->Play(start, count); + + if (displayTimer && !displayTimer->IsRunning()) displayTimer->Start(15); +} + + +void DirectSoundPlayer2::Stop(bool timerToo) +{ + if (thread) thread->Stop(); + + if (timerToo && displayTimer) { + displayTimer->Stop(); + } +} + + +bool DirectSoundPlayer2::IsPlaying() +{ + if (!thread) return false; + return thread->IsPlaying(); +} + + +int64_t DirectSoundPlayer2::GetStartPosition() +{ + if (!thread) return 0; + return thread->GetStartFrame(); +} + + +int64_t DirectSoundPlayer2::GetEndPosition() +{ + if (!thread) return 0; + return thread->GetEndFrame(); +} + + +int64_t DirectSoundPlayer2::GetCurrentPosition() +{ + if (!thread) return 0; + return thread->GetCurrentFrame(); +} + + +void DirectSoundPlayer2::SetEndPosition(int64_t pos) +{ + if (thread) thread->SetEndFrame(pos); +} + + +void DirectSoundPlayer2::SetCurrentPosition(int64_t pos) +{ + if (thread) thread->Play(pos, thread->GetEndFrame()-pos); +} + + +void DirectSoundPlayer2::SetVolume(double vol) +{ + if (thread) thread->SetVolume(vol); +} + + +double DirectSoundPlayer2::GetVolume() +{ + if (!thread) return 0; + return thread->GetVolume(); +} + + +#endif // WITH_DIRECTSOUND diff --git a/aegisub/audio_player_dsound2.h b/aegisub/audio_player_dsound2.h new file mode 100644 index 000000000..d098af8b9 --- /dev/null +++ b/aegisub/audio_player_dsound2.h @@ -0,0 +1,77 @@ +// Copyright (c) 2008, Niels Martin Hansen +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of the Aegisub Group nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. +// +// ----------------------------------------------------------------------------- +// +// AEGISUB +// +// Website: http://www.aegisub.net/ +// Contact: mailto:jiifurusu@gmail.com +// + + +#ifdef WITH_DIRECTSOUND + +#include +#include "include/aegisub/audio_player.h" + + +class DirectSoundPlayer2Thread; + +class DirectSoundPlayer2 : public AudioPlayer { + DirectSoundPlayer2Thread *thread; + +public: + DirectSoundPlayer2(); + ~DirectSoundPlayer2(); + + void OpenStream(); + void CloseStream(); + + void SetProvider(AudioProvider *provider); + + void Play(int64_t start,int64_t count); + void Stop(bool timerToo=true); + bool IsPlaying(); + + int64_t GetStartPosition(); + int64_t GetEndPosition(); + int64_t GetCurrentPosition(); + void SetEndPosition(int64_t pos); + void SetCurrentPosition(int64_t pos); + + void SetVolume(double vol); + double GetVolume(); +}; + + +class DirectSoundPlayer2Factory : public AudioPlayerFactory { +public: + AudioPlayer *CreatePlayer() { return new DirectSoundPlayer2(); } +}; + +#endif diff --git a/aegisub/include/aegisub/audio_player.h b/aegisub/include/aegisub/audio_player.h index 96c606924..afffe9786 100644 --- a/aegisub/include/aegisub/audio_player.h +++ b/aegisub/include/aegisub/audio_player.h @@ -84,7 +84,7 @@ public: virtual wxMutex *GetMutex(); - void SetProvider(AudioProvider *provider); + virtual void SetProvider(AudioProvider *provider); AudioProvider *GetProvider(); void SetDisplayTimer(wxTimer *timer); diff --git a/build/aegisub_vs2005/aegisub_vs2005.vcproj b/build/aegisub_vs2005/aegisub_vs2005.vcproj index e0fa88412..435547655 100644 --- a/build/aegisub_vs2005/aegisub_vs2005.vcproj +++ b/build/aegisub_vs2005/aegisub_vs2005.vcproj @@ -598,6 +598,14 @@ RelativePath="..\..\aegisub\audio_player_dsound.h" > + + + + diff --git a/build/aegisub_vs2008/aegisub_vs2008.vcproj b/build/aegisub_vs2008/aegisub_vs2008.vcproj index 08ee35570..fc1ef1290 100644 --- a/build/aegisub_vs2008/aegisub_vs2008.vcproj +++ b/build/aegisub_vs2008/aegisub_vs2008.vcproj @@ -510,6 +510,14 @@ RelativePath="..\..\aegisub\audio_player_dsound.h" > + + + +