*/
/*
- * The text above constitutes the entire PortAudio license; however,
+ * The text above constitutes the entire PortAudio license; however,
* the PortAudio community also makes the following non-binding requests:
*
* Any person wishing to distribute modifications to the Software is
* requested to send the modifications to the original developer so that
- * they can be incorporated into the canonical version. It is also
- * requested that these non-binding requests be included along with the
+ * they can be incorporated into the canonical version. It is also
+ * requested that these non-binding requests be included along with the
* license above.
*/
UINT readOffset; /* last read position */
UINT inputBufferSizeBytes;
-
+
int hostBufferSizeFrames; /* input and output host ringbuffers have the same number of frames */
double framesWritten;
double secondsPerHostByte; /* Used to optimize latency calculation for outTime */
PaStreamFlags streamFlags;
int callbackResult;
HANDLE processingCompleted;
-
+
/* FIXME - move all below to PaUtilStreamRepresentation */
volatile int isStarted;
volatile int isActive;
volatile int stopProcessing; /* stop thread once existing buffers have been returned */
volatile int abortProcessing; /* stop thread immediately */
- UINT systemTimerResolutionPeriodMs; /* set to 0 if we were unable to set the timer period */
+ UINT systemTimerResolutionPeriodMs; /* set to 0 if we were unable to set the timer period */
#ifdef PA_WIN_DS_USE_WMME_TIMER
MMRESULT timerID;
/*************************************************************************
** Return minimum workable latency required for this host. This is returned
** As the default stream latency in PaDeviceInfo.
-** Latency can be optionally set by user by setting an environment variable.
+** Latency can be optionally set by user by setting an environment variable.
** For example, to set latency to 200 msec, put:
**
** set PA_MIN_LATENCY_MSEC=200
static char *DuplicateDeviceNameString( PaUtilAllocationGroup *allocations, const wchar_t* src )
{
char *result = 0;
-
+
if( src != NULL )
{
size_t len = WideCharToMultiByte(CP_UTF8, 0, src, -1, NULL, 0, NULL, NULL);
guidVector->items = (DSDeviceNameAndGUID*)LocalAlloc( LMEM_FIXED, sizeof(DSDeviceNameAndGUID) * guidVector->free );
if( guidVector->items == NULL )
result = paInsufficientMemory;
-
+
return result;
}
PaError result = paNoError;
DSDeviceNameAndGUID *newItems;
int i;
-
+
/* double size of vector */
int size = guidVector->count + guidVector->free;
guidVector->free += size;
LocalFree( guidVector->items );
guidVector->items = newItems;
- }
+ }
return result;
}
}
/************************************************************************************
-** Collect preliminary device information during DirectSound enumeration
+** Collect preliminary device information during DirectSound enumeration
*/
static BOOL CALLBACK CollectGUIDsProcW(LPGUID lpGUID,
LPCWSTR lpszDesc,
return FALSE;
}
}
-
+
/* Set GUID pointer, copy GUID to storage in DSDeviceNameAndGUIDVector. */
if( lpGUID == NULL )
{
{
namesAndGUIDs->items[namesAndGUIDs->count].lpGUID =
&namesAndGUIDs->items[namesAndGUIDs->count].guid;
-
+
memcpy( &namesAndGUIDs->items[namesAndGUIDs->count].guid, lpGUID, sizeof(GUID) );
}
++namesAndGUIDs->count;
--namesAndGUIDs->free;
-
+
return TRUE;
}
DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs = (DSDeviceNamesAndGUIDs*)context;
/*
- Apparently data->Interface can be NULL in some cases.
+ Apparently data->Interface can be NULL in some cases.
Possibly virtual devices without hardware.
So we check for NULLs now. See mailing list message November 10, 2012:
"[Portaudio] portaudio initialization crash in KsPropertySetEnumerateCallback(pa_win_ds.c)"
if( deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID
&& memcmp( &data->DeviceId, deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
{
- deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].pnpInterface =
+ deviceNamesAndGUIDs->outputNamesAndGUIDs.items[i].pnpInterface =
(char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
break;
}
if( deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID
&& memcmp( &data->DeviceId, deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].lpGUID, sizeof(GUID) ) == 0 )
{
- deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].pnpInterface =
+ deviceNamesAndGUIDs->inputNamesAndGUIDs.items[i].pnpInterface =
(char*)DuplicateWCharString( deviceNamesAndGUIDs->winDsHostApi->allocations, data->Interface );
break;
}
}
-static GUID pawin_CLSID_DirectSoundPrivate =
+static GUID pawin_CLSID_DirectSoundPrivate =
{ 0x11ab3ec0, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
-static GUID pawin_DSPROPSETID_DirectSoundDevice =
+static GUID pawin_DSPROPSETID_DirectSoundDevice =
{ 0x84624f82, 0x25ec, 0x11d1, 0xa4, 0xd8, 0x00, 0xc0, 0x4f, 0xc2, 0x8a, 0xca };
-static GUID pawin_IID_IKsPropertySet =
+static GUID pawin_IID_IKsPropertySet =
{ 0x31efac30, 0x515c, 0x11d0, 0xa9, 0xaa, 0x00, 0xaa, 0x00, 0x61, 0xbe, 0x93 };
FindDevicePnpInterfaces fills in the pnpInterface fields in deviceNamesAndGUIDs
with UNICODE file paths to the devices. The DS documentation mentions
at least two techniques by which these Interface paths can be found using IKsPropertySet on
- the DirectSound class object. One is using the DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION
+ the DirectSound class object. One is using the DSPROPERTY_DIRECTSOUNDDEVICE_DESCRIPTION
property, and the other is using DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE.
I tried both methods and only the second worked. I found two postings on the
- net from people who had the same problem with the first method, so I think the method used here is
+ net from people who had the same problem with the first method, so I think the method used here is
more common/likely to work. The problem is that IKsPropertySet_Get returns S_OK
but the fields of the device description are not filled in.
- The mechanism we use works by registering an enumeration callback which is called for
+ The mechanism we use works by registering an enumeration callback which is called for
every DSound device. Our callback searches for a device in our deviceNamesAndGUIDs list
with the matching GUID and copies the pointer to the Interface path.
- Note that we could have used this enumeration callback to perform the original
+ Note that we could have used this enumeration callback to perform the original
device enumeration, however we choose not to so we can disable this step easily.
Apparently the IKsPropertySet mechanism was added in DirectSound 9c 2004
static void FindDevicePnpInterfaces( DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs )
{
IClassFactory *pClassFactory;
-
+
if( paWinDsDSoundEntryPoints.DllGetClassObject(&pawin_CLSID_DirectSoundPrivate, &IID_IClassFactory, (PVOID *) &pClassFactory) == S_OK ){
IKsPropertySet *pPropertySet;
if( pClassFactory->lpVtbl->CreateInstance( pClassFactory, NULL, &pawin_IID_IKsPropertySet, (PVOID *) &pPropertySet) == S_OK ){
-
+
DSPROPERTY_DIRECTSOUNDDEVICE_ENUMERATE_W_DATA data;
ULONG bytesReturned;
sizeof(data),
&bytesReturned
);
-
+
IKsPropertySet_Release( pPropertySet );
}
pClassFactory->lpVtbl->Release( pClassFactory );
}
/*
- The following code fragment, which I chose not to use, queries for the
+ The following code fragment, which I chose not to use, queries for the
device interface for a device with a specific GUID:
ULONG BytesReturned;
memset (&Property, 0, sizeof(Property));
Property.DataFlow = DIRECTSOUNDDEVICE_DATAFLOW_RENDER;
- Property.DeviceId = *lpGUID;
+ Property.DeviceId = *lpGUID;
hr = IKsPropertySet_Get( pPropertySet,
&pawin_DSPROPSETID_DirectSoundDevice,
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
-/*
+/*
GUIDs for emulated devices which we blacklist below.
are there more than two of them??
*/
memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
}
-
+
if( lpGUID )
{
if (IsEqualGUID (&IID_IRolandVSCEmulated1,lpGUID) ||
hr = IDirectSound_Initialize( lpDirectSound, lpGUID );
}
*/
-
+
if( hr != DS_OK )
{
if (hr == DSERR_ALLOCATED)
else
{
/* Query device characteristics. */
- memset( &caps, 0, sizeof(caps) );
+ memset( &caps, 0, sizeof(caps) );
caps.dwSize = sizeof(caps);
hr = IDirectSound_GetCaps( lpDirectSound, &caps );
if( hr != DS_OK )
set deviceOutputChannelCountIsKnown to 0 (unknown).
In this case OpenStream will try to open the device
when the user requests more than 2 channels, rather than
- returning an error.
+ returning an error.
*/
if( caps.dwFlags & DSCAPS_PRIMARYSTEREO )
{
winDsDeviceInfo->deviceOutputChannelCountIsKnown = 1;
}
- /* Guess channels count from speaker configuration. We do it only when
+ /* Guess channels count from speaker configuration. We do it only when
pnpInterface is NULL or when PAWIN_USE_WDMKS_DEVICE_INFO is undefined.
*/
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
case DSSPEAKER_7POINT1: count = 8; break;
#ifndef DSSPEAKER_7POINT1_SURROUND
#define DSSPEAKER_7POINT1_SURROUND 0x00000008
-#endif
+#endif
case DSSPEAKER_7POINT1_SURROUND: count = 8; break;
#ifndef DSSPEAKER_5POINT1_SURROUND
#define DSSPEAKER_5POINT1_SURROUND 0x00000009
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
/* initialize defaultSampleRate */
-
+
if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
{
/* initialize to caps.dwMaxSecondarySampleRate incase none of the standard rates match */
else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
//printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
- // dwFlags | DSCAPS_CONTINUOUSRATE
+ // dwFlags | DSCAPS_CONTINUOUSRATE
deviceInfo->defaultLowInputLatency = 0.;
deviceInfo->defaultHighInputLatency = 0.;
if( lpGUID == NULL )
hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
-
+
hostApi->info.deviceCount++;
}
DSCCAPS caps;
int deviceOK = TRUE;
PaError result = paNoError;
-
+
/* Copy GUID to the device info structure. Set pointer. */
if( lpGUID == NULL )
{
deviceInfo->defaultLowInputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
deviceInfo->defaultHighInputLatency = deviceInfo->defaultLowInputLatency * 2;
-
+
deviceInfo->defaultLowOutputLatency = 0.;
deviceInfo->defaultHighOutputLatency = 0.;
}
}
-
+
IDirectSoundCapture_Release( lpDirectSoundCapture );
}
(*hostApi)->info.structVersion = 1;
(*hostApi)->info.type = paDirectSound;
(*hostApi)->info.name = "Windows DirectSound";
-
+
(*hostApi)->info.deviceCount = 0;
(*hostApi)->info.defaultInputDevice = paNoDevice;
(*hostApi)->info.defaultOutputDevice = paNoDevice;
-
+
/* DSound - enumerate devices to count them and to gather their GUIDs */
result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
if( result != paNoError )
goto error;
}
- }
+ }
result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
if( result != paNoError )
if( result != paNoError )
goto error;
-
+
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
outputChannelCount = outputParameters->channelCount;
outputSampleFormat = outputParameters->sampleFormat;
-
+
/* unless alternate device specification is supported, reject the use of
paUseHostApiSpecificDeviceSpecification */
{
outputChannelCount = 0;
}
-
+
/*
IMPLEMENT ME:
static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream,
PaWinDsDeviceInfo *inputDevice,
PaSampleFormat hostInputSampleFormat,
- WORD inputChannelCount,
+ WORD inputChannelCount,
int bytesPerInputBuffer,
PaWinWaveFormatChannelMask inputChannelMask,
PaWinDsDeviceInfo *outputDevice,
PaSampleFormat hostOutputSampleFormat,
- WORD outputChannelCount,
+ WORD outputChannelCount,
int bytesPerOutputBuffer,
PaWinWaveFormatChannelMask outputChannelMask,
unsigned long nFrameRate
// capture buffer description
// only try wave format extensible. assume it's available on all ds 8 systems
- PaWin_InitializeWaveFormatExtensible( &captureWaveFormat, inputChannelCount,
+ PaWin_InitializeWaveFormatExtensible( &captureWaveFormat, inputChannelCount,
hostInputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostInputSampleFormat ),
nFrameRate, inputChannelMask );
// render buffer description
- PaWin_InitializeWaveFormatExtensible( &renderWaveFormat, outputChannelCount,
+ PaWin_InitializeWaveFormatExtensible( &renderWaveFormat, outputChannelCount,
hostOutputSampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( hostOutputSampleFormat ),
nFrameRate, outputChannelMask );
/* note that we don't create a primary buffer here at all */
- hr = paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8(
+ hr = paWinDsDSoundEntryPoints.DirectSoundFullDuplexCreate8(
inputDevice->lpGUID, outputDevice->lpGUID,
&captureDesc, &secondaryRenderDesc,
GetDesktopWindow(), /* see InitOutputBuffer() for a discussion of whether this is a good idea */
&stream->pDirectSoundFullDuplex8,
&pCaptureBuffer8,
&pRenderBuffer8,
- NULL /* pUnkOuter must be NULL */
+ NULL /* pUnkOuter must be NULL */
);
if( hr == DS_OK )
/* retrieve the pre ds 8 buffer interfaces which are used by the rest of the code */
hr = IUnknown_QueryInterface( pCaptureBuffer8, &IID_IDirectSoundCaptureBuffer, (LPVOID *)&stream->pDirectSoundInputBuffer );
-
+
if( hr == DS_OK )
hr = IUnknown_QueryInterface( pRenderBuffer8, &IID_IDirectSoundBuffer, (LPVOID *)&stream->pDirectSoundOutputBuffer );
IUnknown_Release( stream->pDirectSoundOutputBuffer );
stream->pDirectSoundOutputBuffer = NULL;
}
-
+
IUnknown_Release( stream->pDirectSoundFullDuplex8 );
stream->pDirectSoundFullDuplex8 = NULL;
}
#endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
-static HRESULT InitInputBuffer( PaWinDsStream *stream,
- PaWinDsDeviceInfo *device,
- PaSampleFormat sampleFormat,
- unsigned long nFrameRate,
- WORD nChannels,
- int bytesPerBuffer,
+static HRESULT InitInputBuffer( PaWinDsStream *stream,
+ PaWinDsDeviceInfo *device,
+ PaSampleFormat sampleFormat,
+ unsigned long nFrameRate,
+ WORD nChannels,
+ int bytesPerBuffer,
PaWinWaveFormatChannelMask channelMask )
{
DSCBUFFERDESC captureDesc;
PaWinWaveFormat waveFormat;
HRESULT result;
-
- if( (result = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate(
+
+ if( (result = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate(
device->lpGUID, &stream->pDirectSoundCapture, NULL) ) != DS_OK ){
- ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
- return result;
+ ERR_RPT(("PortAudio: DirectSoundCaptureCreate() failed!\n"));
+ return result;
}
// Setup the secondary buffer description
captureDesc.dwFlags = 0;
captureDesc.dwBufferBytes = bytesPerBuffer;
captureDesc.lpwfxFormat = (WAVEFORMATEX*)&waveFormat;
-
+
// Create the capture buffer
// first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
- PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
+ PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
nFrameRate, channelMask );
if( IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
&captureDesc, &stream->pDirectSoundInputBuffer, NULL) != DS_OK )
{
- PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
+ PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
if ((result = IDirectSoundCapture_CreateCaptureBuffer( stream->pDirectSoundCapture,
}
-static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device,
- PaSampleFormat sampleFormat, unsigned long nFrameRate,
- WORD nChannels, int bytesPerBuffer,
+static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device,
+ PaSampleFormat sampleFormat, unsigned long nFrameRate,
+ WORD nChannels, int bytesPerBuffer,
PaWinWaveFormatChannelMask channelMask )
{
HRESULT result;
PaWinWaveFormat waveFormat;
DSBUFFERDESC primaryDesc;
DSBUFFERDESC secondaryDesc;
-
- if( (hr = paWinDsDSoundEntryPoints.DirectSoundCreate(
+
+ if( (hr = paWinDsDSoundEntryPoints.DirectSoundCreate(
device->lpGUID, &stream->pDirectSound, NULL )) != DS_OK ){
ERR_RPT(("PortAudio: DirectSoundCreate() failed!\n"));
return hr;
// Set the primary buffer's format
// first try WAVEFORMATEXTENSIBLE. if this fails, fall back to WAVEFORMATEX
- PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
+ PaWin_InitializeWaveFormatExtensible( &waveFormat, nChannels,
sampleFormat, PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ),
nFrameRate, channelMask );
if( IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat) != DS_OK )
{
- PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
+ PaWin_InitializeWaveFormatEx( &waveFormat, nChannels, sampleFormat,
PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat ), nFrameRate );
if((result = IDirectSoundBuffer_SetFormat( stream->pDirectSoundPrimaryBuffer, (WAVEFORMATEX*)&waveFormat)) != DS_OK)
// Create the secondary buffer
if ((result = IDirectSound_CreateSoundBuffer( stream->pDirectSound,
&secondaryDesc, &stream->pDirectSoundOutputBuffer, NULL)) != DS_OK)
- goto error;
-
+ goto error;
+
return DS_OK;
error:
}
-static void CalculateBufferSettings( unsigned long *hostBufferSizeFrames,
+static void CalculateBufferSettings( unsigned long *hostBufferSizeFrames,
unsigned long *pollingPeriodFrames,
int isFullDuplex,
unsigned long suggestedInputLatencyFrames,
unsigned long minimumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS);
unsigned long maximumPollingPeriodFrames = (unsigned long)(sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS);
unsigned long pollingJitterFrames = (unsigned long)(sampleRate * PA_DS_POLLING_JITTER_SECONDS);
-
+
if( userFramesPerBuffer == paFramesPerBufferUnspecified )
{
unsigned long targetBufferingLatencyFrames = max( suggestedInputLatencyFrames, suggestedOutputLatencyFrames );
*pollingPeriodFrames = maximumPollingPeriodFrames;
}
- *hostBufferSizeFrames = *pollingPeriodFrames
+ *hostBufferSizeFrames = *pollingPeriodFrames
+ max( *pollingPeriodFrames + pollingJitterFrames, targetBufferingLatencyFrames);
}
else
/* In full duplex streams we know that the buffer adapter adds userFramesPerBuffer
extra fixed latency. so we subtract it here as a fixed latency before computing
the buffer size. being careful not to produce an unrepresentable negative result.
-
+
Note: this only works as expected if output latency is greater than input latency.
Otherwise we use input latency anyway since we do max(in,out).
*/
if( userFramesPerBuffer < suggestedOutputLatencyFrames )
{
- unsigned long adjustedSuggestedOutputLatencyFrames =
+ unsigned long adjustedSuggestedOutputLatencyFrames =
suggestedOutputLatencyFrames - userFramesPerBuffer;
/* maximum of input and adjusted output suggested latency */
/* maximum of input and output suggested latency */
if( suggestedOutputLatencyFrames > suggestedInputLatencyFrames )
targetBufferingLatencyFrames = suggestedOutputLatencyFrames;
- }
+ }
- *hostBufferSizeFrames = userFramesPerBuffer
+ *hostBufferSizeFrames = userFramesPerBuffer
+ max( userFramesPerBuffer + pollingJitterFrames, targetBufferingLatencyFrames);
*pollingPeriodFrames = max( max(1, userFramesPerBuffer / 4), targetBufferingLatencyFrames / 16 );
{
*pollingPeriodFrames = maximumPollingPeriodFrames;
}
- }
+ }
}
-static void CalculatePollingPeriodFrames( unsigned long hostBufferSizeFrames,
+static void CalculatePollingPeriodFrames( unsigned long hostBufferSizeFrames,
unsigned long *pollingPeriodFrames,
double sampleRate, unsigned long userFramesPerBuffer )
{
}
-static void SetStreamInfoLatencies( PaWinDsStream *stream,
- unsigned long userFramesPerBuffer,
+static void SetStreamInfoLatencies( PaWinDsStream *stream,
+ unsigned long userFramesPerBuffer,
unsigned long pollingPeriodFrames,
double sampleRate )
{
- /* compute the stream info actual latencies based on framesPerBuffer, polling period, hostBufferSizeFrames,
+ /* compute the stream info actual latencies based on framesPerBuffer, polling period, hostBufferSizeFrames,
and the configuration of the buffer processor */
unsigned long effectiveFramesPerBuffer = (userFramesPerBuffer == paFramesPerBufferUnspecified)
if( stream->bufferProcessor.inputChannelCount > 0 )
{
- /* stream info input latency is the minimum buffering latency
+ /* stream info input latency is the minimum buffering latency
(unlike suggested and default which are *maximums*) */
stream->streamRepresentation.streamInfo.inputLatency =
(double)(PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor)
/* IDEA: the following 3 checks could be performed by default by pa_front
unless some flag indicated otherwise */
-
+
/* unless alternate device specification is supported, reject the use of
paUseHostApiSpecificDeviceSpecification */
if( inputParameters->device == paUseHostApiSpecificDeviceSpecification )
if( inputWinDsDeviceInfo->deviceInputChannelCountIsKnown
&& inputChannelCount > inputDeviceInfo->maxInputChannels )
return paInvalidChannelCount;
-
+
/* validate hostApiSpecificStreamInfo */
inputStreamInfo = (PaWinDirectSoundStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
/* validate hostApiSpecificStreamInfo */
outputStreamInfo = (PaWinDirectSoundStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
- if( result != paNoError ) return result;
+ if( result != paNoError ) return result;
if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
userRequestedHostOutputBufferSizeFrames = outputStreamInfo->framesPerBuffer;
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
&winDsHostApi->blockingStreamInterface, streamCallback, userData );
}
-
+
streamRepresentationIsInitialized = 1;
stream->streamFlags = streamFlags;
bufferProcessorIsInitialized = 1;
-
-/* DirectSound specific initialization */
+
+/* DirectSound specific initialization */
{
HRESULT hr;
unsigned long integerSampleRate = (unsigned long) (sampleRate + 0.5);
-
+
stream->processingCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
if( stream->processingCompleted == NULL )
{
*/
stream->hostBufferSizeFrames = max( userRequestedHostInputBufferSizeFrames, userRequestedHostOutputBufferSizeFrames );
- CalculatePollingPeriodFrames(
+ CalculatePollingPeriodFrames(
stream->hostBufferSizeFrames, &pollingPeriodFrames,
sampleRate, framesPerBuffer );
}
CalculateBufferSettings( (unsigned long*)&stream->hostBufferSizeFrames, &pollingPeriodFrames,
/* isFullDuplex = */ (inputParameters && outputParameters),
suggestedInputLatencyFrames,
- suggestedOutputLatencyFrames,
+ suggestedOutputLatencyFrames,
sampleRate, framesPerBuffer );
}
stream->pollingPeriodSeconds = pollingPeriodFrames / sampleRate;
- DBUG(("DirectSound host buffer size frames: %d, polling period seconds: %f, @ sr: %f\n",
+ DBUG(("DirectSound host buffer size frames: %d, polling period seconds: %f, @ sr: %f\n",
stream->hostBufferSizeFrames, stream->pollingPeriodSeconds, sampleRate ));
PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ outputParameters->device ];
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
*/
-
+
int sampleSizeBytes = Pa_GetSampleSize(hostOutputSampleFormat);
stream->outputFrameSizeBytes = outputParameters->channelCount * sampleSizeBytes;
stream->outputIsRunning = FALSE;
stream->outputUnderflowCount = 0;
-
+
/* perfCounterTicksPerBuffer is used by QueryOutputSpace for overflow detection */
if( QueryPerformanceFrequency( &counterFrequency ) )
{
PaDeviceInfo *deviceInfo = hostApi->deviceInfos[ inputParameters->device ];
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
*/
-
+
int sampleSizeBytes = Pa_GetSampleSize(hostInputSampleFormat);
stream->inputFrameSizeBytes = inputParameters->channelCount * sampleSizeBytes;
assert( stream->pDirectSound == NULL );
assert( stream->pDirectSoundPrimaryBuffer == NULL );
assert( stream->pDirectSoundOutputBuffer == NULL );
-
+
if( inputParameters && outputParameters )
{
integerSampleRate
);
DBUG(("InitFullDuplexInputOutputBuffers() returns %x\n", hr));
- /* ignore any error returned by InitFullDuplexInputOutputBuffers.
+ /* ignore any error returned by InitFullDuplexInputOutputBuffers.
we retry opening the buffers below */
#endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */
}
- /* create half duplex buffers. also used for full-duplex streams which didn't
+ /* create half duplex buffers. also used for full-duplex streams which didn't
succeed when using the full duplex API. that could happen because
- DX8 or greater isn't installed, the i/o devices aren't the same
+ DX8 or greater isn't installed, the i/o devices aren't the same
physical device. etc.
*/
/*
From MSDN:
- The write cursor indicates the position at which it is safe
+ The write cursor indicates the position at which it is safe
to write new data to the buffer. The write cursor always leads the
play cursor, typically by about 15 milliseconds' worth of audio
data.
- It is always safe to change data that is behind the position
+ It is always safe to change data that is behind the position
indicated by the lpdwCurrentPlayCursor parameter.
*/
double outputLatency = 0;
double inputLatency = 0;
PaStreamCallbackTimeInfo timeInfo = {0,0,0};
-
+
/* Input */
LPBYTE lpInBuf1 = NULL;
LPBYTE lpInBuf2 = NULL;
}
// FIXME: what happens if IDirectSoundCaptureBuffer_GetCurrentPosition fails?
- framesToXfer = numInFramesReady = bytesFilled / stream->inputFrameSizeBytes;
+ framesToXfer = numInFramesReady = bytesFilled / stream->inputFrameSizeBytes;
/** @todo Check for overflow */
}
framesToXfer = numOutFramesReady = bytesEmpty / stream->outputFrameSizeBytes;
/* Check for underflow */
- /* FIXME QueryOutputSpace should not adjust underflow count as a side effect.
+ /* FIXME QueryOutputSpace should not adjust underflow count as a side effect.
A query function should be a const operator on the stream and return a flag on underflow. */
if( stream->outputUnderflowCount != previousUnderflowCount )
stream->callbackFlags |= paOutputUnderflow;
PaUtil_BeginBufferProcessing( &stream->bufferProcessor, &timeInfo, stream->callbackFlags );
stream->callbackFlags = 0;
-
+
/* Input */
if( stream->bufferProcessor.inputChannelCount > 0 )
{
- timeInfo.inputBufferAdcTime = timeInfo.currentTime - inputLatency;
+ timeInfo.inputBufferAdcTime = timeInfo.currentTime - inputLatency;
bytesToXfer = framesToXfer * stream->inputFrameSizeBytes;
hresult = IDirectSoundCaptureBuffer_Lock ( stream->pDirectSoundInputBuffer,
numFrames = PaUtil_EndBufferProcessing( &stream->bufferProcessor, &stream->callbackResult );
stream->framesWritten += numFrames;
-
+
if( stream->bufferProcessor.outputChannelCount > 0 )
{
/* FIXME: an underflow could happen here */
}
error2:
- PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );
+ PaUtil_EndCpuLoadMeasurement( &stream->cpuLoadMeasurer, numFrames );
}
if( stream->callbackResult == paComplete && !PaUtil_IsBufferProcessorOutputEmpty( &stream->bufferProcessor ) )
(void) uMsg;
(void) dw1;
(void) dw2;
-
+
stream = (PaWinDsStream *) dwUser;
if( stream == NULL ) return;
int callbackResult = TimeSlice( stream );
if( callbackResult != paContinue )
{
- /* FIXME implement handling of paComplete and paAbort if possible
- At the moment this should behave as if paComplete was called and
+ /* FIXME implement handling of paComplete and paAbort if possible
+ At the moment this should behave as if paComplete was called and
flush the buffer.
*/
// 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,
PaError result = paNoError;
PaWinDsStream *stream = (PaWinDsStream*)s;
HRESULT hr;
-
+
stream->callbackResult = paContinue;
PaUtil_ResetBufferProcessor( &stream->bufferProcessor );
-
+
ResetEvent( stream->processingCompleted );
#ifndef PA_WIN_DS_USE_WMME_TIMER
*/
stream->timerID = timeSetEvent( timerPeriodMs, stream->systemTimerResolutionPeriodMs, (LPTIMECALLBACK) TimerCallback,
(DWORD_PTR) stream, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS );
-
+
if( stream->timerID == 0 )
{
stream->isActive = 0;
if( stream->systemTimerResolutionPeriodMs > 0 ){
timeEndPeriod( stream->systemTimerResolutionPeriodMs );
stream->systemTimerResolutionPeriodMs = 0;
- }
+ }
if( stream->bufferProcessor.outputChannelCount > 0 )
{
/* suppress unused variable warnings */
(void) stream;
-
+
/* IMPLEMENT ME, see portaudio.h for required behavior*/
return 0;