/* Output */
LPDIRECTSOUND pDirectSound;
+ LPDIRECTSOUNDBUFFER pDirectSoundPrimaryBuffer;
LPDIRECTSOUNDBUFFER pDirectSoundOutputBuffer;
DWORD outputBufferWriteOffsetBytes; /* last write position */
INT outputBufferSizeBytes;
PaStreamCallbackFlags callbackFlags;
+ PaStreamFlags streamFlags;
int callbackResult;
HANDLE processingCompleted;
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);
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.)
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.
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
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;
}
// ----------------------------------------------------------------------
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;
}
/***********************************************************************************/
streamRepresentationIsInitialized = 1;
+ stream->streamFlags = streamFlags;
+
PaUtil_InitializeCpuLoadMeasurer( &stream->cpuLoadMeasurer, sampleRate );
stream->pDirectSoundOutputBuffer = NULL;
}
+ if( stream->pDirectSoundPrimaryBuffer )
+ {
+ IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
+ stream->pDirectSoundPrimaryBuffer = NULL;
+ }
+
if( stream->pDirectSoundInputBuffer )
{
IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
stream->pDirectSoundOutputBuffer = NULL;
}
+ if( stream->pDirectSoundPrimaryBuffer )
+ {
+ IDirectSoundBuffer_Release( stream->pDirectSoundPrimaryBuffer );
+ stream->pDirectSoundPrimaryBuffer = NULL;
+ }
+
if( stream->pDirectSoundInputBuffer )
{
IDirectSoundCaptureBuffer_Stop( stream->pDirectSoundInputBuffer );
}
/***********************************************************************************/
+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 );
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;
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
{
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 */
}
}