PaHostApiTypeId hostApiType; /**< paDirectSound */\r
unsigned long version; /**< 2 */\r
\r
- unsigned long flags;\r
-\r
- /* low-level latency setting support\r
- Control the size of host buffers in order to set latency. They will \r
- be used instead of the generic parameters to Pa_OpenStream() if \r
- flags contains the paWinDirectSoundUseLowLevelLatencyParameters\r
- flag.\r
-\r
- If PaWinDirectSoundStreamInfo structures with paWinDirectSoundUseLowLevelLatencyParameters\r
- are supplied for both input and output in a full duplex stream, then the\r
- input and output framesPerBuffer must be the same, or the larger of the\r
- two must be a multiple of the smaller, otherwise a\r
- paIncompatibleHostApiSpecificStreamInfo error will be returned from\r
- Pa_OpenStream().\r
+ unsigned long flags; /**< enable other features of this struct */\r
+\r
+ /** \r
+ low-level latency setting support\r
+ Sets the size of the DirectSound host buffer.\r
+ When flags contains the paWinDirectSoundUseLowLevelLatencyParameters\r
+ this size will be used instead of interpreting the generic latency \r
+ parameters to Pa_OpenStream(). If the flag is not set this value is ignored.\r
+\r
+ If the stream is a full duplex stream the implementation requires that\r
+ the values of framesPerBuffer for input and output match (if both are specified).\r
*/\r
- unsigned long framesPerBuffer; /* NOT IMPLEMENTED see http://www.portaudio.com/trac/ticket/129 */\r
+ unsigned long framesPerBuffer;\r
\r
- /*\r
+ /**\r
support for WAVEFORMATEXTENSIBLE channel masks. If flags contains\r
paWinDirectSoundUseChannelMask this allows you to specify which speakers \r
to address in a multichannel stream. Constants for channelMask\r
#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)
{
return paIncompatibleHostApiSpecificStreamInfo;
}
+
+ if( streamInfo->flags & paWinDirectSoundUseLowLevelLatencyParameters )
+ {
+ if( streamInfo->framesPerBuffer <= 0 )
+ return paIncompatibleHostApiSpecificStreamInfo;
+
+ }
}
return paNoError;
#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;
}
-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;
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 )
{
}
+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,
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;
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
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
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:
/* 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 )
{
--- /dev/null
+/*\r
+ * $Id: $\r
+ * Portable Audio I/O Library\r
+ * Windows DirectSound low level buffer parameters test\r
+ *\r
+ * Copyright (c) 2011 Ross Bencina\r
+ *\r
+ * Permission is hereby granted, free of charge, to any person obtaining\r
+ * a copy of this software and associated documentation files\r
+ * (the "Software"), to deal in the Software without restriction,\r
+ * including without limitation the rights to use, copy, modify, merge,\r
+ * publish, distribute, sublicense, and/or sell copies of the Software,\r
+ * and to permit persons to whom the Software is furnished to do so,\r
+ * subject to the following conditions:\r
+ *\r
+ * The above copyright notice and this permission notice shall be\r
+ * included in all copies or substantial portions of the Software.\r
+ *\r
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.\r
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR\r
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF\r
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
+ */\r
+\r
+/*\r
+ * The text above constitutes the entire PortAudio license; however, \r
+ * the PortAudio community also makes the following non-binding requests:\r
+ *\r
+ * Any person wishing to distribute modifications to the Software is\r
+ * requested to send the modifications to the original developer so that\r
+ * they can be incorporated into the canonical version. It is also \r
+ * requested that these non-binding requests be included along with the \r
+ * license above.\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <math.h>\r
+\r
+#include "portaudio.h"\r
+#include "pa_win_ds.h"\r
+\r
+#define NUM_SECONDS (6)\r
+#define SAMPLE_RATE (44100)\r
+\r
+#define DSOUND_FRAMES_PER_HOST_BUFFER (256*2) //(440*10)\r
+\r
+#define FRAMES_PER_BUFFER 256\r
+\r
+#ifndef M_PI\r
+#define M_PI (3.14159265)\r
+#endif\r
+\r
+#define TABLE_SIZE (2048)\r
+\r
+#define CHANNEL_COUNT (2)\r
+\r
+\r
+typedef struct\r
+{\r
+ float sine[TABLE_SIZE];\r
+ double phase;\r
+}\r
+paTestData;\r
+\r
+/* This routine will be called by the PortAudio engine when audio is needed.\r
+** It may called at interrupt level on some machines so don't do anything\r
+** that could mess up the system like calling malloc() or free().\r
+*/\r
+static int patestCallback( const void *inputBuffer, void *outputBuffer,\r
+ unsigned long framesPerBuffer,\r
+ const PaStreamCallbackTimeInfo* timeInfo,\r
+ PaStreamCallbackFlags statusFlags,\r
+ void *userData )\r
+{\r
+ paTestData *data = (paTestData*)userData;\r
+ float *out = (float*)outputBuffer;\r
+ unsigned long i,j;\r
+\r
+ (void) timeInfo; /* Prevent unused variable warnings. */\r
+ (void) statusFlags;\r
+ (void) inputBuffer;\r
+ \r
+ for( i=0; i<framesPerBuffer; i++ )\r
+ {\r
+ float x = data->sine[(int)data->phase];\r
+ data->phase += 20;\r
+ if( data->phase >= TABLE_SIZE ){\r
+ data->phase -= TABLE_SIZE;\r
+ }\r
+\r
+ for( j = 0; j < CHANNEL_COUNT; ++j ){\r
+ *out++ = x;\r
+ }\r
+ }\r
+ \r
+ return paContinue;\r
+}\r
+\r
+/*******************************************************************/\r
+int main(int argc, char* argv[])\r
+{\r
+ PaStreamParameters outputParameters;\r
+ PaWinDirectSoundStreamInfo dsoundStreamInfo;\r
+ PaStream *stream;\r
+ PaError err;\r
+ paTestData data;\r
+ int i;\r
+ int deviceIndex;\r
+\r
+ printf("PortAudio Test: output a sine blip on each channel. SR = %d, BufSize = %d, Chans = %d\n", SAMPLE_RATE, FRAMES_PER_BUFFER, CHANNEL_COUNT);\r
+\r
+ err = Pa_Initialize();\r
+ if( err != paNoError ) goto error;\r
+\r
+ deviceIndex = Pa_GetHostApiInfo( Pa_HostApiTypeIdToHostApiIndex( paDirectSound ) )->defaultOutputDevice;\r
+ if( argc == 2 ){\r
+ sscanf( argv[1], "%d", &deviceIndex );\r
+ }\r
+\r
+ printf( "using device id %d (%s)\n", deviceIndex, Pa_GetDeviceInfo(deviceIndex)->name );\r
+\r
+ /* initialise sinusoidal wavetable */\r
+ for( i=0; i<TABLE_SIZE; i++ )\r
+ {\r
+ data.sine[i] = (float) sin( ((double)i/(double)TABLE_SIZE) * M_PI * 2. );\r
+ }\r
+\r
+ data.phase = 0;\r
+\r
+ outputParameters.device = deviceIndex;\r
+ outputParameters.channelCount = CHANNEL_COUNT;\r
+ outputParameters.sampleFormat = paFloat32; /* 32 bit floating point processing */\r
+ outputParameters.suggestedLatency = 0; /*Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;*/\r
+ outputParameters.hostApiSpecificStreamInfo = NULL;\r
+\r
+ dsoundStreamInfo.size = sizeof(PaWinDirectSoundStreamInfo);\r
+ dsoundStreamInfo.hostApiType = paDirectSound; \r
+ dsoundStreamInfo.version = 2;\r
+ dsoundStreamInfo.flags = paWinDirectSoundUseLowLevelLatencyParameters;\r
+ dsoundStreamInfo.framesPerBuffer = DSOUND_FRAMES_PER_HOST_BUFFER;\r
+ outputParameters.hostApiSpecificStreamInfo = &dsoundStreamInfo;\r
+ \r
+\r
+ if( Pa_IsFormatSupported( 0, &outputParameters, SAMPLE_RATE ) == paFormatIsSupported ){\r
+ printf( "Pa_IsFormatSupported reports device will support %d channels.\n", CHANNEL_COUNT );\r
+ }else{\r
+ printf( "Pa_IsFormatSupported reports device will not support %d channels.\n", CHANNEL_COUNT );\r
+ }\r
+\r
+ err = Pa_OpenStream(\r
+ &stream,\r
+ NULL, /* no input */\r
+ &outputParameters,\r
+ SAMPLE_RATE,\r
+ FRAMES_PER_BUFFER,\r
+ paClipOff, /* we won't output out of range samples so don't bother clipping them */\r
+ patestCallback,\r
+ &data );\r
+ if( err != paNoError ) goto error;\r
+\r
+ err = Pa_StartStream( stream );\r
+ if( err != paNoError ) goto error;\r
+\r
+ printf("Play for %d seconds.\n", NUM_SECONDS );\r
+ Pa_Sleep( NUM_SECONDS * 1000 );\r
+\r
+ err = Pa_StopStream( stream );\r
+ if( err != paNoError ) goto error;\r
+\r
+ err = Pa_CloseStream( stream );\r
+ if( err != paNoError ) goto error;\r
+\r
+ Pa_Terminate();\r
+ printf("Test finished.\n");\r
+ \r
+ return err;\r
+error:\r
+ Pa_Terminate();\r
+ fprintf( stderr, "An error occured while using the portaudio stream\n" );\r
+ fprintf( stderr, "Error number: %d\n", err );\r
+ fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );\r
+ return err;\r
+}\r
+\r