From d9af10e0c94cc94c154ec85b873a8a3ea3ff4f32 Mon Sep 17 00:00:00 2001 From: rossb Date: Sun, 24 Jan 2010 13:17:31 +0000 Subject: [PATCH] implemented paPrimeOutputBuffersUsingStreamCallback for DirectSound (previously this was the default). invoke some buffer reset code to from StartStream (actually ClearOutputBuffer) instead of OpenStream which should give cleaner startup behavior if the stream is started and stopped multiple times. fixed leaks under error in InitOutputBuffer and fixed leak of primary buffer (previously was never released) --- src/hostapi/dsound/pa_win_ds.c | 148 ++++++++++++++++++++++----------- 1 file changed, 100 insertions(+), 48 deletions(-) diff --git a/src/hostapi/dsound/pa_win_ds.c b/src/hostapi/dsound/pa_win_ds.c index f0e3784..78b38fc 100644 --- a/src/hostapi/dsound/pa_win_ds.c +++ b/src/hostapi/dsound/pa_win_ds.c @@ -226,6 +226,7 @@ typedef struct PaWinDsStream /* Output */ LPDIRECTSOUND pDirectSound; + LPDIRECTSOUNDBUFFER pDirectSoundPrimaryBuffer; LPDIRECTSOUNDBUFFER pDirectSoundOutputBuffer; DWORD outputBufferWriteOffsetBytes; /* last write position */ INT outputBufferSizeBytes; @@ -255,6 +256,7 @@ typedef struct PaWinDsStream PaStreamCallbackFlags callbackFlags; + PaStreamFlags streamFlags; int callbackResult; HANDLE processingCompleted; @@ -1463,18 +1465,12 @@ static HRESULT InitInputBuffer( PaWinDsStream *stream, PaSampleFormat sampleForm static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer, PaWinWaveFormatChannelMask channelMask ) { - /** @todo FIXME: if InitOutputBuffer returns an error I'm not sure it frees all resources cleanly */ - - DWORD dwDataLen; - DWORD playCursor; HRESULT result; - LPDIRECTSOUNDBUFFER pPrimaryBuffer; HWND hWnd; HRESULT hr; PaWinWaveFormat waveFormat; DSBUFFERDESC primaryDesc; DSBUFFERDESC secondaryDesc; - unsigned char* pDSBuffData; LARGE_INTEGER counterFrequency; int bytesPerSample = Pa_GetSampleSize(sampleFormat); @@ -1484,6 +1480,18 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFor stream->dsw_framesWritten = 0; stream->bytesPerOutputFrame = nChannels * bytesPerSample; + /* perfCounterTicksPerBuffer is used by QueryOutputSpace for overflow detection */ + if( QueryPerformanceFrequency( &counterFrequency ) ) + { + int framesInBuffer = bytesPerBuffer / (nChannels * bytesPerSample); + stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate; + } + else + { + stream->perfCounterTicksPerBuffer.QuadPart = 0; + } + + // We were using getForegroundWindow() but sometimes the ForegroundWindow may not be the // applications's window. Also if that window is closed before the Buffer is closed // then DirectSound can crash. (Thanks for Scott Patterson for reporting this.) @@ -1497,13 +1505,16 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFor hWnd = GetDesktopWindow(); // Set cooperative level to DSSCL_EXCLUSIVE so that we can get 16 bit output, 44.1 KHz. - // Exclusize also prevents unexpected sounds from other apps during a performance. + // exclusive also prevents unexpected sounds from other apps during a performance. if ((hr = IDirectSound_SetCooperativeLevel( stream->pDirectSound, hWnd, DSSCL_EXCLUSIVE)) != DS_OK) { return hr; } + stream->pDirectSoundPrimaryBuffer = NULL; + stream->pDirectSoundOutputBuffer = NULL; + // ----------------------------------------------------------------------- // Create primary buffer and set format just so we can specify our custom format. // Otherwise we would be stuck with the default which might be 8 bit or 22050 Hz. @@ -1515,7 +1526,8 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFor primaryDesc.lpwfxFormat = NULL; // Create the buffer if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound, - &primaryDesc, &pPrimaryBuffer, NULL)) != DS_OK) return result; + &primaryDesc, &stream->pDirectSoundPrimaryBuffer, NULL)) != DS_OK) + goto error; // Set the primary buffer's format @@ -1524,12 +1536,13 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFor sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate, channelMask ); - if( IDirectSoundBuffer_SetFormat( pPrimaryBuffer, (WAVEFORMATEX*)&waveFormat) != DS_OK ) + if( IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat) != DS_OK ) { PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate ); - if((result = IDirectSoundBuffer_SetFormat( pPrimaryBuffer, (WAVEFORMATEX*)&waveFormat)) != DS_OK) return result; + if((result = IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat)) != DS_OK) + goto error; } // ---------------------------------------------------------------------- @@ -1541,34 +1554,20 @@ static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaSampleFormat sampleFor secondaryDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat; // Create the secondary buffer if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound, - &secondaryDesc, &stream->pDirectSoundOutputBuffer, NULL)) != DS_OK) return result; - // Lock the DS buffer - if ((result = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, 0, stream->outputBufferSizeBytes, (LPVOID*)&pDSBuffData, - &dwDataLen, NULL, 0, 0)) != DS_OK) return result; - // Zero the DS buffer - ZeroMemory(pDSBuffData, dwDataLen); - // Unlock the DS buffer - if ((result = IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) return result; - if( QueryPerformanceFrequency( &counterFrequency ) ) - { - int framesInBuffer = bytesPerBuffer / (nChannels * bytesPerSample); - stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * framesInBuffer) / nFrameRate; - } - else - { - stream->perfCounterTicksPerBuffer.QuadPart = 0; - } - // Let DSound set the starting write position because if we set it to zero, it looks like the - // buffer is full to begin with. This causes a long pause before sound starts when using large buffers. - hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer, - &playCursor, &stream->outputBufferWriteOffsetBytes ); - if( hr != DS_OK ) + &secondaryDesc, &stream->pDirectSoundOutputBuffer, NULL)) != DS_OK) + goto error; + + return DS_OK; + +error: + + if( stream->pDirectSoundPrimaryBuffer ) { - return hr; + IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer ); + stream->pDirectSoundPrimaryBuffer = NULL; } - stream->dsw_framesWritten = stream->outputBufferWriteOffsetBytes / stream->bytesPerOutputFrame; - /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */ - return DS_OK; + + return result; } /***********************************************************************************/ @@ -1727,6 +1726,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, streamRepresentationIsInitialized = 1; + stream->streamFlags = streamFlags; + PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate ); @@ -1944,6 +1945,12 @@ error: stream->pDirectSoundOutputBuffer = NULL; } + if( stream->pDirectSoundPrimaryBuffer ) + { + IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer ); + stream->pDirectSoundPrimaryBuffer = NULL; + } + if( stream->pDirectSoundInputBuffer ) { IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer ); @@ -2353,6 +2360,12 @@ static PaError CloseStream( PaStream* s ) stream->pDirectSoundOutputBuffer = NULL; } + if( stream->pDirectSoundPrimaryBuffer ) + { + IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer ); + stream->pDirectSoundPrimaryBuffer = NULL; + } + if( stream->pDirectSoundInputBuffer ) { IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer ); @@ -2380,12 +2393,47 @@ static PaError CloseStream( PaStream* s ) } /***********************************************************************************/ +static HRESULT ClearOutputBuffer( PaWinDsStream *stream ) +{ + PaError result = paNoError; + unsigned char* pDSBuffData; + DWORD dwDataLen; + HRESULT hr; + + hr = IDirectSoundBuffer_SetCurrentPosition( stream->pDirectSoundOutputBuffer, 0 ); + DBUG(("PaHost_ClearOutputBuffer: IDirectSoundBuffer_SetCurrentPosition returned = 0x%X.\n", hr)); + if( hr != DS_OK ) + return hr; + + // Lock the DS buffer + if ((hr = IDirectSoundBuffer_Lock( stream->pDirectSoundOutputBuffer, 0, stream->outputBufferSizeBytes, (LPVOID*)&pDSBuffData, + &dwDataLen, NULL, 0, 0)) != DS_OK ) + return hr; + + // Zero the DS buffer + ZeroMemory(pDSBuffData, dwDataLen); + // Unlock the DS buffer + if ((hr = IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, pDSBuffData, dwDataLen, NULL, 0)) != DS_OK) + return hr; + + // Let DSound set the starting write position because if we set it to zero, it looks like the + // buffer is full to begin with. This causes a long pause before sound starts when using large buffers. + if ((hr = IDirectSoundBuffer_GetCurrentPosition( stream->pDirectSoundOutputBuffer, + &stream->previousPlayCursor, &stream->outputBufferWriteOffsetBytes )) != DS_OK) + return hr; + + stream->dsw_framesWritten = stream->outputBufferWriteOffsetBytes / stream->bytesPerOutputFrame; + /* printf("DSW_InitOutputBuffer: playCursor = %d, writeCursor = %d\n", playCursor, dsw->dsw_WriteOffset ); */ + + return DS_OK; +} + static PaError StartStream( PaStream *s ) { PaError result = paNoError; PaWinDsStream *stream = (PaWinDsStream*)s; HRESULT hr; - + stream->callbackResult = paContinue; PaUtil_ResetBufferProcessor( &stream->bufferProcessor ); @@ -2417,19 +2465,10 @@ static PaError StartStream( PaStream *s ) if( stream->bufferProcessor.outputChannelCount > 0 ) { - if( stream->streamRepresentation.streamCallback ) - { - /* Give user callback a chance to pre-fill buffer. REVIEW - i thought we weren't pre-filling, rb. */ - TimeSlice( stream ); - /* we ignore the return value from TimeSlice here and start the stream as usual. - The first timer callback will detect if the callback has completed. */ - } - QueryPerformanceCounter( &stream->previousPlayTime ); - stream->previousPlayCursor = 0; stream->finalZeroBytesWritten = 0; - hr = IDirectSoundBuffer_SetCurrentPosition( stream->pDirectSoundOutputBuffer, 0 ); - DBUG(("PaHost_StartOutput: IDirectSoundBuffer_SetCurrentPosition returned = 0x%X.\n", hr)); + + hr = ClearOutputBuffer( stream ); if( hr != DS_OK ) { result = paUnanticipatedHostError; @@ -2437,6 +2476,17 @@ static PaError StartStream( PaStream *s ) goto error; } + if( stream->streamRepresentation.streamCallback && (stream->streamFlags & paPrimeOutputBuffersUsingStreamCallback) ) + { + stream->callbackFlags = paPrimingOutput; + + TimeSlice( stream ); + /* we ignore the return value from TimeSlice here and start the stream as usual. + The first timer callback will detect if the callback has completed. */ + + stream->callbackFlags = 0; + } + // Start the buffer playback in a loop. if( stream->pDirectSoundOutputBuffer != NULL ) // FIXME: not sure this needs to be checked here { @@ -2513,6 +2563,8 @@ static PaError StopStream( PaStream *s ) stream->outputIsRunning = FALSE; // FIXME: what happens if IDirectSoundBuffer_Stop returns an error? hr = IDirectSoundBuffer_Stop( stream->pDirectSoundOutputBuffer ); + + IDirectSoundBuffer_Stop( stream->pDirectSoundPrimaryBuffer ); /* FIXME we never started the primary buffer so I'm not sure we need to stop it */ } } -- 2.43.0