mirror of https://github.com/PCSX2/pcsx2.git
portaudio: Update codebase from svn 1723 to svn 1748.
Notable changes: - fixed latency calculations for some modules - improved stability of the internal ringbuffer - added float32<->uint8 sample conversion git-svn-id: http://pcsx2.googlecode.com/svn/trunk@4911 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
c68bb1bb7f
commit
4e762692da
|
@ -59,15 +59,14 @@ extern "C"
|
|||
typedef struct PaWinDirectSoundStreamInfo{
|
||||
unsigned long size; /**< sizeof(PaWinDirectSoundStreamInfo) */
|
||||
PaHostApiTypeId hostApiType; /**< paDirectSound */
|
||||
unsigned long version; /**< 1 */
|
||||
unsigned long version; /**< 2 */
|
||||
|
||||
unsigned long flags;
|
||||
|
||||
/* low-level latency setting support
|
||||
TODO ** NOT IMPLEMENTED **
|
||||
These settings control the number and 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
|
||||
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
|
||||
|
@ -76,9 +75,8 @@ typedef struct PaWinDirectSoundStreamInfo{
|
|||
two must be a multiple of the smaller, otherwise a
|
||||
paIncompatibleHostApiSpecificStreamInfo error will be returned from
|
||||
Pa_OpenStream().
|
||||
|
||||
unsigned long framesPerBuffer;
|
||||
*/
|
||||
unsigned long framesPerBuffer; /* NOT IMPLEMENTED see http://www.portaudio.com/trac/ticket/129 */
|
||||
|
||||
/*
|
||||
support for WAVEFORMATEXTENSIBLE channel masks. If flags contains
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef PORTAUDIO_H
|
||||
#define PORTAUDIO_H
|
||||
/*
|
||||
* $Id: portaudio.h 1702 2011-07-20 03:24:00Z rossb $
|
||||
* $Id: portaudio.h 1745 2011-08-25 17:44:01Z rossb $
|
||||
* PortAudio Portable Real-Time Audio Library
|
||||
* PortAudio API Header File
|
||||
* Latest version available at: http://www.portaudio.com/
|
||||
|
@ -711,6 +711,28 @@ typedef enum PaStreamCallbackResult
|
|||
Functions of type PaStreamCallback are implemented by PortAudio clients.
|
||||
They consume, process or generate audio in response to requests from an
|
||||
active PortAudio stream.
|
||||
|
||||
When a stream is running, PortAudio calls the stream callback periodically.
|
||||
The callback function is responsible for processing buffers of audio samples
|
||||
passed via the input and output parameters.
|
||||
|
||||
The PortAudio stream callback runs at very high or real-time priority.
|
||||
It is required to consistently meet its time deadlines. Do not allocate
|
||||
memory, access the file system, call library functions or call other functions
|
||||
from the stream callback that may block or take an unpredictable amount of
|
||||
time to complete.
|
||||
|
||||
In order for a stream to maintain glitch-free operation the callback
|
||||
must consume and return audio data faster than it is recorded and/or
|
||||
played. PortAudio anticipates that each callback invocation may execute for
|
||||
a duration approaching the duration of frameCount audio frames at the stream
|
||||
sample rate. It is reasonable to expect to be able to utilise 70% or more of
|
||||
the available CPU time in the PortAudio callback. However, due to buffer size
|
||||
adaption and other factors, not all host APIs are able to guarantee audio
|
||||
stability under heavy CPU load with arbitrary fixed callback buffer sizes.
|
||||
When high callback CPU utilisation is required the most robust behavior
|
||||
can be achieved by using paFramesPerBufferUnspecified as the
|
||||
Pa_OpenStream() framesPerBuffer parameter.
|
||||
|
||||
@param input and @param output are either arrays of interleaved samples or;
|
||||
if non-interleaved samples were requested using the paNonInterleaved sample
|
||||
|
@ -737,7 +759,7 @@ typedef enum PaStreamCallbackResult
|
|||
|
||||
@return
|
||||
The stream callback should return one of the values in the
|
||||
PaStreamCallbackResult enumeration. To ensure that the callback continues
|
||||
::PaStreamCallbackResult enumeration. To ensure that the callback continues
|
||||
to be called, it should return paContinue (0). Either paComplete or paAbort
|
||||
can be returned to finish stream processing, after either of these values is
|
||||
returned the callback will not be called again. If paAbort is returned the
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_converters.c 1576 2011-02-01 12:58:26Z rossb $
|
||||
* $Id: pa_converters.c 1748 2011-09-01 22:08:32Z philburk $
|
||||
* Portable Audio I/O Library sample conversion mechanism
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
|
@ -49,8 +49,7 @@
|
|||
see: "require clipping for dithering sample conversion functions?"
|
||||
http://www.portaudio.com/trac/ticket/112
|
||||
|
||||
@todo implement the converters marked IMPLEMENT ME: Float32_To_UInt8_Dither,
|
||||
Float32_To_UInt8_Clip, Float32_To_UInt8_DitherClip, Int32_To_Int24_Dither,
|
||||
@todo implement the converters marked IMPLEMENT ME: Int32_To_Int24_Dither,
|
||||
Int32_To_UInt8_Dither, Int24_To_Int16_Dither, Int24_To_Int8_Dither,
|
||||
Int24_To_UInt8_Dither, Int16_To_Int8_Dither, Int16_To_UInt8_Dither
|
||||
see: "some conversion functions are not implemented in pa_converters.c"
|
||||
|
@ -729,8 +728,7 @@ static void Float32_To_Int8_Dither(
|
|||
{
|
||||
float *src = (float*)sourceBuffer;
|
||||
signed char *dest = (signed char*)destinationBuffer;
|
||||
(void)ditherGenerator; /* unused parameter */
|
||||
|
||||
|
||||
while( count-- )
|
||||
{
|
||||
float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
|
||||
|
@ -821,12 +819,15 @@ static void Float32_To_UInt8_Dither(
|
|||
{
|
||||
float *src = (float*)sourceBuffer;
|
||||
unsigned char *dest = (unsigned char*)destinationBuffer;
|
||||
(void)ditherGenerator; /* unused parameter */
|
||||
|
||||
|
||||
while( count-- )
|
||||
{
|
||||
/* IMPLEMENT ME */
|
||||
|
||||
float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
|
||||
/* use smaller scaler to prevent overflow when we add the dither */
|
||||
float dithered = (*src * (126.0f)) + dither;
|
||||
PaInt32 samp = (PaInt32) dithered;
|
||||
*dest = (unsigned char) (128 + samp);
|
||||
|
||||
src += sourceStride;
|
||||
dest += destinationStride;
|
||||
}
|
||||
|
@ -845,7 +846,9 @@ static void Float32_To_UInt8_Clip(
|
|||
|
||||
while( count-- )
|
||||
{
|
||||
/* IMPLEMENT ME */
|
||||
PaInt32 samp = 128 + (PaInt32)(*src * (127.0f));
|
||||
PA_CLIP_( samp, 0x0000, 0x00FF );
|
||||
*dest = (unsigned char) samp;
|
||||
|
||||
src += sourceStride;
|
||||
dest += destinationStride;
|
||||
|
@ -865,7 +868,12 @@ static void Float32_To_UInt8_DitherClip(
|
|||
|
||||
while( count-- )
|
||||
{
|
||||
/* IMPLEMENT ME */
|
||||
float dither = PaUtil_GenerateFloatTriangularDither( ditherGenerator );
|
||||
/* use smaller scaler to prevent overflow when we add the dither */
|
||||
float dithered = (*src * (126.0f)) + dither;
|
||||
PaInt32 samp = 128 + (PaInt32) dithered;
|
||||
PA_CLIP_( samp, 0x0000, 0x00FF );
|
||||
*dest = (unsigned char) samp;
|
||||
|
||||
src += sourceStride;
|
||||
dest += destinationStride;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_front.c 1584 2011-02-02 18:58:17Z rossb $
|
||||
* $Id: pa_front.c 1730 2011-08-18 03:43:51Z rossb $
|
||||
* Portable Audio I/O Library Multi-Host API front end
|
||||
* Validate function parameters and manage multiple host APIs.
|
||||
*
|
||||
|
@ -116,6 +116,7 @@ void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
|
|||
|
||||
static PaUtilHostApiRepresentation **hostApis_ = 0;
|
||||
static int hostApisCount_ = 0;
|
||||
static int defaultHostApiIndex_ = 0;
|
||||
static int initializationCount_ = 0;
|
||||
static int deviceCount_ = 0;
|
||||
|
||||
|
@ -146,6 +147,7 @@ static void TerminateHostApis( void )
|
|||
hostApis_[hostApisCount_]->Terminate( hostApis_[hostApisCount_] );
|
||||
}
|
||||
hostApisCount_ = 0;
|
||||
defaultHostApiIndex_ = 0;
|
||||
deviceCount_ = 0;
|
||||
|
||||
if( hostApis_ != 0 )
|
||||
|
@ -172,6 +174,7 @@ static PaError InitializeHostApis( void )
|
|||
}
|
||||
|
||||
hostApisCount_ = 0;
|
||||
defaultHostApiIndex_ = -1; /* indicates that we haven't determined the default host API yet */
|
||||
deviceCount_ = 0;
|
||||
baseDeviceIndex = 0;
|
||||
|
||||
|
@ -193,6 +196,16 @@ static PaError InitializeHostApis( void )
|
|||
assert( hostApi->info.defaultInputDevice < hostApi->info.deviceCount );
|
||||
assert( hostApi->info.defaultOutputDevice < hostApi->info.deviceCount );
|
||||
|
||||
/* the first successfully initialized host API with a default input *or*
|
||||
output device is used as the default host API.
|
||||
*/
|
||||
if( (defaultHostApiIndex_ == -1) &&
|
||||
( hostApi->info.defaultInputDevice != paNoDevice
|
||||
|| hostApi->info.defaultOutputDevice != paNoDevice ) )
|
||||
{
|
||||
defaultHostApiIndex_ = hostApisCount_;
|
||||
}
|
||||
|
||||
hostApi->privatePaFrontInfo.baseDeviceIndex = baseDeviceIndex;
|
||||
|
||||
if( hostApi->info.defaultInputDevice != paNoDevice )
|
||||
|
@ -208,6 +221,10 @@ static PaError InitializeHostApis( void )
|
|||
}
|
||||
}
|
||||
|
||||
/* if no host APIs have devices, the default host API is the first initialized host API */
|
||||
if( defaultHostApiIndex_ == -1 )
|
||||
defaultHostApiIndex_ = 0;
|
||||
|
||||
return result;
|
||||
|
||||
error:
|
||||
|
@ -525,7 +542,7 @@ PaHostApiIndex Pa_GetDefaultHostApi( void )
|
|||
}
|
||||
else
|
||||
{
|
||||
result = paDefaultHostApiIndex;
|
||||
result = defaultHostApiIndex_;
|
||||
|
||||
/* internal consistency check: make sure that the default host api
|
||||
index is within range */
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef PA_HOSTAPI_H
|
||||
#define PA_HOSTAPI_H
|
||||
/*
|
||||
* $Id: pa_hostapi.h 1634 2011-03-04 04:12:08Z rossb $
|
||||
* $Id: pa_hostapi.h 1740 2011-08-25 07:17:48Z philburk $
|
||||
* Portable Audio I/O Library
|
||||
* host api representation
|
||||
*
|
||||
|
@ -106,6 +106,53 @@ are defaulted to 1.
|
|||
#define PA_USE_WDMKS 1
|
||||
#endif
|
||||
|
||||
/* Set default values for Unix based APIs. */
|
||||
#if defined(PA_NO_OSS) || defined(PA_NO_ALSA) || defined(PA_NO_JACK) || defined(PA_NO_COREAUDIO) || defined(PA_NO_SGI) || defined(PA_NO_ASIHPI)
|
||||
#error "Portaudio: PA_NO_<APINAME> is no longer supported, please remove definition and use PA_USE_<APINAME> instead"
|
||||
#endif
|
||||
|
||||
#ifndef PA_USE_OSS
|
||||
#define PA_USE_OSS 0
|
||||
#elif (PA_USE_OSS != 0) && (PA_USE_OSS != 1)
|
||||
#undef PA_USE_OSS
|
||||
#define PA_USE_OSS 1
|
||||
#endif
|
||||
|
||||
#ifndef PA_USE_ALSA
|
||||
#define PA_USE_ALSA 0
|
||||
#elif (PA_USE_ALSA != 0) && (PA_USE_ALSA != 1)
|
||||
#undef PA_USE_ALSA
|
||||
#define PA_USE_ALSA 1
|
||||
#endif
|
||||
|
||||
#ifndef PA_USE_JACK
|
||||
#define PA_USE_JACK 0
|
||||
#elif (PA_USE_JACK != 0) && (PA_USE_JACK != 1)
|
||||
#undef PA_USE_JACK
|
||||
#define PA_USE_JACK 1
|
||||
#endif
|
||||
|
||||
#ifndef PA_USE_SGI
|
||||
#define PA_USE_SGI 0
|
||||
#elif (PA_USE_SGI != 0) && (PA_USE_SGI != 1)
|
||||
#undef PA_USE_SGI
|
||||
#define PA_USE_SGI 1
|
||||
#endif
|
||||
|
||||
#ifndef PA_USE_COREAUDIO
|
||||
#define PA_USE_COREAUDIO 0
|
||||
#elif (PA_USE_COREAUDIO != 0) && (PA_USE_COREAUDIO != 1)
|
||||
#undef PA_USE_COREAUDIO
|
||||
#define PA_USE_COREAUDIO 1
|
||||
#endif
|
||||
|
||||
#ifndef PA_USE_ASIHPI
|
||||
#define PA_USE_ASIHPI 0
|
||||
#elif (PA_USE_ASIHPI != 0) && (PA_USE_ASIHPI != 1)
|
||||
#undef PA_USE_ASIHPI
|
||||
#define PA_USE_ASIHPI 1
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
|
@ -294,22 +341,21 @@ typedef PaError PaUtilHostApiInitializer( PaUtilHostApiRepresentation**, PaHostA
|
|||
|
||||
/** paHostApiInitializers is a NULL-terminated array of host API initialization
|
||||
functions. These functions are called by pa_front.c to initialize the host APIs
|
||||
when the client calls Pa_Initialize().
|
||||
when the client calls Pa_Initialize().
|
||||
|
||||
The initialization functions are invoked in order.
|
||||
|
||||
There is a platform specific file which defines paHostApiInitializers for that
|
||||
The first successfully initialized host API that has a default input *or* output
|
||||
device is used as the default PortAudio host API. This is based on the logic that
|
||||
there is only one default host API, and it must contain the default input and output
|
||||
devices (if defined).
|
||||
|
||||
There is a platform specific file that defines paHostApiInitializers for that
|
||||
platform, pa_win/pa_win_hostapis.c contains the Win32 definitions for example.
|
||||
*/
|
||||
extern PaUtilHostApiInitializer *paHostApiInitializers[];
|
||||
|
||||
|
||||
/** The index of the default host API in the paHostApiInitializers array.
|
||||
|
||||
There is a platform specific file which defines paDefaultHostApiIndex for that
|
||||
platform, see pa_win/pa_win_hostapis.c for example.
|
||||
*/
|
||||
extern int paDefaultHostApiIndex;
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
|
|
@ -103,6 +103,7 @@
|
|||
# pragma intrinsic(_ReadWriteBarrier)
|
||||
# pragma intrinsic(_ReadBarrier)
|
||||
# pragma intrinsic(_WriteBarrier)
|
||||
/* note that MSVC intrinsics _ReadWriteBarrier(), _ReadBarrier(), _WriteBarrier() are just compiler barriers *not* memory barriers */
|
||||
# define PaUtil_FullMemoryBarrier() _ReadWriteBarrier()
|
||||
# define PaUtil_ReadMemoryBarrier() _ReadBarrier()
|
||||
# define PaUtil_WriteMemoryBarrier() _WriteBarrier()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_ringbuffer.c 1694 2011-06-17 08:01:02Z rossb $
|
||||
* $Id: pa_ringbuffer.c 1738 2011-08-18 11:47:28Z rossb $
|
||||
* Portable Audio I/O Library
|
||||
* Ring Buffer utility.
|
||||
*
|
||||
|
@ -79,14 +79,12 @@ ring_buffer_size_t PaUtil_InitializeRingBuffer( PaUtilRingBuffer *rbuf, ring_buf
|
|||
** Return number of elements available for reading. */
|
||||
ring_buffer_size_t PaUtil_GetRingBufferReadAvailable( const PaUtilRingBuffer *rbuf )
|
||||
{
|
||||
PaUtil_ReadMemoryBarrier();
|
||||
return ( (rbuf->writeIndex - rbuf->readIndex) & rbuf->bigMask );
|
||||
}
|
||||
/***************************************************************************
|
||||
** Return number of elements available for writing. */
|
||||
ring_buffer_size_t PaUtil_GetRingBufferWriteAvailable( const PaUtilRingBuffer *rbuf )
|
||||
{
|
||||
/* Since we are calling PaUtil_GetRingBufferReadAvailable, we don't need an aditional MB */
|
||||
return ( rbuf->bufferSize - PaUtil_GetRingBufferReadAvailable(rbuf));
|
||||
}
|
||||
|
||||
|
@ -128,6 +126,10 @@ ring_buffer_size_t PaUtil_GetRingBufferWriteRegions( PaUtilRingBuffer *rbuf, rin
|
|||
*dataPtr2 = NULL;
|
||||
*sizePtr2 = 0;
|
||||
}
|
||||
|
||||
if( available )
|
||||
PaUtil_FullMemoryBarrier(); /* (write-after-read) => full barrier */
|
||||
|
||||
return elementCount;
|
||||
}
|
||||
|
||||
|
@ -136,7 +138,9 @@ ring_buffer_size_t PaUtil_GetRingBufferWriteRegions( PaUtilRingBuffer *rbuf, rin
|
|||
*/
|
||||
ring_buffer_size_t PaUtil_AdvanceRingBufferWriteIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount )
|
||||
{
|
||||
/* we need to ensure that previous writes are seen before we update the write index */
|
||||
/* ensure that previous writes are seen before we update the write index
|
||||
(write after write)
|
||||
*/
|
||||
PaUtil_WriteMemoryBarrier();
|
||||
return rbuf->writeIndex = (rbuf->writeIndex + elementCount) & rbuf->bigMask;
|
||||
}
|
||||
|
@ -152,7 +156,7 @@ ring_buffer_size_t PaUtil_GetRingBufferReadRegions( PaUtilRingBuffer *rbuf, ring
|
|||
void **dataPtr2, ring_buffer_size_t *sizePtr2 )
|
||||
{
|
||||
ring_buffer_size_t index;
|
||||
ring_buffer_size_t available = PaUtil_GetRingBufferReadAvailable( rbuf );
|
||||
ring_buffer_size_t available = PaUtil_GetRingBufferReadAvailable( rbuf ); /* doesn't use memory barrier */
|
||||
if( elementCount > available ) elementCount = available;
|
||||
/* Check to see if read is not contiguous. */
|
||||
index = rbuf->readIndex & rbuf->smallMask;
|
||||
|
@ -172,14 +176,20 @@ ring_buffer_size_t PaUtil_GetRingBufferReadRegions( PaUtilRingBuffer *rbuf, ring
|
|||
*dataPtr2 = NULL;
|
||||
*sizePtr2 = 0;
|
||||
}
|
||||
|
||||
if( available )
|
||||
PaUtil_ReadMemoryBarrier(); /* (read-after-read) => read barrier */
|
||||
|
||||
return elementCount;
|
||||
}
|
||||
/***************************************************************************
|
||||
*/
|
||||
ring_buffer_size_t PaUtil_AdvanceRingBufferReadIndex( PaUtilRingBuffer *rbuf, ring_buffer_size_t elementCount )
|
||||
{
|
||||
/* we need to ensure that previous writes are always seen before updating the index. */
|
||||
PaUtil_WriteMemoryBarrier();
|
||||
/* ensure that previous reads (copies out of the ring buffer) are always completed before updating (writing) the read index.
|
||||
(write-after-read) => full barrier
|
||||
*/
|
||||
PaUtil_FullMemoryBarrier();
|
||||
return rbuf->readIndex = (rbuf->readIndex + elementCount) & rbuf->bigMask;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef PA_RINGBUFFER_H
|
||||
#define PA_RINGBUFFER_H
|
||||
/*
|
||||
* $Id: pa_ringbuffer.h 1694 2011-06-17 08:01:02Z rossb $
|
||||
* $Id: pa_ringbuffer.h 1734 2011-08-18 11:19:36Z rossb $
|
||||
* Portable Audio I/O Library
|
||||
* Ring Buffer utility.
|
||||
*
|
||||
|
@ -90,8 +90,8 @@ extern "C"
|
|||
typedef struct PaUtilRingBuffer
|
||||
{
|
||||
ring_buffer_size_t bufferSize; /**< Number of elements in FIFO. Power of 2. Set by PaUtil_InitRingBuffer. */
|
||||
ring_buffer_size_t writeIndex; /**< Index of next writable element. Set by PaUtil_AdvanceRingBufferWriteIndex. */
|
||||
ring_buffer_size_t readIndex; /**< Index of next readable element. Set by PaUtil_AdvanceRingBufferReadIndex. */
|
||||
volatile ring_buffer_size_t writeIndex; /**< Index of next writable element. Set by PaUtil_AdvanceRingBufferWriteIndex. */
|
||||
volatile ring_buffer_size_t readIndex; /**< Index of next readable element. Set by PaUtil_AdvanceRingBufferReadIndex. */
|
||||
ring_buffer_size_t bigMask; /**< Used for wrapping indices with extra bit to distinguish full/empty. */
|
||||
ring_buffer_size_t smallMask; /**< Used for fitting indices to buffer. */
|
||||
ring_buffer_size_t elementSizeBytes; /**< Number of bytes per element. */
|
||||
|
|
|
@ -1009,6 +1009,10 @@ static OSStatus UpdateSampleRateFromDeviceProperty( PaMacCoreStream *stream, Aud
|
|||
static OSStatus AudioDevicePropertyActualSampleRateListenerProc( AudioDeviceID inDevice, UInt32 inChannel, Boolean isInput, AudioDevicePropertyID inPropertyID, void *inClientData )
|
||||
{
|
||||
PaMacCoreStream *stream = (PaMacCoreStream*)inClientData;
|
||||
|
||||
// Make sure the callback is operating on a stream that is still valid!
|
||||
assert( stream->streamRepresentation.magic == PA_STREAM_MAGIC );
|
||||
|
||||
OSStatus osErr = UpdateSampleRateFromDeviceProperty( stream, inDevice, isInput );
|
||||
if( osErr == noErr )
|
||||
{
|
||||
|
@ -1034,6 +1038,10 @@ static OSStatus AudioDevicePropertyGenericListenerProc( AudioDeviceID inDevice,
|
|||
{
|
||||
OSStatus osErr = noErr;
|
||||
PaMacCoreStream *stream = (PaMacCoreStream*)inClientData;
|
||||
|
||||
// Make sure the callback is operating on a stream that is still valid!
|
||||
assert( stream->streamRepresentation.magic == PA_STREAM_MAGIC );
|
||||
|
||||
PaMacCoreDeviceProperties *deviceProperties = isInput ? &stream->inputProperties : &stream->outputProperties;
|
||||
UInt32 *valuePtr = NULL;
|
||||
switch( inPropertyID )
|
||||
|
@ -2107,7 +2115,7 @@ static OSStatus AudioIOProc( void *inRefCon,
|
|||
PaMacCoreStream *stream = (PaMacCoreStream*)inRefCon;
|
||||
const bool isRender = inBusNumber == OUTPUT_ELEMENT;
|
||||
int callbackResult = paContinue ;
|
||||
double hostTimeStampInPaTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime);
|
||||
double hostTimeStampInPaTime = HOST_TIME_TO_PA_TIME(inTimeStamp->mHostTime);
|
||||
|
||||
VVDBUG(("AudioIOProc()\n"));
|
||||
|
||||
|
@ -2546,7 +2554,7 @@ static PaError CloseStream( PaStream* s )
|
|||
|
||||
if( stream->inputUnit )
|
||||
{
|
||||
Boolean isInput = FALSE;
|
||||
Boolean isInput = TRUE;
|
||||
CleanupDevicePropertyListeners( stream, stream->inputDevice, isInput );
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_win_ds.c 1685 2011-05-11 21:05:18Z rossb $
|
||||
* $Id: pa_win_ds.c 1744 2011-08-25 15:59:32Z rossb $
|
||||
* Portable Audio I/O Library DirectSound implementation
|
||||
*
|
||||
* Authors: Phil Burk, Robert Marsanyi & Ross Bencina
|
||||
|
@ -45,7 +45,7 @@
|
|||
We're replacing this with a new implementation using a thread and a different timer mechanim.
|
||||
Defining PA_WIN_DS_USE_WMME_TIMER uses the old (pre-May 2011) behavior.
|
||||
*/
|
||||
//#define PA_WIN_DS_USE_WMME_TIMER
|
||||
//#define PA_WIN_DS_USE_WMME_TIMER
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
@ -56,6 +56,7 @@
|
|||
#include <windows.h>
|
||||
#include <objbase.h>
|
||||
|
||||
|
||||
/*
|
||||
Use the earliest version of DX required, no need to polute the namespace
|
||||
*/
|
||||
|
@ -142,17 +143,17 @@ PA_THREAD_FUNC ProcessingThreadProc( void *pArg );
|
|||
|
||||
#define PA_USE_HIGH_LATENCY (0)
|
||||
#if PA_USE_HIGH_LATENCY
|
||||
#define PA_WIN_9X_LATENCY (500)
|
||||
#define PA_WIN_NT_LATENCY (600)
|
||||
#define PA_DS_WIN_9X_DEFAULT_LATENCY_ (.500)
|
||||
#define PA_DS_WIN_NT_DEFAULT_LATENCY_ (.600)
|
||||
#else
|
||||
#define PA_WIN_9X_LATENCY (140)
|
||||
#define PA_WIN_NT_LATENCY (280)
|
||||
#define PA_DS_WIN_9X_DEFAULT_LATENCY_ (.140)
|
||||
#define PA_DS_WIN_NT_DEFAULT_LATENCY_ (.280)
|
||||
#endif
|
||||
|
||||
#define PA_WIN_WDM_LATENCY (120)
|
||||
#define PA_DS_WIN_WDM_DEFAULT_LATENCY_ (.120)
|
||||
|
||||
#define SECONDS_PER_MSEC (0.001)
|
||||
#define MSEC_PER_SECOND (1000)
|
||||
#define MSECS_PER_SECOND (1000)
|
||||
|
||||
/* prototypes for functions declared in this file */
|
||||
|
||||
|
@ -254,7 +255,7 @@ typedef struct PaWinDsStream
|
|||
LPDIRECTSOUNDBUFFER pDirectSoundOutputBuffer;
|
||||
DWORD outputBufferWriteOffsetBytes; /* last write position */
|
||||
INT outputBufferSizeBytes;
|
||||
INT bytesPerOutputFrame;
|
||||
INT outputFrameSizeBytes;
|
||||
/* Try to detect play buffer underflows. */
|
||||
LARGE_INTEGER perfCounterTicksPerBuffer; /* counter ticks it should take to play a full buffer */
|
||||
LARGE_INTEGER previousPlayTime;
|
||||
|
@ -266,14 +267,15 @@ typedef struct PaWinDsStream
|
|||
/* Input */
|
||||
LPDIRECTSOUNDCAPTURE pDirectSoundCapture;
|
||||
LPDIRECTSOUNDCAPTUREBUFFER pDirectSoundInputBuffer;
|
||||
INT bytesPerInputFrame;
|
||||
INT inputFrameSizeBytes;
|
||||
UINT readOffset; /* last read position */
|
||||
UINT inputSize;
|
||||
UINT inputBufferSizeBytes;
|
||||
|
||||
|
||||
int framesPerDSBuffer;
|
||||
int hostBufferSizeFrames; /* input and output host ringbuffers have the same number of frames */
|
||||
double framesWritten;
|
||||
double secondsPerHostByte; /* Used to optimize latency calculation for outTime */
|
||||
double pollingPeriodSeconds;
|
||||
|
||||
PaStreamCallbackFlags callbackFlags;
|
||||
|
||||
|
@ -304,6 +306,77 @@ typedef struct PaWinDsStream
|
|||
} PaWinDsStream;
|
||||
|
||||
|
||||
/* Set minimal latency based on the current OS version.
|
||||
* NT has higher latency.
|
||||
*/
|
||||
static double PaWinDS_GetMinSystemLatencySeconds( void )
|
||||
{
|
||||
double minLatencySeconds;
|
||||
/* Set minimal latency based on whether NT or other OS.
|
||||
* NT has higher latency.
|
||||
*/
|
||||
OSVERSIONINFO osvi;
|
||||
osvi.dwOSVersionInfoSize = sizeof( osvi );
|
||||
GetVersionEx( &osvi );
|
||||
DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
|
||||
DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
|
||||
DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
|
||||
/* Check for NT */
|
||||
if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
|
||||
{
|
||||
minLatencySeconds = PA_DS_WIN_NT_DEFAULT_LATENCY_;
|
||||
}
|
||||
else if(osvi.dwMajorVersion >= 5)
|
||||
{
|
||||
minLatencySeconds = PA_DS_WIN_WDM_DEFAULT_LATENCY_;
|
||||
}
|
||||
else
|
||||
{
|
||||
minLatencySeconds = PA_DS_WIN_9X_DEFAULT_LATENCY_;
|
||||
}
|
||||
return minLatencySeconds;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
** 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.
|
||||
** For example, to set latency to 200 msec, put:
|
||||
**
|
||||
** set PA_MIN_LATENCY_MSEC=200
|
||||
**
|
||||
** in the AUTOEXEC.BAT file and reboot.
|
||||
** If the environment variable is not set, then the latency will be determined
|
||||
** based on the OS. Windows NT has higher latency than Win95.
|
||||
*/
|
||||
#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
|
||||
#define PA_ENV_BUF_SIZE (32)
|
||||
|
||||
static double PaWinDs_GetMinLatencySeconds( double sampleRate )
|
||||
{
|
||||
char envbuf[PA_ENV_BUF_SIZE];
|
||||
DWORD hresult;
|
||||
double minLatencySeconds = 0;
|
||||
|
||||
/* Let user determine minimal latency by setting environment variable. */
|
||||
hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
|
||||
if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
|
||||
{
|
||||
minLatencySeconds = atoi( envbuf ) * SECONDS_PER_MSEC;
|
||||
}
|
||||
else
|
||||
{
|
||||
minLatencySeconds = PaWinDS_GetMinSystemLatencySeconds();
|
||||
#if PA_USE_HIGH_LATENCY
|
||||
PRINT(("PA - Minimum Latency set to %f msec!\n", minLatencySeconds * MSECS_PER_SECOND ));
|
||||
#endif
|
||||
}
|
||||
|
||||
return minLatencySeconds;
|
||||
}
|
||||
|
||||
|
||||
/************************************************************************************
|
||||
** Duplicate the input string using the allocations allocator.
|
||||
** A NULL string is converted to a zero length string.
|
||||
|
@ -482,6 +555,7 @@ static BOOL CALLBACK CollectGUIDsProc(LPGUID lpGUID,
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
|
||||
|
||||
static void *DuplicateWCharString( PaUtilAllocationGroup *allocations, wchar_t *source )
|
||||
|
@ -816,11 +890,6 @@ static PaError AddOutputDeviceInfoFromDirectSound(
|
|||
}
|
||||
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
|
||||
|
||||
deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
|
||||
deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
|
||||
deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */
|
||||
deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */
|
||||
|
||||
/* initialize defaultSampleRate */
|
||||
|
||||
if( caps.dwFlags & DSCAPS_CONTINUOUSRATE )
|
||||
|
@ -847,7 +916,7 @@ static PaError AddOutputDeviceInfoFromDirectSound(
|
|||
** But it supports continuous sampling.
|
||||
** So fake range of rates, and hope it really supports it.
|
||||
*/
|
||||
deviceInfo->defaultSampleRate = 44100.0f;
|
||||
deviceInfo->defaultSampleRate = 48000.0f; /* assume 48000 as the default */
|
||||
|
||||
DBUG(("PA - Reported rates both zero. Setting to fake values for device #%s\n", name ));
|
||||
}
|
||||
|
@ -862,14 +931,19 @@ static PaError AddOutputDeviceInfoFromDirectSound(
|
|||
** But we know that they really support a range of rates!
|
||||
** So when we see a ridiculous set of rates, assume it is a range.
|
||||
*/
|
||||
deviceInfo->defaultSampleRate = 44100.0f;
|
||||
deviceInfo->defaultSampleRate = 48000.0f; /* assume 48000 as the default */
|
||||
DBUG(("PA - Sample rate range used instead of two odd values for device #%s\n", name ));
|
||||
}
|
||||
else deviceInfo->defaultSampleRate = caps.dwMaxSecondarySampleRate;
|
||||
|
||||
|
||||
//printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
|
||||
// dwFlags | DSCAPS_CONTINUOUSRATE
|
||||
|
||||
deviceInfo->defaultLowInputLatency = 0.;
|
||||
deviceInfo->defaultHighInputLatency = 0.;
|
||||
|
||||
deviceInfo->defaultLowOutputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
|
||||
deviceInfo->defaultHighOutputLatency = deviceInfo->defaultLowOutputLatency * 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -978,11 +1052,6 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
|
|||
}
|
||||
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
|
||||
|
||||
deviceInfo->defaultLowInputLatency = 0.; /** @todo IMPLEMENT ME */
|
||||
deviceInfo->defaultLowOutputLatency = 0.; /** @todo IMPLEMENT ME */
|
||||
deviceInfo->defaultHighInputLatency = 0.; /** @todo IMPLEMENT ME */
|
||||
deviceInfo->defaultHighOutputLatency = 0.; /** @todo IMPLEMENT ME */
|
||||
|
||||
/* constants from a WINE patch by Francois Gouget, see:
|
||||
http://www.winehq.com/hypermail/wine-patches/2003/01/0290.html
|
||||
|
||||
|
@ -1024,7 +1093,7 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
|
|||
else if( caps.dwFormats & WAVE_FORMAT_96S16 )
|
||||
deviceInfo->defaultSampleRate = 96000.0;
|
||||
else
|
||||
deviceInfo->defaultSampleRate = 0.;
|
||||
deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
|
||||
}
|
||||
else if( caps.dwChannels == 1 )
|
||||
{
|
||||
|
@ -1039,9 +1108,15 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
|
|||
else if( caps.dwFormats & WAVE_FORMAT_96M16 )
|
||||
deviceInfo->defaultSampleRate = 96000.0;
|
||||
else
|
||||
deviceInfo->defaultSampleRate = 0.;
|
||||
deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
|
||||
}
|
||||
else deviceInfo->defaultSampleRate = 0.;
|
||||
else deviceInfo->defaultSampleRate = 48000.0; /* assume 48000 as the default */
|
||||
|
||||
deviceInfo->defaultLowInputLatency = PaWinDs_GetMinLatencySeconds( deviceInfo->defaultSampleRate );
|
||||
deviceInfo->defaultHighInputLatency = deviceInfo->defaultLowInputLatency * 2;
|
||||
|
||||
deviceInfo->defaultLowOutputLatency = 0.;
|
||||
deviceInfo->defaultHighOutputLatency = 0.;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1250,38 +1325,6 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
|
|||
PaWinDs_TerminateDSoundEntryPoints();
|
||||
}
|
||||
|
||||
|
||||
/* Set minimal latency based on whether NT or Win95.
|
||||
* NT has higher latency.
|
||||
*/
|
||||
static int PaWinDS_GetMinSystemLatency( void )
|
||||
{
|
||||
int minLatencyMsec;
|
||||
/* Set minimal latency based on whether NT or other OS.
|
||||
* NT has higher latency.
|
||||
*/
|
||||
OSVERSIONINFO osvi;
|
||||
osvi.dwOSVersionInfoSize = sizeof( osvi );
|
||||
GetVersionEx( &osvi );
|
||||
DBUG(("PA - PlatformId = 0x%x\n", osvi.dwPlatformId ));
|
||||
DBUG(("PA - MajorVersion = 0x%x\n", osvi.dwMajorVersion ));
|
||||
DBUG(("PA - MinorVersion = 0x%x\n", osvi.dwMinorVersion ));
|
||||
/* Check for NT */
|
||||
if( (osvi.dwMajorVersion == 4) && (osvi.dwPlatformId == 2) )
|
||||
{
|
||||
minLatencyMsec = PA_WIN_NT_LATENCY;
|
||||
}
|
||||
else if(osvi.dwMajorVersion >= 5)
|
||||
{
|
||||
minLatencyMsec = PA_WIN_WDM_LATENCY;
|
||||
}
|
||||
else
|
||||
{
|
||||
minLatencyMsec = PA_WIN_9X_LATENCY;
|
||||
}
|
||||
return minLatencyMsec;
|
||||
}
|
||||
|
||||
static PaError ValidateWinDirectSoundSpecificStreamInfo(
|
||||
const PaStreamParameters *streamParameters,
|
||||
const PaWinDirectSoundStreamInfo *streamInfo )
|
||||
|
@ -1289,7 +1332,7 @@ static PaError ValidateWinDirectSoundSpecificStreamInfo(
|
|||
if( streamInfo )
|
||||
{
|
||||
if( streamInfo->size != sizeof( PaWinDirectSoundStreamInfo )
|
||||
|| streamInfo->version != 1 )
|
||||
|| streamInfo->version != 2 )
|
||||
{
|
||||
return paIncompatibleHostApiSpecificStreamInfo;
|
||||
}
|
||||
|
@ -1394,45 +1437,6 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
|
|||
}
|
||||
|
||||
|
||||
/*************************************************************************
|
||||
** Determine minimum number of buffers required for this host based
|
||||
** on minimum latency. 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
|
||||
**
|
||||
** in the AUTOEXEC.BAT file and reboot.
|
||||
** If the environment variable is not set, then the latency will be determined
|
||||
** based on the OS. Windows NT has higher latency than Win95.
|
||||
*/
|
||||
#define PA_LATENCY_ENV_NAME ("PA_MIN_LATENCY_MSEC")
|
||||
#define PA_ENV_BUF_SIZE (32)
|
||||
|
||||
static int PaWinDs_GetMinLatencyFrames( double sampleRate )
|
||||
{
|
||||
char envbuf[PA_ENV_BUF_SIZE];
|
||||
DWORD hresult;
|
||||
int minLatencyMsec = 0;
|
||||
|
||||
/* Let user determine minimal latency by setting environment variable. */
|
||||
hresult = GetEnvironmentVariable( PA_LATENCY_ENV_NAME, envbuf, PA_ENV_BUF_SIZE );
|
||||
if( (hresult > 0) && (hresult < PA_ENV_BUF_SIZE) )
|
||||
{
|
||||
minLatencyMsec = atoi( envbuf );
|
||||
}
|
||||
else
|
||||
{
|
||||
minLatencyMsec = PaWinDS_GetMinSystemLatency();
|
||||
#if PA_USE_HIGH_LATENCY
|
||||
PRINT(("PA - Minimum Latency set to %d msec!\n", minLatencyMsec ));
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
return (int) (minLatencyMsec * sampleRate * SECONDS_PER_MSEC);
|
||||
}
|
||||
|
||||
|
||||
#ifdef PAWIN_USE_DIRECTSOUNDFULLDUPLEXCREATE
|
||||
static HRESULT InitFullDuplexInputOutputBuffers( PaWinDsStream *stream,
|
||||
PaWinDsDeviceInfo *inputDevice,
|
||||
|
@ -1668,6 +1672,119 @@ error:
|
|||
return result;
|
||||
}
|
||||
|
||||
|
||||
static void CalculateBufferSettings( unsigned long *hostBufferSizeFrames,
|
||||
unsigned long *pollingPeriodFrames,
|
||||
int isFullDuplex,
|
||||
unsigned long suggestedInputLatencyFrames,
|
||||
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 */
|
||||
|
||||
if( userFramesPerBuffer == paFramesPerBufferUnspecified )
|
||||
{
|
||||
unsigned long suggestedLatencyFrames = max( suggestedInputLatencyFrames, suggestedOutputLatencyFrames );
|
||||
|
||||
*pollingPeriodFrames = suggestedLatencyFrames / 4;
|
||||
if( *pollingPeriodFrames < minimumPollingPeriodFrames )
|
||||
{
|
||||
*pollingPeriodFrames = minimumPollingPeriodFrames;
|
||||
}
|
||||
else if( *pollingPeriodFrames > maximumPollingPeriodFrames )
|
||||
{
|
||||
*pollingPeriodFrames = maximumPollingPeriodFrames;
|
||||
}
|
||||
|
||||
*hostBufferSizeFrames = *pollingPeriodFrames
|
||||
+ max( *pollingPeriodFrames + pollingJitterFrames, suggestedLatencyFrames);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned long suggestedLatencyFrames = suggestedInputLatencyFrames;
|
||||
if( isFullDuplex )
|
||||
{
|
||||
/* 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 =
|
||||
suggestedOutputLatencyFrames - userFramesPerBuffer;
|
||||
|
||||
/* maximum of input and adjusted output suggested latency */
|
||||
if( adjustedSuggestedOutputLatencyFrames > suggestedInputLatencyFrames )
|
||||
suggestedLatencyFrames = adjustedSuggestedOutputLatencyFrames;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* maximum of input and output suggested latency */
|
||||
if( suggestedOutputLatencyFrames > suggestedInputLatencyFrames )
|
||||
suggestedLatencyFrames = suggestedOutputLatencyFrames;
|
||||
}
|
||||
|
||||
*hostBufferSizeFrames = userFramesPerBuffer
|
||||
+ max( userFramesPerBuffer + pollingJitterFrames, suggestedLatencyFrames);
|
||||
|
||||
*pollingPeriodFrames = max( max(1, userFramesPerBuffer / 4), suggestedLatencyFrames / 16 );
|
||||
|
||||
if( *pollingPeriodFrames > maximumPollingPeriodFrames )
|
||||
{
|
||||
*pollingPeriodFrames = maximumPollingPeriodFrames;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
and the configuration of the buffer processor */
|
||||
|
||||
unsigned long effectiveFramesPerBuffer = (userFramesPerBuffer == paFramesPerBufferUnspecified)
|
||||
? pollingPeriodFrames
|
||||
: userFramesPerBuffer;
|
||||
|
||||
if( stream->bufferProcessor.inputChannelCount > 0 )
|
||||
{
|
||||
/* 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)
|
||||
+ effectiveFramesPerBuffer) / sampleRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream->streamRepresentation.streamInfo.inputLatency = 0;
|
||||
}
|
||||
|
||||
if( stream->bufferProcessor.outputChannelCount > 0 )
|
||||
{
|
||||
stream->streamRepresentation.streamInfo.outputLatency =
|
||||
(double)(PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor)
|
||||
+ (stream->hostBufferSizeFrames - effectiveFramesPerBuffer)) / sampleRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
stream->streamRepresentation.streamInfo.outputLatency = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************************/
|
||||
/* see pa_hostapi.h for a list of validity guarantees made about OpenStream parameters */
|
||||
|
||||
|
@ -1694,6 +1811,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
unsigned long suggestedInputLatencyFrames, suggestedOutputLatencyFrames;
|
||||
PaWinDirectSoundStreamInfo *inputStreamInfo, *outputStreamInfo;
|
||||
PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
|
||||
unsigned long pollingPeriodFrames = 0;
|
||||
|
||||
if( inputParameters )
|
||||
{
|
||||
|
@ -1833,7 +1951,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
{
|
||||
/* IMPLEMENT ME - establish which host formats are available */
|
||||
PaSampleFormat nativeInputFormats = paInt16;
|
||||
//PaSampleFormat nativeFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32;
|
||||
/* PaSampleFormat nativeFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; */
|
||||
|
||||
hostInputSampleFormat =
|
||||
PaUtil_SelectClosestAvailableFormat( nativeInputFormats, inputParameters->sampleFormat );
|
||||
|
@ -1847,7 +1965,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
{
|
||||
/* IMPLEMENT ME - establish which host formats are available */
|
||||
PaSampleFormat nativeOutputFormats = paInt16;
|
||||
//PaSampleFormat nativeOutputFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32;
|
||||
/* PaSampleFormat nativeOutputFormats = paUInt8 | paInt16 | paInt24 | paInt32 | paFloat32; */
|
||||
|
||||
hostOutputSampleFormat =
|
||||
PaUtil_SelectClosestAvailableFormat( nativeOutputFormats, outputParameters->sampleFormat );
|
||||
|
@ -1861,7 +1979,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
inputChannelCount, inputSampleFormat, hostInputSampleFormat,
|
||||
outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
|
||||
sampleRate, streamFlags, framesPerBuffer,
|
||||
framesPerBuffer, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */
|
||||
0, /* ignored in paUtilVariableHostBufferSizePartialUsageAllowed mode. */
|
||||
/* This next mode is required because DS can split the host buffer when it wraps around. */
|
||||
paUtilVariableHostBufferSizePartialUsageAllowed,
|
||||
streamCallback, userData );
|
||||
|
@ -1870,24 +1988,12 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
|
||||
bufferProcessorIsInitialized = 1;
|
||||
|
||||
stream->streamRepresentation.streamInfo.inputLatency =
|
||||
(PaTime)PaUtil_GetBufferProcessorInputLatencyFrames(&stream->bufferProcessor) / sampleRate; /* FIXME: only includes buffer processor latency */
|
||||
stream->streamRepresentation.streamInfo.outputLatency =
|
||||
(PaTime)PaUtil_GetBufferProcessorOutputLatencyFrames(&stream->bufferProcessor) / sampleRate; /* FIXME: only includes buffer processor latency */
|
||||
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
|
||||
|
||||
|
||||
|
||||
/* DirectSound specific initialization */
|
||||
{
|
||||
HRESULT hr;
|
||||
int bytesPerDirectSoundInputBuffer, bytesPerDirectSoundOutputBuffer;
|
||||
int userLatencyFrames;
|
||||
int minLatencyFrames;
|
||||
unsigned long integerSampleRate = (unsigned long) (sampleRate + 0.5);
|
||||
|
||||
#ifdef PA_WIN_DS_USE_WMME_TIMER
|
||||
stream->timerID = 0;
|
||||
#endif
|
||||
|
||||
stream->processingCompleted = CreateEvent( NULL, /* bManualReset = */ TRUE, /* bInitialState = */ FALSE, NULL );
|
||||
if( stream->processingCompleted == NULL )
|
||||
{
|
||||
|
@ -1895,6 +2001,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
goto error;
|
||||
}
|
||||
|
||||
#ifdef PA_WIN_DS_USE_WMME_TIMER
|
||||
stream->timerID = 0;
|
||||
#endif
|
||||
|
||||
#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
|
||||
stream->waitableTimer = (HANDLE)CreateWaitableTimer( 0, FALSE, NULL );
|
||||
if( stream->waitableTimer == NULL )
|
||||
|
@ -1915,42 +2025,16 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
}
|
||||
#endif
|
||||
|
||||
/* Get system minimum latency. */
|
||||
minLatencyFrames = PaWinDs_GetMinLatencyFrames( sampleRate );
|
||||
|
||||
/* Let user override latency by passing latency parameter. */
|
||||
userLatencyFrames = (suggestedInputLatencyFrames > suggestedOutputLatencyFrames)
|
||||
? suggestedInputLatencyFrames
|
||||
: suggestedOutputLatencyFrames;
|
||||
if( userLatencyFrames > 0 ) minLatencyFrames = userLatencyFrames;
|
||||
|
||||
/* Calculate stream->framesPerDSBuffer depending on framesPerBuffer */
|
||||
if( framesPerBuffer == paFramesPerBufferUnspecified )
|
||||
{
|
||||
/* App support variable framesPerBuffer */
|
||||
stream->framesPerDSBuffer = minLatencyFrames;
|
||||
|
||||
stream->streamRepresentation.streamInfo.outputLatency = (double)(minLatencyFrames - 1) / sampleRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Round up to number of buffers needed to guarantee that latency. */
|
||||
int numUserBuffers = (minLatencyFrames + framesPerBuffer - 1) / framesPerBuffer;
|
||||
if( numUserBuffers < 1 ) numUserBuffers = 1;
|
||||
numUserBuffers += 1; /* So we have latency worth of buffers ahead of current buffer. */
|
||||
stream->framesPerDSBuffer = framesPerBuffer * numUserBuffers;
|
||||
|
||||
stream->streamRepresentation.streamInfo.outputLatency = (double)(framesPerBuffer * (numUserBuffers-1)) / sampleRate;
|
||||
}
|
||||
|
||||
{
|
||||
/** @todo REVIEW: this calculation seems incorrect to me - rossb. */
|
||||
int msecLatency = (int) ((stream->framesPerDSBuffer * MSEC_PER_SECOND) / sampleRate);
|
||||
PRINT(("PortAudio on DirectSound - Latency = %d frames, %d msec\n", stream->framesPerDSBuffer, msecLatency ));
|
||||
}
|
||||
|
||||
/* set up i/o parameters */
|
||||
|
||||
CalculateBufferSettings( &stream->hostBufferSizeFrames, &pollingPeriodFrames,
|
||||
/* isFullDuplex = */ (inputParameters && outputParameters),
|
||||
suggestedInputLatencyFrames,
|
||||
suggestedOutputLatencyFrames,
|
||||
sampleRate, framesPerBuffer );
|
||||
|
||||
stream->pollingPeriodSeconds = pollingPeriodFrames / sampleRate;
|
||||
|
||||
/* ------------------ OUTPUT */
|
||||
if( outputParameters )
|
||||
{
|
||||
|
@ -1961,14 +2045,16 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", outputParameters->device));
|
||||
*/
|
||||
|
||||
int bytesPerSample = Pa_GetSampleSize(hostOutputSampleFormat);
|
||||
bytesPerDirectSoundOutputBuffer = stream->framesPerDSBuffer * outputParameters->channelCount * bytesPerSample;
|
||||
if( bytesPerDirectSoundOutputBuffer < DSBSIZE_MIN )
|
||||
int sampleSizeBytes = Pa_GetSampleSize(hostOutputSampleFormat);
|
||||
stream->outputFrameSizeBytes = outputParameters->channelCount * sampleSizeBytes;
|
||||
|
||||
stream->outputBufferSizeBytes = stream->hostBufferSizeFrames * stream->outputFrameSizeBytes;
|
||||
if( stream->outputBufferSizeBytes < DSBSIZE_MIN )
|
||||
{
|
||||
result = paBufferTooSmall;
|
||||
goto error;
|
||||
}
|
||||
else if( bytesPerDirectSoundOutputBuffer > DSBSIZE_MAX )
|
||||
else if( stream->outputBufferSizeBytes > DSBSIZE_MAX )
|
||||
{
|
||||
result = paBufferTooBig;
|
||||
goto error;
|
||||
|
@ -1979,15 +2065,13 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
(stream->bufferProcessor.bytesPerHostOutputSample *
|
||||
outputChannelCount * sampleRate);
|
||||
|
||||
stream->outputBufferSizeBytes = bytesPerDirectSoundOutputBuffer;
|
||||
stream->outputIsRunning = FALSE;
|
||||
stream->outputUnderflowCount = 0;
|
||||
stream->bytesPerOutputFrame = outputParameters->channelCount * bytesPerSample;
|
||||
|
||||
|
||||
/* perfCounterTicksPerBuffer is used by QueryOutputSpace for overflow detection */
|
||||
if( QueryPerformanceFrequency( &counterFrequency ) )
|
||||
{
|
||||
stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * stream->framesPerDSBuffer) / integerSampleRate;
|
||||
stream->perfCounterTicksPerBuffer.QuadPart = (counterFrequency.QuadPart * stream->hostBufferSizeFrames) / integerSampleRate;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2003,22 +2087,20 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
DBUG(("PaHost_OpenStream: deviceID = 0x%x\n", inputParameters->device));
|
||||
*/
|
||||
|
||||
int bytesPerSample = Pa_GetSampleSize(hostInputSampleFormat);
|
||||
bytesPerDirectSoundInputBuffer = stream->framesPerDSBuffer * inputParameters->channelCount * bytesPerSample;
|
||||
if( bytesPerDirectSoundInputBuffer < DSBSIZE_MIN )
|
||||
int sampleSizeBytes = Pa_GetSampleSize(hostInputSampleFormat);
|
||||
stream->inputFrameSizeBytes = inputParameters->channelCount * sampleSizeBytes;
|
||||
|
||||
stream->inputBufferSizeBytes = stream->hostBufferSizeFrames * stream->inputFrameSizeBytes;
|
||||
if( stream->inputBufferSizeBytes < DSBSIZE_MIN )
|
||||
{
|
||||
result = paBufferTooSmall;
|
||||
goto error;
|
||||
}
|
||||
else if( bytesPerDirectSoundInputBuffer > DSBSIZE_MAX )
|
||||
else if( stream->inputBufferSizeBytes > DSBSIZE_MAX )
|
||||
{
|
||||
result = paBufferTooBig;
|
||||
goto error;
|
||||
}
|
||||
|
||||
stream->bytesPerInputFrame = inputParameters->channelCount * bytesPerSample;
|
||||
|
||||
stream->inputSize = bytesPerDirectSoundInputBuffer;
|
||||
}
|
||||
|
||||
/* open/create the DirectSound buffers */
|
||||
|
@ -2040,11 +2122,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
hr = InitFullDuplexInputOutputBuffers( stream,
|
||||
(PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
|
||||
hostInputSampleFormat,
|
||||
(WORD)inputParameters->channelCount, bytesPerDirectSoundInputBuffer,
|
||||
(WORD)inputParameters->channelCount, stream->inputBufferSizeBytes,
|
||||
inputChannelMask,
|
||||
(PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
|
||||
hostOutputSampleFormat,
|
||||
(WORD)outputParameters->channelCount, bytesPerDirectSoundOutputBuffer,
|
||||
(WORD)outputParameters->channelCount, stream->outputBufferSizeBytes,
|
||||
outputChannelMask,
|
||||
integerSampleRate
|
||||
);
|
||||
|
@ -2066,7 +2148,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
(PaWinDsDeviceInfo*)hostApi->deviceInfos[outputParameters->device],
|
||||
hostOutputSampleFormat,
|
||||
integerSampleRate,
|
||||
(WORD)outputParameters->channelCount, bytesPerDirectSoundOutputBuffer,
|
||||
(WORD)outputParameters->channelCount, stream->outputBufferSizeBytes,
|
||||
outputChannelMask );
|
||||
DBUG(("InitOutputBuffer() returns %x\n", hr));
|
||||
if( hr != DS_OK )
|
||||
|
@ -2083,7 +2165,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
(PaWinDsDeviceInfo*)hostApi->deviceInfos[inputParameters->device],
|
||||
hostInputSampleFormat,
|
||||
integerSampleRate,
|
||||
(WORD)inputParameters->channelCount, bytesPerDirectSoundInputBuffer,
|
||||
(WORD)inputParameters->channelCount, stream->inputBufferSizeBytes,
|
||||
inputChannelMask );
|
||||
DBUG(("InitInputBuffer() returns %x\n", hr));
|
||||
if( hr != DS_OK )
|
||||
|
@ -2096,6 +2178,10 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
|
|||
}
|
||||
}
|
||||
|
||||
SetStreamInfoLatencies( stream, framesPerBuffer, pollingPeriodFrames, sampleRate );
|
||||
|
||||
stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
|
||||
|
||||
*s = (PaStream*)stream;
|
||||
|
||||
return result;
|
||||
|
@ -2256,7 +2342,7 @@ static int TimeSlice( PaWinDsStream *stream )
|
|||
long bytesEmpty = 0;
|
||||
long bytesFilled = 0;
|
||||
long bytesToXfer = 0;
|
||||
long framesToXfer = 0;
|
||||
long framesToXfer = 0; /* the number of frames we'll process this tick */
|
||||
long numInFramesReady = 0;
|
||||
long numOutFramesReady = 0;
|
||||
long bytesProcessed;
|
||||
|
@ -2289,12 +2375,12 @@ static int TimeSlice( PaWinDsStream *stream )
|
|||
if( hr == DS_OK )
|
||||
{
|
||||
filled = readPos - stream->readOffset;
|
||||
if( filled < 0 ) filled += stream->inputSize; // unwrap offset
|
||||
if( filled < 0 ) filled += stream->inputBufferSizeBytes; // unwrap offset
|
||||
bytesFilled = filled;
|
||||
}
|
||||
// FIXME: what happens if IDirectSoundCaptureBuffer_GetCurrentPosition fails?
|
||||
|
||||
framesToXfer = numInFramesReady = bytesFilled / stream->bytesPerInputFrame;
|
||||
framesToXfer = numInFramesReady = bytesFilled / stream->inputFrameSizeBytes;
|
||||
outputLatency = ((double)bytesFilled) * stream->secondsPerHostByte; // FIXME: this doesn't look right. we're calculating output latency in input branch. also secondsPerHostByte is only initialized for the output stream
|
||||
|
||||
/** @todo Check for overflow */
|
||||
|
@ -2305,14 +2391,15 @@ static int TimeSlice( PaWinDsStream *stream )
|
|||
{
|
||||
UINT previousUnderflowCount = stream->outputUnderflowCount;
|
||||
QueryOutputSpace( stream, &bytesEmpty );
|
||||
framesToXfer = numOutFramesReady = bytesEmpty / stream->bytesPerOutputFrame;
|
||||
framesToXfer = numOutFramesReady = bytesEmpty / stream->outputFrameSizeBytes;
|
||||
|
||||
/* Check for underflow */
|
||||
if( stream->outputUnderflowCount != previousUnderflowCount )
|
||||
stream->callbackFlags |= paOutputUnderflow;
|
||||
}
|
||||
|
||||
if( (numInFramesReady > 0) && (numOutFramesReady > 0) )
|
||||
/* if it's a full duplex stream, set framesToXfer to the minimum of input and output frames ready */
|
||||
if( stream->bufferProcessor.inputChannelCount > 0 && stream->bufferProcessor.outputChannelCount > 0 )
|
||||
{
|
||||
framesToXfer = (numOutFramesReady < numInFramesReady) ? numOutFramesReady : numInFramesReady;
|
||||
}
|
||||
|
@ -2333,7 +2420,7 @@ static int TimeSlice( PaWinDsStream *stream )
|
|||
/* Input */
|
||||
if( stream->bufferProcessor.inputChannelCount > 0 )
|
||||
{
|
||||
bytesToXfer = framesToXfer * stream->bytesPerInputFrame;
|
||||
bytesToXfer = framesToXfer * stream->inputFrameSizeBytes;
|
||||
hresult = IDirectSoundCaptureBuffer_Lock ( stream->pDirectSoundInputBuffer,
|
||||
stream->readOffset, bytesToXfer,
|
||||
(void **) &lpInBuf1, &dwInSize1,
|
||||
|
@ -2347,13 +2434,13 @@ static int TimeSlice( PaWinDsStream *stream )
|
|||
goto error2;
|
||||
}
|
||||
|
||||
numFrames = dwInSize1 / stream->bytesPerInputFrame;
|
||||
numFrames = dwInSize1 / stream->inputFrameSizeBytes;
|
||||
PaUtil_SetInputFrameCount( &stream->bufferProcessor, numFrames );
|
||||
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf1, 0 );
|
||||
/* Is input split into two regions. */
|
||||
if( dwInSize2 > 0 )
|
||||
{
|
||||
numFrames = dwInSize2 / stream->bytesPerInputFrame;
|
||||
numFrames = dwInSize2 / stream->inputFrameSizeBytes;
|
||||
PaUtil_Set2ndInputFrameCount( &stream->bufferProcessor, numFrames );
|
||||
PaUtil_Set2ndInterleavedInputChannels( &stream->bufferProcessor, 0, lpInBuf2, 0 );
|
||||
}
|
||||
|
@ -2362,7 +2449,7 @@ static int TimeSlice( PaWinDsStream *stream )
|
|||
/* Output */
|
||||
if( stream->bufferProcessor.outputChannelCount > 0 )
|
||||
{
|
||||
bytesToXfer = framesToXfer * stream->bytesPerOutputFrame;
|
||||
bytesToXfer = framesToXfer * stream->outputFrameSizeBytes;
|
||||
hresult = IDirectSoundBuffer_Lock ( stream->pDirectSoundOutputBuffer,
|
||||
stream->outputBufferWriteOffsetBytes, bytesToXfer,
|
||||
(void **) &lpOutBuf1, &dwOutSize1,
|
||||
|
@ -2376,14 +2463,14 @@ static int TimeSlice( PaWinDsStream *stream )
|
|||
goto error1;
|
||||
}
|
||||
|
||||
numFrames = dwOutSize1 / stream->bytesPerOutputFrame;
|
||||
numFrames = dwOutSize1 / stream->outputFrameSizeBytes;
|
||||
PaUtil_SetOutputFrameCount( &stream->bufferProcessor, numFrames );
|
||||
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf1, 0 );
|
||||
|
||||
/* Is output split into two regions. */
|
||||
if( dwOutSize2 > 0 )
|
||||
{
|
||||
numFrames = dwOutSize2 / stream->bytesPerOutputFrame;
|
||||
numFrames = dwOutSize2 / stream->outputFrameSizeBytes;
|
||||
PaUtil_Set2ndOutputFrameCount( &stream->bufferProcessor, numFrames );
|
||||
PaUtil_Set2ndInterleavedOutputChannels( &stream->bufferProcessor, 0, lpOutBuf2, 0 );
|
||||
}
|
||||
|
@ -2397,7 +2484,7 @@ static int TimeSlice( PaWinDsStream *stream )
|
|||
/* FIXME: an underflow could happen here */
|
||||
|
||||
/* Update our buffer offset and unlock sound buffer */
|
||||
bytesProcessed = numFrames * stream->bytesPerOutputFrame;
|
||||
bytesProcessed = numFrames * stream->outputFrameSizeBytes;
|
||||
stream->outputBufferWriteOffsetBytes = (stream->outputBufferWriteOffsetBytes + bytesProcessed) % stream->outputBufferSizeBytes;
|
||||
IDirectSoundBuffer_Unlock( stream->pDirectSoundOutputBuffer, lpOutBuf1, dwOutSize1, lpOutBuf2, dwOutSize2);
|
||||
}
|
||||
|
@ -2408,8 +2495,8 @@ error1:
|
|||
/* FIXME: an overflow could happen here */
|
||||
|
||||
/* Update our buffer offset and unlock sound buffer */
|
||||
bytesProcessed = numFrames * stream->bytesPerInputFrame;
|
||||
stream->readOffset = (stream->readOffset + bytesProcessed) % stream->inputSize;
|
||||
bytesProcessed = numFrames * stream->inputFrameSizeBytes;
|
||||
stream->readOffset = (stream->readOffset + bytesProcessed) % stream->inputBufferSizeBytes;
|
||||
IDirectSoundCaptureBuffer_Unlock( stream->pDirectSoundInputBuffer, lpInBuf1, dwInSize1, lpInBuf2, dwInSize2);
|
||||
}
|
||||
error2:
|
||||
|
@ -2548,28 +2635,27 @@ PA_THREAD_FUNC ProcessingThreadProc( void *pArg )
|
|||
MMRESULT mmResult;
|
||||
HANDLE hWaitableTimer;
|
||||
LARGE_INTEGER dueTime;
|
||||
int framesPerWakeup, msecPerWakeup;
|
||||
int timerPeriodMs;
|
||||
|
||||
framesPerWakeup = stream->framesPerDSBuffer / 4; /* always poll using quadruple buffering, probably not the best strategy */
|
||||
msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate;
|
||||
if( msecPerWakeup < 1 ) msecPerWakeup = 1;
|
||||
else if( msecPerWakeup > 100 ) msecPerWakeup = 100;
|
||||
timerPeriodMs = stream->pollingPeriodSeconds * MSECS_PER_SECOND;
|
||||
if( timerPeriodMs < 1 )
|
||||
timerPeriodMs = 1;
|
||||
|
||||
#ifdef PA_WIN_DS_USE_WAITABLE_TIMER_OBJECT
|
||||
assert( stream->waitableTimer != NULL );
|
||||
|
||||
/* invoke first timeout immediately */
|
||||
dueTime.LowPart = msecPerWakeup * 1000 * 10;
|
||||
dueTime.LowPart = timerPeriodMs * 1000 * 10;
|
||||
dueTime.HighPart = 0;
|
||||
|
||||
/* tick using waitable timer */
|
||||
if( SetWaitableTimer( stream->waitableTimer, &dueTime, msecPerWakeup, WaitableTimerAPCProc, pArg, FALSE ) != 0 )
|
||||
if( SetWaitableTimer( stream->waitableTimer, &dueTime, timerPeriodMs, WaitableTimerAPCProc, pArg, FALSE ) != 0 )
|
||||
{
|
||||
DWORD wfsoResult = 0;
|
||||
do
|
||||
{
|
||||
/* wait for processingCompleted to be signaled or our timer APC to be called */
|
||||
wfsoResult = WaitForSingleObjectEx( stream->processingCompleted, msecPerWakeup * 10, /* alertable = */ TRUE );
|
||||
wfsoResult = WaitForSingleObjectEx( stream->processingCompleted, timerPeriodMs * 10, /* alertable = */ TRUE );
|
||||
|
||||
}while( wfsoResult == WAIT_TIMEOUT || wfsoResult == WAIT_IO_COMPLETION );
|
||||
}
|
||||
|
@ -2579,7 +2665,7 @@ PA_THREAD_FUNC ProcessingThreadProc( void *pArg )
|
|||
#else
|
||||
|
||||
/* tick using WaitForSingleObject timout */
|
||||
while ( WaitForSingleObject( stream->processingCompleted, msecPerWakeup ) == WAIT_TIMEOUT )
|
||||
while ( WaitForSingleObject( stream->processingCompleted, timerPeriodMs ) == WAIT_TIMEOUT )
|
||||
{
|
||||
TimerCallback( 0, 0, (DWORD_PTR)pArg, 0, 0 );
|
||||
}
|
||||
|
@ -2775,13 +2861,9 @@ static PaError StartStream( PaStream *s )
|
|||
if( stream->streamRepresentation.streamCallback )
|
||||
{
|
||||
TIMECAPS timecaps;
|
||||
|
||||
int timerResolution;
|
||||
int framesPerWakeup = stream->framesPerDSBuffer / 4;
|
||||
int msecPerWakeup = MSEC_PER_SECOND * framesPerWakeup / (int) stream->streamRepresentation.streamInfo.sampleRate;
|
||||
if( msecPerWakeup < 10 ) msecPerWakeup = 10;
|
||||
else if( msecPerWakeup > 100 ) msecPerWakeup = 100;
|
||||
timerResolution = msecPerWakeup/4;
|
||||
int timerPeriodMs = stream->pollingPeriodSeconds * MSECS_PER_SECOND;
|
||||
if( timerPeriodMs < 1 )
|
||||
timerPeriodMs = 1;
|
||||
|
||||
/* set windows scheduler granularity only as fine as needed, no finer */
|
||||
/* Although this is not fully documented by MS, it appears that
|
||||
|
@ -2792,9 +2874,12 @@ static PaError StartStream( PaStream *s )
|
|||
assert( stream->systemTimerResolutionPeriodMs == 0 );
|
||||
if( timeGetDevCaps( &timecaps, sizeof(TIMECAPS) == MMSYSERR_NOERROR && timecaps.wPeriodMin > 0 ) )
|
||||
{
|
||||
stream->systemTimerResolutionPeriodMs = timerResolution;
|
||||
/* aim for resolution 4 times higher than polling rate */
|
||||
stream->systemTimerResolutionPeriodMs = (stream->pollingPeriodSeconds * MSECS_PER_SECOND) / 4;
|
||||
if( stream->systemTimerResolutionPeriodMs < timecaps.wPeriodMin )
|
||||
stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMin;
|
||||
if( stream->systemTimerResolutionPeriodMs > timecaps.wPeriodMax )
|
||||
stream->systemTimerResolutionPeriodMs = timecaps.wPeriodMax;
|
||||
|
||||
if( timeBeginPeriod( stream->systemTimerResolutionPeriodMs ) != MMSYSERR_NOERROR )
|
||||
stream->systemTimerResolutionPeriodMs = 0; /* timeBeginPeriod failed, so we don't need to call timeEndPeriod() later */
|
||||
|
@ -2807,7 +2892,7 @@ static PaError StartStream( PaStream *s )
|
|||
are serialised onto a single thread. Which creates problems with multiple
|
||||
PA streams, or when also using timers for other time critical tasks
|
||||
*/
|
||||
stream->timerID = timeSetEvent( msecPerWakeup, timerResolution, (LPTIMECALLBACK) TimerCallback,
|
||||
stream->timerID = timeSetEvent( timerPeriodMs, stream->systemTimerResolutionPeriodMs, (LPTIMECALLBACK) TimerCallback,
|
||||
(DWORD_PTR) stream, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS );
|
||||
|
||||
if( stream->timerID == 0 )
|
||||
|
@ -2876,7 +2961,7 @@ static PaError StopStream( PaStream *s )
|
|||
stream->stopProcessing = 1;
|
||||
|
||||
/* Set timeout at 4 times maximum time we might wait. */
|
||||
timeoutMsec = (int) (4 * MSEC_PER_SECOND * (stream->framesPerDSBuffer / stream->streamRepresentation.streamInfo.sampleRate));
|
||||
timeoutMsec = (int) (4 * MSECS_PER_SECOND * (stream->hostBufferSizeFrames / stream->streamRepresentation.streamInfo.sampleRate));
|
||||
|
||||
WaitForSingleObject( stream->processingCompleted, timeoutMsec );
|
||||
}
|
||||
|
|
|
@ -125,13 +125,13 @@
|
|||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
//#define NOMMIDS
|
||||
#define NOMMIDS
|
||||
#define DYNAMIC_GUID(data) {data}
|
||||
//#define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
|
||||
//#undef DEFINE_GUID
|
||||
//#define DEFINE_GUID(n,data) EXTERN_C const GUID n = {data}
|
||||
//#define DEFINE_GUID_THUNK(n,data) DEFINE_GUID(n,data)
|
||||
//#define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n)
|
||||
#define _NTRTL_ /* Turn off default definition of DEFINE_GUIDEX */
|
||||
#undef DEFINE_GUID
|
||||
#define DEFINE_GUID(n,data) EXTERN_C const GUID n = {data}
|
||||
#define DEFINE_GUID_THUNK(n,data) DEFINE_GUID(n,data)
|
||||
#define DEFINE_GUIDEX(n) DEFINE_GUID_THUNK(n, STATIC_##n)
|
||||
#endif
|
||||
|
||||
#include <mmreg.h>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_win_wmme.c 1717 2011-07-28 12:03:09Z rossb $
|
||||
* $Id: pa_win_wmme.c 1739 2011-08-25 07:15:31Z rossb $
|
||||
* pa_win_wmme.c
|
||||
* Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
|
||||
*
|
||||
|
@ -174,16 +174,27 @@
|
|||
/* Use higher latency for NT because it is even worse at real-time
|
||||
operation than Win9x.
|
||||
*/
|
||||
#define PA_MME_WIN_NT_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_ * 2)
|
||||
#define PA_MME_WIN_WDM_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_)
|
||||
#define PA_MME_WIN_NT_DEFAULT_LATENCY_ (0.4)
|
||||
|
||||
/* Default low latency for WDM based systems. This is based on a rough
|
||||
survey of workable latency settings using patest_wmme_find_best_latency_params.c.
|
||||
See pdf attached to ticket 185 for a graph of the survey results:
|
||||
http://www.portaudio.com/trac/ticket/185
|
||||
|
||||
Workable latencies varied between 40ms and ~80ms on different systems (different
|
||||
combinations of hardware, 32 and 64 bit, WinXP, Vista and Win7. We didn't
|
||||
get enough Vista results to know if Vista has systemically worse latency.
|
||||
For now we choose a safe value across all Windows versions here.
|
||||
*/
|
||||
#define PA_MME_WIN_WDM_DEFAULT_LATENCY_ (0.090)
|
||||
|
||||
|
||||
/* When client suggestedLatency could result in many host buffers, we aim to have around 8,
|
||||
based off Windows documentation that suggests that the kmixer uses 8 buffers. This choice
|
||||
is somewhat arbitrary here, since we havn't observed significant stability degredation
|
||||
with using either more, or less buffers.
|
||||
*/
|
||||
|
||||
#define PA_MME_TARGET_HOST_BUFFER_COUNT_ 8
|
||||
#define PA_MME_TARGET_HOST_BUFFER_COUNT_ 8
|
||||
|
||||
#define PA_MME_MIN_TIMEOUT_MSEC_ (1000)
|
||||
|
||||
|
@ -1341,13 +1352,9 @@ static unsigned long ComputeHostBufferCountForFixedBufferSizeFrames(
|
|||
/* Calculate the number of buffers of length hostFramesPerBuffer
|
||||
that fit in suggestedLatencyFrames, rounding up to the next integer.
|
||||
|
||||
The exact formula for round-up is:
|
||||
((suggestedLatencyFrames + (hostFramesPerBuffer - 1)) / hostFramesPerBuffer)
|
||||
However we subtract 2 instead of 1 below to account for rounding errors.
|
||||
This ensures we don't erroneously overallocate an extra buffer due to a one-sample rounding error.
|
||||
The value (hostBufferSizeFrames - 1) below is to ensure the buffer count is rounded up.
|
||||
*/
|
||||
|
||||
unsigned long resultBufferCount = ((suggestedLatencyFrames + (hostBufferSizeFrames - 2)) / hostBufferSizeFrames);
|
||||
unsigned long resultBufferCount = ((suggestedLatencyFrames + (hostBufferSizeFrames - 1)) / hostBufferSizeFrames);
|
||||
|
||||
/* We always need one extra buffer for processing while the rest are queued/playing.
|
||||
i.e. latency is framesPerBuffer * (bufferCount - 1)
|
||||
|
@ -1361,6 +1368,48 @@ static unsigned long ComputeHostBufferCountForFixedBufferSizeFrames(
|
|||
}
|
||||
|
||||
|
||||
static unsigned long ComputeHostBufferSizeGivenHardUpperLimit(
|
||||
unsigned long userFramesPerBuffer,
|
||||
unsigned long absoluteMaximumBufferSizeFrames )
|
||||
{
|
||||
static unsigned long primes_[] = { 2, 3, 5, 7, 11, 13, 17, 19, 23,
|
||||
29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 0 }; /* zero terminated */
|
||||
|
||||
unsigned long result = userFramesPerBuffer;
|
||||
int i;
|
||||
|
||||
assert( absoluteMaximumBufferSizeFrames > 67 ); /* assume maximum is large and we're only factoring by small primes */
|
||||
|
||||
/* search for the largest integer factor of userFramesPerBuffer less
|
||||
than or equal to absoluteMaximumBufferSizeFrames */
|
||||
|
||||
/* repeatedly divide by smallest prime factors until a buffer size
|
||||
smaller than absoluteMaximumBufferSizeFrames is found */
|
||||
while( result > absoluteMaximumBufferSizeFrames ){
|
||||
|
||||
/* search for the smallest prime factor of result */
|
||||
for( i=0; primes_[i] != 0; ++i )
|
||||
{
|
||||
unsigned long p = primes_[i];
|
||||
unsigned long divided = result / p;
|
||||
if( divided*p == result )
|
||||
{
|
||||
result = divided;
|
||||
break; /* continue with outer while loop */
|
||||
}
|
||||
}
|
||||
if( primes_[i] == 0 )
|
||||
{ /* loop failed to find a prime factor, return an approximate result */
|
||||
unsigned long d = (userFramesPerBuffer + (absoluteMaximumBufferSizeFrames-1))
|
||||
/ absoluteMaximumBufferSizeFrames;
|
||||
return userFramesPerBuffer / d;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static PaError SelectHostBufferSizeFramesAndHostBufferCount(
|
||||
unsigned long suggestedLatencyFrames,
|
||||
unsigned long userFramesPerBuffer,
|
||||
|
@ -1370,56 +1419,77 @@ static PaError SelectHostBufferSizeFramesAndHostBufferCount(
|
|||
unsigned long *hostBufferSizeFrames,
|
||||
unsigned long *hostBufferCount )
|
||||
{
|
||||
unsigned long effectiveUserFramesPerBuffer;
|
||||
unsigned long numberOfUserBuffersPerHostBuffer;
|
||||
|
||||
|
||||
if( userFramesPerBuffer == paFramesPerBufferUnspecified ){
|
||||
|
||||
*hostBufferSizeFrames = PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_;
|
||||
effectiveUserFramesPerBuffer = PA_MME_HOST_BUFFER_GRANULARITY_FRAMES_WHEN_UNSPECIFIED_;
|
||||
|
||||
}else{
|
||||
|
||||
*hostBufferSizeFrames = userFramesPerBuffer;
|
||||
if( userFramesPerBuffer > absoluteMaximumBufferSizeFrames ){
|
||||
|
||||
if( *hostBufferSizeFrames > absoluteMaximumBufferSizeFrames ){
|
||||
/* user has requested a user buffer that's larger than what we can handle */
|
||||
|
||||
/* @todo FIXME right now we allow the user to request an oversize host buffer,
|
||||
even though elsewhere in the code there are suggestions that oversize buffers
|
||||
can cause crashes with some drivers. */
|
||||
/* return paBufferTooBig; */
|
||||
/* user has requested a user buffer that's larger than absoluteMaximumBufferSizeFrames.
|
||||
try to choose a buffer size that is equal or smaller than absoluteMaximumBufferSizeFrames
|
||||
but is also an integer factor of userFramesPerBuffer, so as to distribute computation evenly.
|
||||
the buffer processor will handle the block adaption between host and user buffer sizes.
|
||||
see http://www.portaudio.com/trac/ticket/189 for discussion.
|
||||
*/
|
||||
|
||||
effectiveUserFramesPerBuffer = ComputeHostBufferSizeGivenHardUpperLimit( userFramesPerBuffer, absoluteMaximumBufferSizeFrames );
|
||||
assert( effectiveUserFramesPerBuffer <= absoluteMaximumBufferSizeFrames );
|
||||
|
||||
/* try to ensure that duration of host buffering is at least as
|
||||
large as duration of user buffer. */
|
||||
if( suggestedLatencyFrames < userFramesPerBuffer )
|
||||
suggestedLatencyFrames = userFramesPerBuffer;
|
||||
|
||||
}else{
|
||||
|
||||
effectiveUserFramesPerBuffer = userFramesPerBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
/* compute a host buffer count based on suggestedLatencyFrames and our granularity */
|
||||
|
||||
*hostBufferSizeFrames = effectiveUserFramesPerBuffer;
|
||||
|
||||
*hostBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
|
||||
suggestedLatencyFrames, *hostBufferSizeFrames, minimumBufferCount );
|
||||
|
||||
/* consider coalescing multiple user buffers into each host buffer */
|
||||
if( *hostBufferSizeFrames >= userFramesPerBuffer )
|
||||
{
|
||||
/*
|
||||
If there are too many host buffers we would like to coalesce
|
||||
them by packing an integer number of user buffers into each host buffer.
|
||||
We try to coalesce such that hostBufferCount will lie between
|
||||
PA_MME_TARGET_HOST_BUFFER_COUNT_ and (PA_MME_TARGET_HOST_BUFFER_COUNT_*2)-1.
|
||||
We limit coalescing to avoid exceeding either absoluteMaximumBufferSizeFrames and
|
||||
preferredMaximumBufferSizeFrames.
|
||||
|
||||
if( *hostBufferCount >= PA_MME_TARGET_HOST_BUFFER_COUNT_ * 2 ){
|
||||
First, compute a coalescing factor: the number of user buffers per host buffer.
|
||||
The goal is to achieve PA_MME_TARGET_HOST_BUFFER_COUNT_ total buffer count.
|
||||
Since our latency is computed based on (*hostBufferCount - 1) we compute a
|
||||
coalescing factor based on (*hostBufferCount - 1) and (PA_MME_TARGET_HOST_BUFFER_COUNT_-1).
|
||||
|
||||
The + (PA_MME_TARGET_HOST_BUFFER_COUNT_-2) term below is intended to round up.
|
||||
*/
|
||||
numberOfUserBuffersPerHostBuffer = ((*hostBufferCount - 1) + (PA_MME_TARGET_HOST_BUFFER_COUNT_-2)) / (PA_MME_TARGET_HOST_BUFFER_COUNT_ - 1);
|
||||
|
||||
/* If there are too many host buffers we would like to coalesce
|
||||
them by packing an integer number of user buffers into each host buffer.
|
||||
We try to coalesce such that hostBufferCount will lie between
|
||||
PA_MME_TARGET_HOST_BUFFER_COUNT_ and (PA_MME_TARGET_HOST_BUFFER_COUNT_*2)-1.
|
||||
We limit coalescing to avoid exceeding either absoluteMaximumBufferSizeFrames and
|
||||
preferredMaximumBufferSizeFrames.
|
||||
*/
|
||||
if( numberOfUserBuffersPerHostBuffer > 1 )
|
||||
{
|
||||
unsigned long maxCoalescedBufferSizeFrames = (absoluteMaximumBufferSizeFrames < preferredMaximumBufferSizeFrames) /* minimum of our limits */
|
||||
? absoluteMaximumBufferSizeFrames
|
||||
: preferredMaximumBufferSizeFrames;
|
||||
|
||||
unsigned long maxCoalescedBufferSizeFrames = (absoluteMaximumBufferSizeFrames < preferredMaximumBufferSizeFrames) /* min of our limits */
|
||||
? absoluteMaximumBufferSizeFrames
|
||||
: preferredMaximumBufferSizeFrames;
|
||||
unsigned long maxUserBuffersPerHostBuffer = maxCoalescedBufferSizeFrames / effectiveUserFramesPerBuffer; /* don't coalesce more than this */
|
||||
|
||||
unsigned long maxUserBuffersPerHostBuffer = maxCoalescedBufferSizeFrames / *hostBufferSizeFrames; /* don't coalesce more than this */
|
||||
if( numberOfUserBuffersPerHostBuffer > maxUserBuffersPerHostBuffer )
|
||||
numberOfUserBuffersPerHostBuffer = maxUserBuffersPerHostBuffer;
|
||||
|
||||
/* we truncate here to compute a conservative value of numUserBuffersPerHostBuffer
|
||||
then we recompute hostBufferCount after coalescing.
|
||||
*/
|
||||
unsigned long numUserBuffersPerHostBuffer = *hostBufferCount / PA_MME_TARGET_HOST_BUFFER_COUNT_;
|
||||
if( numUserBuffersPerHostBuffer > maxUserBuffersPerHostBuffer )
|
||||
numUserBuffersPerHostBuffer = maxUserBuffersPerHostBuffer;
|
||||
|
||||
if( numUserBuffersPerHostBuffer > 1 ){
|
||||
*hostBufferSizeFrames *= numUserBuffersPerHostBuffer;
|
||||
*hostBufferSizeFrames = effectiveUserFramesPerBuffer * numberOfUserBuffersPerHostBuffer;
|
||||
|
||||
/* recompute hostBufferCount to approximate suggestedLatencyFrames now that hostBufferSizeFrames is larger */
|
||||
*hostBufferCount = ComputeHostBufferCountForFixedBufferSizeFrames(
|
||||
|
@ -1515,7 +1585,7 @@ static PaError CalculateBufferSettings(
|
|||
: PA_MME_MIN_HOST_INPUT_BUFFER_COUNT_HALF_DUPLEX_;
|
||||
|
||||
result = SelectHostBufferSizeFramesAndHostBufferCount(
|
||||
(unsigned long)(suggestedInputLatency * sampleRate),
|
||||
(unsigned long)(suggestedInputLatency * sampleRate), /* (truncate) */
|
||||
userFramesPerBuffer,
|
||||
minimumBufferCount,
|
||||
(unsigned long)(PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate), /* in frames. preferred maximum */
|
||||
|
@ -1610,7 +1680,7 @@ static PaError CalculateBufferSettings(
|
|||
/* compute the output buffer size and count */
|
||||
|
||||
result = SelectHostBufferSizeFramesAndHostBufferCount(
|
||||
(unsigned long)(suggestedOutputLatency * sampleRate),
|
||||
(unsigned long)(suggestedOutputLatency * sampleRate), /* (truncate) */
|
||||
userFramesPerBuffer,
|
||||
PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_,
|
||||
(unsigned long)(PA_MME_MAX_HOST_BUFFER_SECS_ * sampleRate), /* in frames. preferred maximum */
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_unix_hostapis.c 1413 2009-05-24 17:00:36Z aknudsen $
|
||||
* $Id: pa_unix_hostapis.c 1740 2011-08-25 07:17:48Z philburk $
|
||||
* Portable Audio I/O Library UNIX initialization table
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
|
@ -59,47 +59,45 @@ PaUtilHostApiInitializer *paHostApiInitializers[] =
|
|||
{
|
||||
#ifdef __linux__
|
||||
|
||||
#ifdef PA_USE_ALSA
|
||||
#if PA_USE_ALSA
|
||||
PaAlsa_Initialize,
|
||||
#endif
|
||||
|
||||
#ifdef PA_USE_OSS
|
||||
#if PA_USE_OSS
|
||||
PaOSS_Initialize,
|
||||
#endif
|
||||
|
||||
#else /* __linux__ */
|
||||
|
||||
#ifdef PA_USE_OSS
|
||||
#if PA_USE_OSS
|
||||
PaOSS_Initialize,
|
||||
#endif
|
||||
|
||||
#ifdef PA_USE_ALSA
|
||||
#if PA_USE_ALSA
|
||||
PaAlsa_Initialize,
|
||||
#endif
|
||||
|
||||
#endif /* __linux__ */
|
||||
|
||||
#ifdef PA_USE_JACK
|
||||
#if PA_USE_JACK
|
||||
PaJack_Initialize,
|
||||
#endif
|
||||
/* Added for IRIX, Pieter, oct 2, 2003: */
|
||||
#ifdef PA_USE_SGI
|
||||
#if PA_USE_SGI
|
||||
PaSGI_Initialize,
|
||||
#endif
|
||||
|
||||
#ifdef PA_USE_ASIHPI
|
||||
#if PA_USE_ASIHPI
|
||||
PaAsiHpi_Initialize,
|
||||
#endif
|
||||
|
||||
#ifdef PA_USE_COREAUDIO
|
||||
#if PA_USE_COREAUDIO
|
||||
PaMacCore_Initialize,
|
||||
#endif
|
||||
|
||||
#ifdef PA_USE_SKELETON
|
||||
#if PA_USE_SKELETON
|
||||
PaSkeleton_Initialize,
|
||||
#endif
|
||||
|
||||
0 /* NULL terminated array */
|
||||
};
|
||||
|
||||
int paDefaultHostApiIndex = 0;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* $Id: pa_win_hostapis.c 1687 2011-05-19 08:24:02Z rob_bielik $
|
||||
* $Id: pa_win_hostapis.c 1728 2011-08-18 03:31:51Z rossb $
|
||||
* Portable Audio I/O Library Windows initialization table
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
|
@ -100,5 +100,3 @@ PaUtilHostApiInitializer *paHostApiInitializers[] =
|
|||
};
|
||||
|
||||
|
||||
int paDefaultHostApiIndex = 0;
|
||||
|
||||
|
|
Loading…
Reference in New Issue