Fixed #320, other applications no longer interfere with audio playback. Apparently those notifications for DSound buffers aren't reliable at all.
Still one minor problem with audio display cursor making a strange jump about one second from end of selection, this doesn't seem to affect playback though. Originally committed to SVN as r1099.
This commit is contained in:
parent
813a487b23
commit
4276b02f89
2 changed files with 56 additions and 62 deletions
|
@ -105,7 +105,7 @@ void DirectSoundPlayer::OpenStream() {
|
||||||
bufSize = MIN(MAX(min,aim),max);
|
bufSize = MIN(MAX(min,aim),max);
|
||||||
DSBUFFERDESC desc;
|
DSBUFFERDESC desc;
|
||||||
desc.dwSize = sizeof(DSBUFFERDESC);
|
desc.dwSize = sizeof(DSBUFFERDESC);
|
||||||
desc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY;
|
desc.dwFlags = DSBCAPS_GETCURRENTPOSITION2 | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY;
|
||||||
desc.dwBufferBytes = bufSize;
|
desc.dwBufferBytes = bufSize;
|
||||||
desc.dwReserved = 0;
|
desc.dwReserved = 0;
|
||||||
desc.lpwfxFormat = &waveFormat;
|
desc.lpwfxFormat = &waveFormat;
|
||||||
|
@ -147,49 +147,67 @@ void DirectSoundPlayer::CloseStream() {
|
||||||
|
|
||||||
///////////////
|
///////////////
|
||||||
// Fill buffer
|
// Fill buffer
|
||||||
bool DirectSoundPlayer::FillBuffer() {
|
bool DirectSoundPlayer::FillBuffer(bool fill) {
|
||||||
if (playPos >= endPos) return false;
|
if (playPos >= endPos) return false;
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
|
HRESULT res;
|
||||||
void *ptr1, *ptr2;
|
void *ptr1, *ptr2;
|
||||||
unsigned long int size1, size2;
|
unsigned long int size1, size2;
|
||||||
AudioProvider *provider = GetProvider();
|
AudioProvider *provider = GetProvider();
|
||||||
int bytesps = provider->GetBytesPerSample();
|
int bytesps = provider->GetBytesPerSample();
|
||||||
|
|
||||||
// To write length
|
// To write length
|
||||||
//int max = bufSize/4;
|
int toWrite = 0;
|
||||||
//if (fill) max = bufSize;
|
if (fill) {
|
||||||
//int toWrite = MIN(max,(endPos-playPos)*bytesps);
|
toWrite = bufSize;
|
||||||
int toWrite = bufSize/4;
|
}
|
||||||
|
else {
|
||||||
|
DWORD bufplay;
|
||||||
|
res = buffer->GetCurrentPosition(&bufplay, NULL);
|
||||||
|
if (FAILED(res)) return false;
|
||||||
|
toWrite = (int)bufplay - (int)offset;
|
||||||
|
if (toWrite < 0) toWrite += bufSize;
|
||||||
|
//wxLogDebug(_T("DSound Fill Buffer: bufplay=%u, offset=%d, toWrite=%d"), bufplay, offset, toWrite);
|
||||||
|
}
|
||||||
|
if (toWrite == 0) return true;
|
||||||
|
|
||||||
// Lock buffer
|
// Lock buffer
|
||||||
HRESULT res;
|
RetryLock:
|
||||||
res = buffer->Lock(offset,toWrite,&ptr1,&size1,&ptr2,&size2,0);
|
if (fill) {
|
||||||
|
res = buffer->Lock(offset, toWrite, &ptr1, &size1, &ptr2, &size2, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res = buffer->Lock(offset, toWrite, &ptr1, &size1, &ptr2, &size2, 0);//DSBLOCK_FROMWRITECURSOR);
|
||||||
|
}
|
||||||
|
|
||||||
// Buffer lost?
|
// Buffer lost?
|
||||||
if (res == DSERR_BUFFERLOST) {
|
if (res == DSERR_BUFFERLOST) {
|
||||||
|
wxLogDebug(_T("Lost DSound buffer"));
|
||||||
buffer->Restore();
|
buffer->Restore();
|
||||||
res = buffer->Lock(offset,toWrite,&ptr1,&size1,&ptr2,&size2,0);
|
goto RetryLock;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error
|
// Error
|
||||||
if (FAILED(res)) return false;
|
if (FAILED(res)) return false;
|
||||||
|
|
||||||
// Set offset
|
// Update offset
|
||||||
offset = (offset + toWrite) % bufSize;
|
offset = (offset + toWrite) % bufSize;
|
||||||
|
|
||||||
|
//wxLogDebug(_T("DSound Fill Buffer: offset=%d, toWrite=%d, size1=%u, size2=%u"), offset, toWrite, size1, size2);
|
||||||
|
|
||||||
// Convert size to number of samples
|
// Convert size to number of samples
|
||||||
unsigned long int count1 = size1 / bytesps;
|
unsigned long int count1 = size1 / bytesps;
|
||||||
unsigned long int count2 = size2 / bytesps;
|
unsigned long int count2 = size2 / bytesps;
|
||||||
|
|
||||||
// Check if it's writing over play
|
// Check if remaining buffer is longer than remaining sound
|
||||||
unsigned long int totalCount = count1+count2;
|
unsigned long int totalCount = count1+count2;
|
||||||
unsigned long int left = 0;
|
unsigned long int left = 0;
|
||||||
if (endPos > playPos) left = endPos - playPos;
|
if (endPos > playPos) left = endPos - playPos;
|
||||||
unsigned long int delta = 0;
|
unsigned long int delta = 0;
|
||||||
if (totalCount > left) delta = totalCount - left;
|
if (totalCount > left) delta = totalCount - left;
|
||||||
|
|
||||||
// If so, don't allow it
|
// If so, zero-fill buffer first
|
||||||
if (delta) {
|
if (delta) {
|
||||||
// Zero at start
|
// Zero at start
|
||||||
memset(ptr1,0,size1);
|
memset(ptr1,0,size1);
|
||||||
|
@ -204,13 +222,17 @@ bool DirectSoundPlayer::FillBuffer() {
|
||||||
delta -= temp;
|
delta -= temp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//wxLogDebug(_T("DSound Fill Buffer, playpos=%d, count1=%u, count2=%u"), (int)playPos, count1, count2);
|
||||||
|
|
||||||
// Get source wave
|
// Get source wave
|
||||||
if (count1) provider->GetAudioWithVolume(ptr1,playPos,count1,volume);
|
if (count1) provider->GetAudioWithVolume(ptr1, playPos, count1, volume);
|
||||||
if (count2) provider->GetAudioWithVolume(ptr2,playPos+count1,count2,volume);
|
if (count2) provider->GetAudioWithVolume(ptr2, playPos+count1, count2, volume);
|
||||||
playPos += totalCount;
|
playPos += count1+count2;
|
||||||
|
|
||||||
|
//wxLogDebug(_T("DSound Fill Buffer post-fill, playpos=%d, count1=%u, count2=%u"), (int)playPos, count1, count2);
|
||||||
|
|
||||||
// Unlock
|
// Unlock
|
||||||
buffer->Unlock(ptr1,size1,ptr2,size2);
|
buffer->Unlock(ptr1,count1*bytesps,ptr2,count2*bytesps);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -223,40 +245,19 @@ void DirectSoundPlayer::Play(__int64 start,__int64 count) {
|
||||||
Stop();
|
Stop();
|
||||||
// The thread is now guaranteed dead
|
// The thread is now guaranteed dead
|
||||||
|
|
||||||
|
HRESULT res;
|
||||||
|
|
||||||
// We sure better have a buffer
|
// We sure better have a buffer
|
||||||
assert(buffer);
|
assert(buffer);
|
||||||
|
|
||||||
// Create a new notification event
|
|
||||||
// It was already destroyed by Stop
|
|
||||||
notificationEvent = CreateEvent(NULL,false,false,NULL);
|
|
||||||
|
|
||||||
// Create notification interface
|
|
||||||
IDirectSoundNotify8 *notify;
|
|
||||||
HRESULT res;
|
|
||||||
res = buffer->QueryInterface(IID_IDirectSoundNotify8,(LPVOID*)¬ify);
|
|
||||||
if (FAILED(res)) return;
|
|
||||||
|
|
||||||
// Set notification
|
|
||||||
DSBPOSITIONNOTIFY positionNotify[4];
|
|
||||||
positionNotify[0].dwOffset = bufSize / 8;
|
|
||||||
positionNotify[0].hEventNotify = notificationEvent;
|
|
||||||
positionNotify[1].dwOffset = 3 * bufSize / 8;
|
|
||||||
positionNotify[1].hEventNotify = notificationEvent;
|
|
||||||
positionNotify[2].dwOffset = 5 * bufSize / 8;
|
|
||||||
positionNotify[2].hEventNotify = notificationEvent;
|
|
||||||
positionNotify[3].dwOffset = 7 * bufSize / 8;
|
|
||||||
positionNotify[3].hEventNotify = notificationEvent;
|
|
||||||
res = notify->SetNotificationPositions(4,positionNotify);
|
|
||||||
notify->Release();
|
|
||||||
|
|
||||||
// Set variables
|
// Set variables
|
||||||
startPos = start;
|
startPos = start;
|
||||||
endPos = start+count;
|
endPos = start+count;
|
||||||
playPos = start;
|
playPos = start;
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
|
||||||
// Fill buffer
|
// Fill whole buffer
|
||||||
FillBuffer();
|
FillBuffer(true);
|
||||||
|
|
||||||
// Start thread
|
// Start thread
|
||||||
thread = new DirectSoundPlayerThread(this);
|
thread = new DirectSoundPlayerThread(this);
|
||||||
|
@ -285,7 +286,7 @@ void DirectSoundPlayer::Stop(bool timerToo) {
|
||||||
// The thread is now guaranteed dead and there are no concurrency problems to worry about
|
// The thread is now guaranteed dead and there are no concurrency problems to worry about
|
||||||
|
|
||||||
// Stop
|
// Stop
|
||||||
if (buffer) buffer->Stop();
|
if (buffer) buffer->Stop(); // the thread should have done this already
|
||||||
|
|
||||||
// Reset variables
|
// Reset variables
|
||||||
playing = false;
|
playing = false;
|
||||||
|
@ -295,7 +296,8 @@ void DirectSoundPlayer::Stop(bool timerToo) {
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
|
||||||
// Close event handle
|
// Close event handle
|
||||||
CloseHandle(notificationEvent);
|
if (notificationEvent)
|
||||||
|
CloseHandle(notificationEvent);
|
||||||
notificationEvent = 0;
|
notificationEvent = 0;
|
||||||
|
|
||||||
// Stop timer
|
// Stop timer
|
||||||
|
@ -344,7 +346,7 @@ __int64 DirectSoundPlayer::GetCurrentPosition() {
|
||||||
// Thread constructor
|
// Thread constructor
|
||||||
DirectSoundPlayerThread::DirectSoundPlayerThread(DirectSoundPlayer *par) : wxThread(wxTHREAD_JOINABLE) {
|
DirectSoundPlayerThread::DirectSoundPlayerThread(DirectSoundPlayer *par) : wxThread(wxTHREAD_JOINABLE) {
|
||||||
parent = par;
|
parent = par;
|
||||||
stopnotify = CreateSemaphore(NULL, 0, 1, NULL);
|
stopnotify = CreateEvent(NULL, false, false, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -358,25 +360,17 @@ DirectSoundPlayerThread::~DirectSoundPlayerThread() {
|
||||||
//////////////////////
|
//////////////////////
|
||||||
// Thread entry point
|
// Thread entry point
|
||||||
wxThread::ExitCode DirectSoundPlayerThread::Entry() {
|
wxThread::ExitCode DirectSoundPlayerThread::Entry() {
|
||||||
// Objects to wait for
|
// Wake up thread every half second to fill buffer as needed
|
||||||
HANDLE notifies[2] = {stopnotify, parent->notificationEvent};
|
// This more or less assumes the buffer is at least one second long
|
||||||
|
while (WaitForSingleObject(stopnotify, 500)) {
|
||||||
while (true) {
|
if (!parent->FillBuffer(false))
|
||||||
// Wait for something to happen
|
// FillBuffer returns true if there's more to play
|
||||||
DWORD cause = WaitForMultipleObjects(2, notifies, false, INFINITE);
|
// So false means end-of-stream
|
||||||
|
|
||||||
if (cause == WAIT_OBJECT_0) {
|
|
||||||
// stopnotify
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
else if (cause == WAIT_OBJECT_0 + 1) {
|
|
||||||
// dsound notification event
|
|
||||||
// fill the buffer
|
|
||||||
if (!parent->FillBuffer())
|
|
||||||
break; // end of stream
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
wxLogDebug(_T("DS thread dead"));
|
||||||
|
|
||||||
parent->playing = false;
|
parent->playing = false;
|
||||||
parent->buffer->Stop();
|
parent->buffer->Stop();
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -387,7 +381,7 @@ wxThread::ExitCode DirectSoundPlayerThread::Entry() {
|
||||||
// Stop playback thread
|
// Stop playback thread
|
||||||
void DirectSoundPlayerThread::Stop() {
|
void DirectSoundPlayerThread::Stop() {
|
||||||
// Increase the stopnotify by one, causing a wait for it to succeed
|
// Increase the stopnotify by one, causing a wait for it to succeed
|
||||||
ReleaseSemaphore(stopnotify, 1, NULL);
|
SetEvent(stopnotify);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -86,7 +86,7 @@ private:
|
||||||
IDirectSoundBuffer8 *buffer;
|
IDirectSoundBuffer8 *buffer;
|
||||||
HANDLE notificationEvent;
|
HANDLE notificationEvent;
|
||||||
|
|
||||||
bool FillBuffer();
|
bool FillBuffer(bool fill);
|
||||||
|
|
||||||
DirectSoundPlayerThread *thread;
|
DirectSoundPlayerThread *thread;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue