merge dsound driver code

This commit is contained in:
Glenn Maynard
2002-12-21 07:21:49 +00:00
parent cc9211a510
commit 23b740bba8
6 changed files with 311 additions and 274 deletions
+183 -26
View File
@@ -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 <mmsystem.h>
#include <dsound.h>
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;
}
/*
+48 -7
View File
@@ -1,15 +1,56 @@
#ifndef DSOUND_HELPERS
#define DSOUND_HELPERS 1
#define DIRECTSOUND_VERSION 0x0800
#include <mmsystem.h>
#include <dsound.h>
#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
@@ -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();
}
/*
@@ -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 *> stream_pool;
IDirectSound8 *ds8;
DSound ds;
bool shutdown; /* tells the MixerThread to shut down */
static int MixerThread_start(void *p);
@@ -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
@@ -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<sound *> 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);