From 23b740bba8d9c775d16d67c677718f8dfc1e0919 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Sat, 21 Dec 2002 07:21:49 +0000 Subject: [PATCH] merge dsound driver code --- stepmania/src/arch/Sound/DSoundHelpers.cpp | 209 +++++++++++++++--- stepmania/src/arch/Sound/DSoundHelpers.h | 55 ++++- .../src/arch/Sound/RageSoundDriver_DSound.cpp | 188 +++++----------- .../src/arch/Sound/RageSoundDriver_DSound.h | 21 +- .../Sound/RageSoundDriver_DSound_Software.cpp | 104 ++------- .../Sound/RageSoundDriver_DSound_Software.h | 8 +- 6 files changed, 311 insertions(+), 274 deletions(-) diff --git a/stepmania/src/arch/Sound/DSoundHelpers.cpp b/stepmania/src/arch/Sound/DSoundHelpers.cpp index b2a5284f77..4e03b677ae 100644 --- a/stepmania/src/arch/Sound/DSoundHelpers.cpp +++ b/stepmania/src/arch/Sound/DSoundHelpers.cpp @@ -1,48 +1,205 @@ #include "../../stdafx.h" #include "DSoundHelpers.h" +#include "../../RageUtil.h" +#include "../../RageLog.h" -/* Stuff shared between the two DirectSound drivers. */ -static IDirectSoundBuffer8 *CreateBuf(IDirectSound8 *ds8, WAVEFORMATEX *wavefmt, Uint32 buffersize, bool Hardware) +#define DIRECTSOUND_VERSION 0x0800 +#include +#include + +DSound::DSound() { - /* Try to create the secondary buffer */ - DSBUFFERDESC format; - memset(&format, 0, sizeof(format)); - format.dwSize = sizeof(format); - format.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; - if(Hardware) - format.dwFlags |= DSBCAPS_LOCHARDWARE; - format.dwBufferBytes = buffersize; - format.dwReserved = 0; - format.lpwfxFormat = wavefmt; + HRESULT hr; - IDirectSoundBuffer *sndbuf_buf; - HRESULT hr = ds8->CreateSoundBuffer(&format, &sndbuf_buf, NULL); - if (FAILED(hr)) - throw "CreateSoundBuffer failed"; + if(FAILED(hr=DirectSoundCreate8(NULL, &ds8, NULL))) + throw RageException(hr_ssprintf(hr, "DirectSoundCreate8")); - IDirectSoundBuffer8 *buf; - sndbuf_buf->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*) &buf); - - return buf; + /* Try to set primary mixing privileges */ + hr = ds8->SetCooperativeLevel(GetDesktopWindow(), DSSCL_PRIORITY); } -IDirectSoundBuffer8 *CreateBuf(IDirectSound8 *ds8, - int channels, int samplerate, int bits, - Uint32 buffersize, bool Hardware) +DSound::~DSound() { + ds8->Release(); +} + +bool DSound::IsEmulated() const +{ + /* Don't bother wasting time trying to create buffers if we're + * emulated. This also gives us better diagnostic information. */ + DSCAPS Caps; + Caps.dwSize = sizeof(Caps); + HRESULT hr; + if(FAILED(hr = ds8->GetCaps(&Caps))) + { + LOG->Warn(hr_ssprintf(hr, "ds8->GetCaps failed")); + /* This is strange, so let's be conservative. */ + return true; + } + + return !!(Caps.dwFlags & DSCAPS_EMULDRIVER); +} + +DSoundBuf::DSoundBuf(DSound &ds, DSoundBuf::hw hardware, + int channels_, int samplerate_, int samplebits_, int buffersize_) +{ + channels = channels_; + samplerate = samplerate_; + samplebits = samplebits_; + buffersize = buffersize_; + buffer_locked = false; + last_cursor_pos = write_cursor = 0; + + WAVEFORMATEX waveformat; memset(&waveformat, 0, sizeof(waveformat)); waveformat.cbSize = sizeof(waveformat); waveformat.wFormatTag = WAVE_FORMAT_PCM; - int bytes = bits/8; - waveformat.wBitsPerSample = WORD(bits); + int bytes = samplebits/8; + waveformat.wBitsPerSample = WORD(samplebits); waveformat.nChannels = WORD(channels); waveformat.nSamplesPerSec = DWORD(samplerate); waveformat.nBlockAlign = WORD(bytes*channels); waveformat.nAvgBytesPerSec = samplerate * bytes*channels; - return CreateBuf(ds8, &waveformat, buffersize, Hardware); + /* Try to create the secondary buffer */ + DSBUFFERDESC format; + memset(&format, 0, sizeof(format)); + format.dwSize = sizeof(format); + format.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS; + if(hardware == HW_HARDWARE) + format.dwFlags |= DSBCAPS_LOCHARDWARE; + else if(hardware == HW_SOFTWARE) + format.dwFlags |= DSBCAPS_LOCSOFTWARE; + if(hardware == HW_DONT_CARE) + format.dwFlags |= DSBCAPS_STATIC; + format.dwBufferBytes = buffersize; + format.dwReserved = 0; + format.lpwfxFormat = &waveformat; + + IDirectSoundBuffer *sndbuf_buf; + HRESULT hr = ds.GetDS8()->CreateSoundBuffer(&format, &sndbuf_buf, NULL); + if (FAILED(hr)) + throw "CreateSoundBuffer failed"; + + sndbuf_buf->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*) &buf); + + if(buf == NULL) + throw "foo"; // XXX + +} + +DSoundBuf::~DSoundBuf() +{ + buf->Release(); +} + +bool DSoundBuf::get_output_buf(char **buffer, unsigned *bufsiz, int *play_pos, int chunksize) +{ + ASSERT(!buffer_locked); + + DWORD cursor, junk, write; + + HRESULT result; + + result = buf->GetCurrentPosition(&cursor, &write); + if ( result == DSERR_BUFFERLOST ) { + buf->Restore(); + result = buf->GetCurrentPosition(&cursor, &write); + } + if ( result != DS_OK ) { + LOG->Warn(hr_ssprintf(result, "DirectSound::GetCurrentPosition failed")); + return false; + } + + int num_bytes_empty = cursor - write_cursor; + if(num_bytes_empty <= 0) num_bytes_empty += buffersize; /* unwrap */ + + /* num_bytes_empty is now the actual amount of free buffer space. If it's + * too small, come back later. */ + if(num_bytes_empty < chunksize) + return false; + + /* I don't want to deal with DSound's split-circular-buffer locking stuff, so cap + * the writing space at the end of the physical buffer. */ + num_bytes_empty = min(num_bytes_empty, buffersize - write_cursor); + + /* Don't fill more than one chunk at a time. This reduces the maximum + * amount of time until we give data; that way, if we're short on time, + * we'll give some data soon instead of lots of data later. */ + num_bytes_empty = min(num_bytes_empty, chunksize); + + /* Lock the audio buffer. */ + result = buf->Lock(write_cursor, num_bytes_empty, (LPVOID *)buffer, (DWORD *) bufsiz, NULL, &junk, 0); + if ( result == DSERR_BUFFERLOST ) { + buf->Restore(); + result = buf->Lock(write_cursor, num_bytes_empty, (LPVOID *)buffer, (DWORD *) bufsiz, NULL, &junk, 0); + } + if ( result != DS_OK ) { + LOG->Warn(hr_ssprintf(result, "Couldn't lock the DirectSound buffer.")); + return false; + } + + write_cursor += num_bytes_empty; + if(write_cursor >= buffersize) write_cursor -= buffersize; + + *play_pos = last_cursor_pos; + + /* Increment last_cursor_pos to point at where the data we're about to + * ask for will actually be played. */ + last_cursor_pos += buffersize_frames(); + + buffer_locked = true; + +// LOG->Trace("gave %i", num_bytes_empty); + return true; +} + +void DSoundBuf::release_output_buf(char *buffer, unsigned bufsiz) +{ + buf->Unlock(buffer, bufsiz, NULL, 0); + buffer_locked = false; +} + +int DSoundBuf::GetPosition() const +{ + DWORD cursor, junk; + buf->GetCurrentPosition(&cursor, &junk); + int last_fill = write_cursor; + + int frames_behind = (last_fill - int(cursor)) / samplesize(); + if(frames_behind <= 0) + frames_behind += buffersize_frames(); /* unwrap */ + + int ret = last_cursor_pos - frames_behind; + + /* Failsafe: never return a value smaller than we've already returned. + * This can happen once in a while in underrun conditions. */ + ret = max(LastPosition, ret); + LastPosition = ret; + + return ret; +} + +void DSoundBuf::Play() +{ + HRESULT hr = buf->Play(0, 0, DSBPLAY_LOOPING); +} + +void DSoundBuf::Stop() +{ + buf->Stop(); + buf->SetCurrentPosition(0); + last_cursor_pos = LastPosition = write_cursor = 0; +} + + +void DSoundBuf::Reset() +{ + /* Nothing is playing. Reset the sample count; this is just to + * prevent eventual overflow. */ + last_cursor_pos = LastPosition = 0; } /* diff --git a/stepmania/src/arch/Sound/DSoundHelpers.h b/stepmania/src/arch/Sound/DSoundHelpers.h index 13e41f56d7..d59383d49a 100644 --- a/stepmania/src/arch/Sound/DSoundHelpers.h +++ b/stepmania/src/arch/Sound/DSoundHelpers.h @@ -1,15 +1,56 @@ #ifndef DSOUND_HELPERS #define DSOUND_HELPERS 1 -#define DIRECTSOUND_VERSION 0x0800 -#include -#include #include "SDL.h" -/* Create a DS buffer of the given format. */ -IDirectSoundBuffer8 *CreateBuf(IDirectSound8 *ds8, - int channels, int samplerate, int bits, - Uint32 buffersize, bool Hardware); +struct IDirectSound8; +struct IDirectSoundBuffer8; + +class DSound +{ + IDirectSound8 *ds8; + +public: + IDirectSound8 *GetDS8() const { return ds8; } + bool IsEmulated() const; + + DSound(); + ~DSound(); +}; + +class DSoundBuf +{ + IDirectSoundBuffer8 *buf; + + int channels, samplerate, samplebits, buffersize; + + int buffersize_frames() const { return buffersize / samplesize(); } + int samplesize() const { return samplebits; } + + int write_cursor, last_cursor_pos; + mutable int LastPosition; + + bool buffer_locked; + char *locked_buf; + int locked_len; + +// int GetPos(); +public: + enum hw { HW_HARDWARE, HW_SOFTWARE, HW_DONT_CARE }; + DSoundBuf(DSound &ds, hw hardware, + int channels, int samplerate, int samplebits, int buffersize); + + bool get_output_buf(char **buffer, unsigned *bufsiz, int *play_pos, int chunksize); + void release_output_buf(char *buffer, unsigned bufsiz); + + void Reset(); + void Play(); + void Stop(); + + ~DSoundBuf(); + int GetPosition() const; + int GetMaxPosition() const { return last_cursor_pos; } +}; #endif diff --git a/stepmania/src/arch/Sound/RageSoundDriver_DSound.cpp b/stepmania/src/arch/Sound/RageSoundDriver_DSound.cpp index aed84a4f8b..e8ffacff50 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_DSound.cpp +++ b/stepmania/src/arch/Sound/RageSoundDriver_DSound.cpp @@ -28,6 +28,10 @@ const int samplerate = 44100; const int buffersize_frames = 4096; /* in frames */ const int buffersize = buffersize_frames * samplesize; /* in bytes */ +const int num_chunks = 8; +const int chunksize_frames = buffersize_frames / num_chunks; +const int chunksize = buffersize / num_chunks; + int RageSound_DSound::MixerThread_start(void *p) { ((RageSound_DSound *) p)->MixerThread(); @@ -43,6 +47,9 @@ void RageSound_DSound::MixerThread() * assigns it; we might get here before that happens, though. */ while(!SOUNDMAN && !shutdown) Sleep(10); + if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL)) + LOG->Warn("Failed to set sound thread priority: %i", GetLastError()); /* XXX */ + while(!shutdown) { VDCHECKPOINT; Sleep(10); @@ -54,7 +61,8 @@ void RageSound_DSound::MixerThread() if(stream_pool[i]->state == stream_pool[i]->INACTIVE) continue; /* inactive */ - stream_pool[i]->GetPCM(false); + while(stream_pool[i]->GetPCM(false)) + ; } } } @@ -70,7 +78,8 @@ void RageSound_DSound::Update(float delta) { if(str[i]->state != str[i]->STOPPING) continue; - if(str[i]->flush_bufs) + + if(str[i]->str_ds->GetPosition() < str[i]->flush_pos) continue; /* stopping but still flushing */ /* The sound has stopped and flushed all of its buffers. */ @@ -79,8 +88,6 @@ void RageSound_DSound::Update(float delta) str[i]->snd = NULL; str[i]->str_ds->Stop(); - str[i]->str_ds->SetCurrentPosition(0); - str[i]->LastPosition = -1; str[i]->state = str[i]->INACTIVE; } } @@ -88,133 +95,73 @@ void RageSound_DSound::Update(float delta) /* If init is true, we're filling the buffer while it's stopped, so put * data in the current buffer (where the play cursor is); otherwise put * it in the opposite buffer. */ -void RageSound_DSound::stream::GetPCM(bool init) +bool RageSound_DSound::stream::GetPCM(bool init) { VDCHECKPOINT; - DWORD cursor, junk; - HRESULT result; + char *locked_buf; + unsigned len; + int play_pos; if(init) { - cursor = 0; - last_cursor_pos = 0; + /* We're initializing; fill the entire buffer. The buffer is supposed to + * be empty, so this should never fail. */ + if(!str_ds->get_output_buf(&locked_buf, &len, &play_pos, buffersize)) + ASSERT(0); } else { - result = str_ds->GetCurrentPosition(&cursor, &junk); - if ( result == DSERR_BUFFERLOST ) { - str_ds->Restore(); - result = str_ds->GetCurrentPosition(&cursor, &junk); - } - if ( result != DS_OK ) { - LOG->Warn(hr_ssprintf(result, "DirectSound::GetCurrentPosition failed")); - return; - } - - /* The DSound buffer is equal to two of our buffers. Round cursor - * down to our buffer size. */ - cursor = (cursor / buffersize) * buffersize; - - /* Cursor points to the buffer that's currently playing. We want to - * fill the buffer that *isn't* playing. */ - cursor += buffersize; - cursor %= buffersize*2; - - /* If it hasn't changed, we have nothing to do yet. */ - if(int(cursor) == last_cursor_filled) - return; - - /* Increment last_cursor_pos to point at where the data we're about to - * ask for will actually be played. */ - last_cursor_pos += buffersize_frames; - } - - last_cursor_filled = cursor; - - if(state == STOPPING) - { - if(!flush_bufs) - return; /* sound is finished and will be cleaned up in the main thread */ - - flush_bufs--; - } - - /* Lock the audio buffer. */ - DWORD len; - char *locked_buf = NULL; - result = str_ds->Lock(cursor, buffersize, (LPVOID *)&locked_buf, &len, NULL, &junk, 0); - if ( result == DSERR_BUFFERLOST ) { - str_ds->Restore(); - result = str_ds->Lock(cursor, buffersize, (LPVOID *)&locked_buf, &len, NULL, &junk, 0); - } - if ( result != DS_OK ) { - LOG->Warn(hr_ssprintf(result, "Couldn't lock the DirectSound buffer.")); - return; + /* Just fill one chunk. */ + if(!str_ds->get_output_buf(&locked_buf, &len, &play_pos, chunksize)) + return false; } /* It might be INACTIVE, when we're prebuffering. We just don't want to * fill anything in STOPPING; in that case, we just clear the audio buffer. */ if(state != STOPPING) - CallAudioCallback(locked_buf, len, last_cursor_pos, this); - else + { + unsigned got = snd->GetPCM(locked_buf, len, play_pos); + + if(got < len) { + /* Fill the remainder of the buffer with silence. */ + memset(locked_buf+got, 0, len-got); + + /* STOPPING tells the mixer thread to release the stream once str->flush_bufs + * buffers have been flushed. */ + state = STOPPING; + + /* Flush two buffers worth of data. */ + flush_pos = str_ds->GetMaxPosition(); + } + } else { /* Silence the buffer. */ memset(locked_buf, 0, len); - - str_ds->Unlock(locked_buf, len, NULL, 0); -} - -void RageSound_DSound::stream::CallAudioCallback( - char *buf, unsigned long bytes, - int outTime, stream *str ) -{ - unsigned got = str->snd->GetPCM(buf, bytes, outTime); - - if(got < bytes) { - /* Fill the remainder of the buffer with silence. */ - memset(buf+got, 0, bytes-got); - - /* STOPPING tells the mixer thread to release the stream once str->flush_bufs - * buffers have been flushed. */ - str->state = str->STOPPING; - - /* Flush two buffers worth of data. */ - str->flush_bufs = 2; } + + str_ds->release_output_buf(locked_buf, len); + + return true; } RageSound_DSound::stream::~stream() { + delete str_ds; } RageSound_DSound::RageSound_DSound() { shutdown = false; - /* Fire up DSound. */ - int hr; - if(FAILED(hr=DirectSoundCreate8(NULL, &ds8, NULL))) - throw RageException(hr_ssprintf(hr, "DirectSoundCreate8")); - - /* Try to set primary mixing privileges */ - hr = ds8->SetCooperativeLevel(GetDesktopWindow(), DSSCL_PRIORITY); - { - /* Don't bother wasting time trying to create buffers if we're - * emulated. This also gives us better diagnostic information. */ - DSCAPS Caps; - Caps.dwSize = sizeof(Caps); - HRESULT hr; - if(FAILED(hr = ds8->GetCaps(&Caps))) - LOG->Warn(hr_ssprintf(hr, "ds8->GetCaps failed")); - else if(Caps.dwFlags & DSCAPS_EMULDRIVER) - { - ds8->Release(); - throw "Driver unusable (emulated device)"; - } - } + /* Don't bother wasting time trying to create buffers if we're + * emulated. This also gives us better diagnostic information. */ + if(ds.IsEmulated()) + throw "Driver unusable (emulated device)"; /* Create a bunch of streams and put them into the stream pool. */ for(int i = 0; i < 32; ++i) { - IDirectSoundBuffer8 *newbuf; + DSoundBuf *newbuf; try { - newbuf = CreateBuf(ds8, channels, samplerate, 16, buffersize*2, true); + newbuf = new DSoundBuf(ds, + DSoundBuf::HW_HARDWARE, + channels, samplerate, 16, buffersize); } catch(const char *e) { /* If we didn't get at least 8, fail. */ if(i >= 8) break; /* OK */ @@ -222,7 +169,6 @@ RageSound_DSound::RageSound_DSound() /* Clean up; the dtor won't be called. */ for(int n = 0; n < i; ++n) delete stream_pool[n]; - ds8->Release(); if(i) { @@ -248,8 +194,6 @@ RageSound_DSound::~RageSound_DSound() shutdown = true; SDL_WaitThread(MixerThreadPtr, NULL); - ds8->Release(); - for(unsigned i = 0; i < stream_pool.size(); ++i) delete stream_pool[i]; } @@ -277,12 +221,8 @@ void RageSound_DSound::StartMixing(RageSound *snd) /* Pre-buffer the stream. */ /* There are two buffers of data; fill them both ahead of time so the * sound can start almost immediately. */ - stream_pool[i]->GetPCM(true); /* first call = true */ - stream_pool[i]->GetPCM(false); - HRESULT hr = stream_pool[i]->str_ds->Play(0, 0, DSBPLAY_LOOPING); - if ( hr != DS_OK ) { - return; - } + stream_pool[i]->GetPCM(true); + stream_pool[i]->str_ds->Play(); /* Normally, at this point we should still be INACTIVE, in which case, * tell the mixer thread to start mixing this channel. However, if it's @@ -318,7 +258,7 @@ void RageSound_DSound::StopMixing(RageSound *snd) stream_pool[i]->state = stream_pool[i]->STOPPING; /* Flush two buffers worth of data. */ - stream_pool[i]->flush_bufs = 2; + stream_pool[i]->flush_pos = stream_pool[i]->str_ds->GetMaxPosition(); /* This function is called externally (by RageSound) to stop immediately. * We need to prevent SoundStopped from being called; it should only be @@ -339,27 +279,7 @@ int RageSound_DSound::GetPosition(const RageSound *snd) const ASSERT(i != stream_pool.size()); - DWORD cursor, junk; - stream_pool[i]->str_ds->GetCurrentPosition(&cursor, &junk); - int last_fill = stream_pool[i]->last_cursor_filled; - - if(last_fill == 0) - { - if(int(cursor) < last_fill + buffersize) - cursor += buffersize*2; - last_fill += buffersize*2; - } - - int last_pos = stream_pool[i]->last_cursor_pos; - int ret = (int(cursor) - last_fill)/samplesize + /* bytes -> samples */ - last_pos; - - /* Failsafe: never return a value smaller than we've already returned. - * This can happen once in a while in underrun conditions. */ - ret = max(stream_pool[i]->LastPosition, ret); - stream_pool[i]->LastPosition = ret; - - return ret; + return stream_pool[i]->str_ds->GetPosition(); } /* diff --git a/stepmania/src/arch/Sound/RageSoundDriver_DSound.h b/stepmania/src/arch/Sound/RageSoundDriver_DSound.h index 2ee5428f1d..e5493d38cb 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_DSound.h +++ b/stepmania/src/arch/Sound/RageSoundDriver_DSound.h @@ -3,6 +3,7 @@ #include "RageSoundDriver.h" #include "SDL_Thread.h" +#include "DSoundHelpers.h" struct IDirectSound8; struct IDirectSoundBuffer8; @@ -11,7 +12,7 @@ class RageSound_DSound: public RageSoundDriver { struct stream { /* Actual audio stream: */ - IDirectSoundBuffer8 *str_ds; + DSoundBuf *str_ds; /* Sound object that's playing on this stream, or NULL if this * channel is available: */ @@ -23,29 +24,19 @@ class RageSound_DSound: public RageSoundDriver STOPPING } state; - int flush_bufs; /* state == STOPPING only */ - /* Position in the DS buffer that we filled last; always - * either 0 or halfway through the buffer: */ - int last_cursor_filled; - /* Position, in samples, of the last buffer filled: */ - int last_cursor_pos; + int flush_pos; /* state == STOPPING only */ - /* Last time returned for this stream; used to make sure - * we never go backwards. */ - mutable int LastPosition; + bool GetPCM(bool init); - void GetPCM(bool init); - - stream() { str_ds = NULL; snd = NULL; state=INACTIVE; LastPosition = -1; } + stream() { str_ds = NULL; snd = NULL; state=INACTIVE; } ~stream(); - void CallAudioCallback(char *buf, unsigned long frames, int outTime, stream *str ); }; friend struct stream; /* Pool of available streams. */ vector stream_pool; - IDirectSound8 *ds8; + DSound ds; bool shutdown; /* tells the MixerThread to shut down */ static int MixerThread_start(void *p); diff --git a/stepmania/src/arch/Sound/RageSoundDriver_DSound_Software.cpp b/stepmania/src/arch/Sound/RageSoundDriver_DSound_Software.cpp index 8dde98b7c9..ea675f3756 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_DSound_Software.cpp +++ b/stepmania/src/arch/Sound/RageSoundDriver_DSound_Software.cpp @@ -23,7 +23,7 @@ const int channels = 2; const int samplesize = channels*2; /* 16-bit */ const int samplerate = 44100; -const int buffersize_frames = 2048*2; /* in frames */ +const int buffersize_frames = 1024*4; /* in frames */ const int buffersize = buffersize_frames * samplesize; /* in bytes */ /* We'll fill the buffer in chunks this big. This should evenly divide the @@ -46,6 +46,7 @@ void RageSound_DSound_Software::MixerThread() /* SOUNDMAN will be set once RageSoundManager's ctor returns and * assigns it; we might get here before that happens, though. */ while(!SOUNDMAN && !shutdown) Sleep(10); + if(!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL)) LOG->Warn("Failed to set sound thread priority: %i", GetLastError()); /* XXX */ @@ -61,49 +62,12 @@ bool RageSound_DSound_Software::GetPCM() { LockMut(SOUNDMAN->lock); - DWORD cursor, junk, write; + char *locked_buf; + unsigned len; + int play_pos; - HRESULT result; - - result = str_ds->GetCurrentPosition(&cursor, &write); - if ( result == DSERR_BUFFERLOST ) { - str_ds->Restore(); - result = str_ds->GetCurrentPosition(&cursor, &write); - } - if ( result != DS_OK ) { - LOG->Warn(hr_ssprintf(result, "DirectSound::GetCurrentPosition failed")); + if(!str_ds->get_output_buf(&locked_buf, &len, &play_pos, chunksize)) return false; - } - - int num_bytes_empty = cursor - write_cursor; - if(num_bytes_empty < 0) num_bytes_empty += buffersize; /* unwrap */ - - /* num_bytes_empty is now the actual amount of free buffer space. If it's - * too small, come back later. */ - if(num_bytes_empty < chunksize) - return false; - - /* I don't want to deal with DSound's split-circular-buffer locking stuff, so cap - * the writing space at the end of the physical buffer. */ - num_bytes_empty = min(num_bytes_empty, buffersize - write_cursor); - - /* Don't fill more than one chunk at a time. This reduces the maximum - * amount of time until we give data; that way, if we're short on time, - * we'll give some data soon instead of lots of data later. */ - num_bytes_empty = min(num_bytes_empty, chunksize); - - /* Lock the audio buffer. */ - DWORD len; - char *locked_buf = NULL; - result = str_ds->Lock(write_cursor, num_bytes_empty, (LPVOID *)&locked_buf, &len, NULL, &junk, 0); - if ( result == DSERR_BUFFERLOST ) { - str_ds->Restore(); - result = str_ds->Lock(write_cursor, num_bytes_empty, (LPVOID *)&locked_buf, &len, NULL, &junk, 0); - } - if ( result != DS_OK ) { - LOG->Warn(hr_ssprintf(result, "Couldn't lock the DirectSound buffer.")); - return false; - } /* Silence the buffer. */ memset(locked_buf, 0, len); @@ -128,7 +92,7 @@ bool RageSound_DSound_Software::GetPCM() sounds[i]->flush_bufs--; } else { /* Call the callback. */ - unsigned got = sounds[i]->snd->GetPCM((char *) buf, len, last_cursor_pos); + unsigned got = sounds[i]->snd->GetPCM((char *) buf, len, play_pos); SOUNDMAN->MixAudio( (Uint8 *) locked_buf, (Uint8 *) buf, got, SDL_MIX_MAXVOLUME/2); @@ -142,14 +106,7 @@ bool RageSound_DSound_Software::GetPCM() } } - str_ds->Unlock(locked_buf, len, NULL, 0); - - write_cursor += num_bytes_empty; - if(write_cursor >= buffersize) write_cursor -= buffersize; - - /* Increment last_cursor_pos to point at where the data we're about to - * ask for will actually be played. */ - last_cursor_pos += buffersize_frames; + str_ds->release_output_buf(locked_buf, len); return true; } @@ -199,58 +156,32 @@ void RageSound_DSound_Software::StopMixing(RageSound *snd) delete sounds[i]; sounds.erase(sounds.begin()+i, sounds.begin()+i+1); + /* If nothing is playing, reset the sample count; this is just to + * prevent eventual overflow. */ if(sounds.empty()) - { - /* Nothing is playing. Reset the sample count; this is just to - * prevent eventual overflow. */ - last_cursor_pos = LastPosition = 0; - } + str_ds->Reset(); } int RageSound_DSound_Software::GetPosition(const RageSound *snd) const { LockMut(SOUNDMAN->lock); - - DWORD cursor, junk; - str_ds->GetCurrentPosition(&cursor, &junk); - int last_fill = write_cursor; - - int frames_behind = (last_fill - int(cursor)) / samplesize; - if(frames_behind < 0) - frames_behind += buffersize_frames; - - int ret = last_cursor_pos - frames_behind; - - /* Failsafe: never return a value smaller than we've already returned. - * This can happen once in a while in underrun conditions. */ - ret = max(LastPosition, ret); - LastPosition = ret; - - return ret; + return str_ds->GetPosition(); } RageSound_DSound_Software::RageSound_DSound_Software() { shutdown = false; - last_cursor_pos = write_cursor = 0; - LastPosition = -1; /* XXX make another exception type that doesn't trigger debug stuff * and use that */ - /* Fire up DSound. */ - int hr; - if(FAILED(hr=DirectSoundCreate8(NULL, &ds8, NULL))) - throw RageException(hr_ssprintf(hr, "DirectSoundCreate8")); - - /* Try to set primary mixing privileges */ - hr = ds8->SetCooperativeLevel(GetDesktopWindow(), DSSCL_PRIORITY); /* Create a DirectSound stream, but don't force it into hardware. */ - str_ds = CreateBuf(ds8, channels, samplerate, 16, buffersize, false); - + str_ds = new DSoundBuf(ds, + DSoundBuf::HW_DONT_CARE, + channels, samplerate, 16, buffersize); MixerThreadPtr = SDL_CreateThread(MixerThread_start, this); - hr = str_ds->Play(0, 0, DSBPLAY_LOOPING); + str_ds->Play(); } RageSound_DSound_Software::~RageSound_DSound_Software() @@ -261,8 +192,7 @@ RageSound_DSound_Software::~RageSound_DSound_Software() SDL_WaitThread(MixerThreadPtr, NULL); LOG->Trace("Mixer thread shut down."); - str_ds->Release(); - ds8->Release(); + delete str_ds; } float RageSound_DSound_Software::GetPlayLatency() const diff --git a/stepmania/src/arch/Sound/RageSoundDriver_DSound_Software.h b/stepmania/src/arch/Sound/RageSoundDriver_DSound_Software.h index c4ef385451..5e4f05f456 100644 --- a/stepmania/src/arch/Sound/RageSoundDriver_DSound_Software.h +++ b/stepmania/src/arch/Sound/RageSoundDriver_DSound_Software.h @@ -3,6 +3,7 @@ #include "RageSoundDriver.h" #include "SDL_Thread.h" +#include "DSoundHelpers.h" struct IDirectSound8; struct IDirectSoundBuffer8; @@ -22,13 +23,10 @@ class RageSound_DSound_Software: public RageSoundDriver /* List of currently playing sounds: */ vector sounds; - mutable int LastPosition; - bool shutdown; - int write_cursor, last_cursor_pos; - IDirectSound8 *ds8; - IDirectSoundBuffer8 *str_ds; + DSound ds; + DSoundBuf *str_ds; bool GetPCM(); void Update(float delta);