Skip to content

Commit

Permalink
Implemented host-API specific mechanism for setting DirectSound buffe…
Browse files Browse the repository at this point in the history
…r sizes in frames. resolves #129
  • Loading branch information
RossBencina committed Nov 24, 2011
1 parent 08c21ac commit 8b11ef3
Show file tree
Hide file tree
Showing 3 changed files with 288 additions and 29 deletions.
29 changes: 13 additions & 16 deletions include/pa_win_ds.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,24 +61,21 @@ typedef struct PaWinDirectSoundStreamInfo{
PaHostApiTypeId hostApiType; /**< paDirectSound */
unsigned long version; /**< 2 */

unsigned long flags;

/* low-level latency setting support
Control the size of host buffers in order to set latency. They will
be used instead of the generic parameters to Pa_OpenStream() if
flags contains the paWinDirectSoundUseLowLevelLatencyParameters
flag.
If PaWinDirectSoundStreamInfo structures with paWinDirectSoundUseLowLevelLatencyParameters
are supplied for both input and output in a full duplex stream, then the
input and output framesPerBuffer must be the same, or the larger of the
two must be a multiple of the smaller, otherwise a
paIncompatibleHostApiSpecificStreamInfo error will be returned from
Pa_OpenStream().
unsigned long flags; /**< enable other features of this struct */

/**
low-level latency setting support
Sets the size of the DirectSound host buffer.
When flags contains the paWinDirectSoundUseLowLevelLatencyParameters
this size will be used instead of interpreting the generic latency
parameters to Pa_OpenStream(). If the flag is not set this value is ignored.
If the stream is a full duplex stream the implementation requires that
the values of framesPerBuffer for input and output match (if both are specified).
*/
unsigned long framesPerBuffer; /* NOT IMPLEMENTED see http://www.portaudio.com/trac/ticket/129 */
unsigned long framesPerBuffer;

/*
/**
support for WAVEFORMATEXTENSIBLE channel masks. If flags contains
paWinDirectSoundUseChannelMask this allows you to specify which speakers
to address in a multichannel stream. Constants for channelMask
Expand Down
101 changes: 88 additions & 13 deletions src/hostapi/dsound/pa_win_ds.c
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,13 @@ PA_THREAD_FUNC ProcessingThreadProc( void *pArg );

#define PA_DS_WIN_WDM_DEFAULT_LATENCY_ (.120)

/* we allow the polling period to range between 1 and 100ms.
prior to August 2011 we limited the minimum polling period to 10ms.
*/
#define PA_DS_MINIMUM_POLLING_PERIOD_SECONDS (0.001) /* 1ms */
#define PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS (0.100) /* 100ms */
#define PA_DS_POLLING_JITTER_SECONDS (0.001) /* 1ms */

#define SECONDS_PER_MSEC (0.001)
#define MSECS_PER_SECOND (1000)

Expand Down Expand Up @@ -1336,6 +1343,13 @@ static PaError ValidateWinDirectSoundSpecificStreamInfo(
{
return paIncompatibleHostApiSpecificStreamInfo;
}

if( streamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
{
if( streamInfo->framesPerBuffer <= 0 )
return paIncompatibleHostApiSpecificStreamInfo;

}
}

return paNoError;
Expand Down Expand Up @@ -1541,7 +1555,13 @@ static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream,
#endif /* PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE */


static HRESULT InitInputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer, PaWinWaveFormatChannelMask channelMask )
static HRESULT InitInputBuffer( PaWinDsStream *stream,
PaWinDsDeviceInfo *device,
PaSampleFormat sampleFormat,
unsigned long nFrameRate,
WORD nChannels,
int bytesPerBuffer,
PaWinWaveFormatChannelMask channelMask )
{
DSCBUFFERDESC captureDesc;
PaWinWaveFormat waveFormat;
Expand Down Expand Up @@ -1582,7 +1602,10 @@ static HRESULT InitInputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device
}


static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device, PaSampleFormat sampleFormat, unsigned long nFrameRate, WORD nChannels, int bytesPerBuffer, PaWinWaveFormatChannelMask channelMask )
static HRESULT InitOutputBuffer( PaWinDsStream *stream, PaWinDsDeviceInfo *device,
PaSampleFormat sampleFormat, unsigned long nFrameRate,
WORD nChannels, int bytesPerBuffer,
PaWinWaveFormatChannelMask channelMask )
{
HRESULT result;
HWND hWnd;
Expand Down Expand Up @@ -1680,12 +1703,9 @@ static void CalculateBufferSettings( unsigned long *hostBufferSizeFrames,
unsigned long suggestedOutputLatencyFrames,
double sampleRate, unsigned long userFramesPerBuffer )
{
/* we allow the polling period to range between 1 and 100ms.
prior to August 2011 we limited the minimum polling period to 10ms.
*/
unsigned long minimumPollingPeriodFrames = sampleRate / 1000; /* 1ms */
unsigned long maximumPollingPeriodFrames = sampleRate / 10; /* 100ms */
unsigned long pollingJitterFrames = sampleRate / 1000; /* 1ms */
unsigned long minimumPollingPeriodFrames = sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS;
unsigned long maximumPollingPeriodFrames = sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS;
unsigned long pollingJitterFrames = sampleRate * PA_DS_POLLING_JITTER_SECONDS;

if( userFramesPerBuffer == paFramesPerBufferUnspecified )
{
Expand Down Expand Up @@ -1747,6 +1767,23 @@ static void CalculateBufferSettings( unsigned long *hostBufferSizeFrames,
}


static void CalculatePollingPeriodFrames( unsigned long hostBufferSizeFrames,
unsigned long *pollingPeriodFrames,
double sampleRate, unsigned long userFramesPerBuffer )
{
unsigned long minimumPollingPeriodFrames = sampleRate * PA_DS_MINIMUM_POLLING_PERIOD_SECONDS;
unsigned long maximumPollingPeriodFrames = sampleRate * PA_DS_MAXIMUM_POLLING_PERIOD_SECONDS;
unsigned long pollingJitterFrames = sampleRate * PA_DS_POLLING_JITTER_SECONDS;

*pollingPeriodFrames = max( max(1, userFramesPerBuffer / 4), hostBufferSizeFrames / 16 );

if( *pollingPeriodFrames > maximumPollingPeriodFrames )
{
*pollingPeriodFrames = maximumPollingPeriodFrames;
}
}


static void SetStreamInfoLatencies( PaWinDsStream *stream,
unsigned long userFramesPerBuffer,
unsigned long pollingPeriodFrames,
Expand Down Expand Up @@ -1808,6 +1845,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
int inputChannelCount, outputChannelCount;
PaSampleFormat inputSampleFormat, outputSampleFormat;
PaSampleFormat hostInputSampleFormat, hostOutputSampleFormat;
int userRequestedHostInputBufferSizeFrames = 0;
int userRequestedHostOutputBufferSizeFrames = 0;
unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
Expand Down Expand Up @@ -1840,6 +1879,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
result = ValidateWinDirectSoundSpecificStreamInfo( inputParameters, inputStreamInfo );
if( result != paNoError ) return result;

if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
userRequestedHostInputBufferSizeFrames = inputStreamInfo->framesPerBuffer;

if( inputStreamInfo && inputStreamInfo->flags & paWinDirectSoundUseChannelMask )
inputChannelMask = inputStreamInfo->channelMask;
else
Expand Down Expand Up @@ -1877,6 +1919,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
result = ValidateWinDirectSoundSpecificStreamInfo( outputParameters, outputStreamInfo );
if( result != paNoError ) return result;

if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
userRequestedHostOutputBufferSizeFrames = outputStreamInfo->framesPerBuffer;

if( outputStreamInfo && outputStreamInfo->flags & paWinDirectSoundUseChannelMask )
outputChannelMask = outputStreamInfo->channelMask;
else
Expand All @@ -1889,6 +1934,16 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
suggestedOutputLatencyFrames = 0;
}

/*
If low level host buffer size is specified for both input and output
the current code requires the sizes to match.
*/

if( (userRequestedHostInputBufferSizeFrames > 0 && userRequestedHostOutputBufferSizeFrames > 0)
&& userRequestedHostInputBufferSizeFrames != userRequestedHostOutputBufferSizeFrames )
return paIncompatibleHostApiSpecificStreamInfo;



/*
IMPLEMENT ME:
Expand Down Expand Up @@ -2027,14 +2082,34 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,

/* set up i/o parameters */

CalculateBufferSettings( &stream->hostBufferSizeFrames, &pollingPeriodFrames,
/* isFullDuplex = */ (inputParameters && outputParameters),
suggestedInputLatencyFrames,
suggestedOutputLatencyFrames,
sampleRate, framesPerBuffer );
if( userRequestedHostInputBufferSizeFrames > 0 || userRequestedHostOutputBufferSizeFrames > 0 )
{
/* use low level parameters */

/* since we use the same host buffer size for input and output
we choose the highest user specified value.
*/
stream->hostBufferSizeFrames = max( userRequestedHostInputBufferSizeFrames, userRequestedHostOutputBufferSizeFrames );

CalculatePollingPeriodFrames(
stream->hostBufferSizeFrames, &pollingPeriodFrames,
sampleRate, framesPerBuffer );
}
else
{
CalculateBufferSettings( &stream->hostBufferSizeFrames, &pollingPeriodFrames,
/* isFullDuplex = */ (inputParameters && outputParameters),
suggestedInputLatencyFrames,
suggestedOutputLatencyFrames,
sampleRate, framesPerBuffer );
}

stream->pollingPeriodSeconds = pollingPeriodFrames / sampleRate;

DBUG(("DirectSound host buffer size frames: %d, polling period seconds: %f, @ sr: %f\n",
stream->hostBufferSizeFrames, stream->pollingPeriodSeconds, sampleRate ));


/* ------------------ OUTPUT */
if( outputParameters )
{
Expand Down
Loading

0 comments on commit 8b11ef3

Please sign in to comment.