aboutsummaryrefslogtreecommitdiff
path: root/pd/portaudio/src/hostapi
diff options
context:
space:
mode:
Diffstat (limited to 'pd/portaudio/src/hostapi')
-rw-r--r--pd/portaudio/src/hostapi/alsa/pa_linux_alsa.c250
-rw-r--r--pd/portaudio/src/hostapi/asio/pa_asio.cpp1538
-rw-r--r--pd/portaudio/src/hostapi/coreaudio/notes.txt22
-rw-r--r--pd/portaudio/src/hostapi/coreaudio/pa_mac_core.c161
-rw-r--r--pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c88
-rw-r--r--pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.h9
-rw-r--r--pd/portaudio/src/hostapi/coreaudio/pa_mac_core_internal.h7
-rw-r--r--pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c141
-rw-r--r--pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.h31
-rw-r--r--pd/portaudio/src/hostapi/jack/pa_jack.c17
-rw-r--r--pd/portaudio/src/hostapi/oss/pa_unix_oss.c133
-rw-r--r--pd/portaudio/src/hostapi/wmme/pa_win_wmme.c565
12 files changed, 2402 insertions, 560 deletions
diff --git a/pd/portaudio/src/hostapi/alsa/pa_linux_alsa.c b/pd/portaudio/src/hostapi/alsa/pa_linux_alsa.c
index 06b17ac1..6b4a7b9f 100644
--- a/pd/portaudio/src/hostapi/alsa/pa_linux_alsa.c
+++ b/pd/portaudio/src/hostapi/alsa/pa_linux_alsa.c
@@ -1,11 +1,12 @@
/*
- * $Id: pa_linux_alsa.c 1236 2007-06-24 20:39:26Z aknudsen $
+ * $Id: pa_linux_alsa.c 1415 2009-06-03 18:57:56Z aknudsen $
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* ALSA implementation by Joshua Haberman and Arve Knudsen
*
* Copyright (c) 2002 Joshua Haberman <joshua@haberman.com>
- * Copyright (c) 2005-2006 Arve Knudsen <aknuds-1@broadpark.no>
+ * Copyright (c) 2005-2009 Arve Knudsen <arve.knudsen@gmail.com>
+ * Copyright (c) 2008 Kevin Kofler <kevin.kofler@chello.at>
*
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 1999-2002 Ross Bencina, Phil Burk
@@ -62,6 +63,7 @@
#include <sys/mman.h>
#include <signal.h> /* For sig_atomic_t */
+#include "portaudio.h"
#include "pa_util.h"
#include "pa_unix_util.h"
#include "pa_allocation.h"
@@ -98,6 +100,7 @@
static int aErr_; /* Used with ENSURE_ */
static int numPeriods_ = 4;
+static int busyRetries_ = 100;
int PaAlsa_SetNumPeriods( int numPeriods )
{
@@ -117,6 +120,8 @@ typedef struct
unsigned long framesPerBuffer;
int numUserChannels, numHostChannels;
int userInterleaved, hostInterleaved;
+ int canMmap;
+ void *nonMmapBuffer;
PaDeviceIndex device; /* Keep the device index */
snd_pcm_t *pcm;
@@ -320,7 +325,7 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
* and a suitable result returned. The device is closed before returning.
*/
static PaError GropeDevice( snd_pcm_t* pcm, int isPlug, StreamDirection mode, int openBlocking,
- PaAlsaDeviceInfo* devInfo, int* canMmap )
+ PaAlsaDeviceInfo* devInfo )
{
PaError result = paNoError;
snd_pcm_hw_params_t *hwParams;
@@ -353,9 +358,6 @@ static PaError GropeDevice( snd_pcm_t* pcm, int isPlug, StreamDirection mode, in
snd_pcm_hw_params_alloca( &hwParams );
snd_pcm_hw_params_any( pcm, hwParams );
- *canMmap = snd_pcm_hw_params_test_access( pcm, hwParams, SND_PCM_ACCESS_MMAP_INTERLEAVED ) >= 0 ||
- snd_pcm_hw_params_test_access( pcm, hwParams, SND_PCM_ACCESS_MMAP_NONINTERLEAVED ) >= 0;
-
if( defaultSr >= 0 )
{
/* Could be that the device opened in one mode supports samplerates that the other mode wont have,
@@ -538,7 +540,7 @@ static int IgnorePlugin( const char *pluginId )
**/
static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, int mode, int waitOnBusy )
{
- int tries = 0, maxTries = waitOnBusy ? 100 : 0;
+ int tries = 0, maxTries = waitOnBusy ? busyRetries_ : 0;
int ret = snd_pcm_open( pcmp, name, stream, mode );
for( tries = 0; tries < maxTries && -EBUSY == ret; ++tries )
{
@@ -565,7 +567,6 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
PaError result = 0;
PaDeviceInfo *baseDeviceInfo = &devInfo->baseDeviceInfo;
snd_pcm_t *pcm;
- int canMmap = -1;
PaUtilHostApiRepresentation *baseApi = &alsaApi->baseHostApiRep;
/* Zero fields */
@@ -579,8 +580,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_CAPTURE, blocking, 0 )
>= 0 )
{
- if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_In, blocking, devInfo,
- &canMmap ) != paNoError )
+ if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_In, blocking, devInfo ) != paNoError )
{
/* Error */
PA_DEBUG(("%s: Failed groping %s for capture\n", __FUNCTION__, deviceName->alsaName));
@@ -593,8 +593,7 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
OpenPcm( &pcm, deviceName->alsaName, SND_PCM_STREAM_PLAYBACK, blocking, 0 )
>= 0 )
{
- if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_Out, blocking, devInfo,
- &canMmap ) != paNoError )
+ if( GropeDevice( pcm, deviceName->isPlug, StreamDirection_Out, blocking, devInfo ) != paNoError )
{
/* Error */
PA_DEBUG(("%s: Failed groping %s for playback\n", __FUNCTION__, deviceName->alsaName));
@@ -602,12 +601,6 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
}
}
- if( 0 == canMmap )
- {
- PA_DEBUG(("%s: Device %s doesn't support mmap\n", __FUNCTION__, deviceName->alsaName));
- goto end;
- }
-
baseDeviceInfo->structVersion = 2;
baseDeviceInfo->hostApi = alsaApi->hostApiIndex;
baseDeviceInfo->name = deviceName->name;
@@ -620,8 +613,12 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
if( baseDeviceInfo->maxInputChannels > 0 || baseDeviceInfo->maxOutputChannels > 0 )
{
/* Make device default if there isn't already one or it is the ALSA "default" device */
- if( baseApi->info.defaultInputDevice == paNoDevice && baseDeviceInfo->maxInputChannels > 0 )
+ if( (baseApi->info.defaultInputDevice == paNoDevice || !strcmp(deviceName->alsaName,
+ "default" )) && baseDeviceInfo->maxInputChannels > 0 )
+ {
baseApi->info.defaultInputDevice = *devIdx;
+ PA_DEBUG(("Default input device: %s\n", deviceName->name));
+ }
if( (baseApi->info.defaultOutputDevice == paNoDevice || !strcmp(deviceName->alsaName,
"default" )) && baseDeviceInfo->maxOutputChannels > 0 )
{
@@ -1192,6 +1189,8 @@ static PaError PaAlsaStreamComponent_Initialize( PaAlsaStreamComponent *self, Pa
self->hostInterleaved = self->userInterleaved = !(userSampleFormat & paNonInterleaved);
self->numUserChannels = params->channelCount;
self->streamDir = streamDir;
+ self->canMmap = 0;
+ self->nonMmapBuffer = NULL;
if( !callbackMode && !self->userInterleaved )
{
@@ -1234,6 +1233,7 @@ static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *se
PaError result = paNoError;
snd_pcm_access_t accessMode, alternateAccessMode;
+ snd_pcm_access_t rwAccessMode, alternateRwAccessMode;
int dir = 0;
snd_pcm_t *pcm = self->pcm;
double sr = *sampleRate;
@@ -1253,32 +1253,40 @@ static PaError PaAlsaStreamComponent_InitialConfigure( PaAlsaStreamComponent *se
if( self->userInterleaved )
{
accessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+ rwAccessMode = SND_PCM_ACCESS_RW_INTERLEAVED;
alternateAccessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+ alternateRwAccessMode = SND_PCM_ACCESS_RW_NONINTERLEAVED;
}
else
{
accessMode = SND_PCM_ACCESS_MMAP_NONINTERLEAVED;
+ rwAccessMode = SND_PCM_ACCESS_RW_NONINTERLEAVED;
alternateAccessMode = SND_PCM_ACCESS_MMAP_INTERLEAVED;
+ alternateRwAccessMode = SND_PCM_ACCESS_RW_INTERLEAVED;
}
/* If requested access mode fails, try alternate mode */
+ self->canMmap = 1;
if( snd_pcm_hw_params_set_access( pcm, hwParams, accessMode ) < 0 )
{
- int err = 0;
- if( (err = snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode )) < 0)
+ if( snd_pcm_hw_params_set_access( pcm, hwParams, rwAccessMode ) >= 0 )
+ self->canMmap = 0;
+ else
{
- result = paUnanticipatedHostError;
- if( -EINVAL == err )
- {
- PaUtil_SetLastHostErrorInfo( paALSA, err, "PA ALSA requires that a device supports mmap access" );
- }
- else
+ if( snd_pcm_hw_params_set_access( pcm, hwParams, alternateAccessMode ) < 0 )
{
- PaUtil_SetLastHostErrorInfo( paALSA, err, snd_strerror( err ) );
+ int err = 0;
+ if( (err = snd_pcm_hw_params_set_access( pcm, hwParams, alternateRwAccessMode )) >= 0)
+ self->canMmap = 0;
+ else
+ {
+ result = paUnanticipatedHostError;
+ PaUtil_SetLastHostErrorInfo( paALSA, err, snd_strerror( err ) );
+ goto error;
+ }
}
- goto error;
+ /* Flip mode */
+ self->hostInterleaved = !self->userInterleaved;
}
- /* Flip mode */
- self->hostInterleaved = !self->userInterleaved;
}
ENSURE_( snd_pcm_hw_params_set_format( pcm, hwParams, self->nativeFormat ), paUnanticipatedHostError );
@@ -1356,7 +1364,10 @@ static PaError PaAlsaStreamComponent_FinishConfigure( PaAlsaStreamComponent *sel
ENSURE_( snd_pcm_sw_params_set_avail_min( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError );
ENSURE_( snd_pcm_sw_params_set_xfer_align( self->pcm, swParams, 1 ), paUnanticipatedHostError );
- ENSURE_( snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError );
+#ifndef SND_PCM_TSTAMP_ENABLE /* old versions of ALSA called this something different */
+#define SND_PCM_TSTAMP_ENABLE SND_PCM_TSTAMP_MMAP
+#endif
+ ENSURE_( snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_ENABLE ), paUnanticipatedHostError );
/* Set the parameters! */
ENSURE_( snd_pcm_sw_params( self->pcm, swParams ), paUnanticipatedHostError );
@@ -1584,6 +1595,10 @@ static PaError PaAlsaStreamComponent_DetermineFramesPerBuffer( PaAlsaStreamCompo
}
}
+ /* non-mmap mode needs a reasonably-sized buffer or it'll stutter */
+ if( !self->canMmap && framesPerHostBuffer < 2048 )
+ framesPerHostBuffer = 2048;
+
assert( framesPerHostBuffer > 0 );
{
snd_pcm_uframes_t min = 0, max = 0;
@@ -1725,12 +1740,15 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
while( optimalPeriodSize >= periodSize )
{
- if( snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, optimalPeriodSize, 0 ) < 0 )
- continue;
- if( snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback, optimalPeriodSize, 0 ) >= 0 )
+ if( snd_pcm_hw_params_test_period_size( self->capture.pcm, hwParamsCapture, optimalPeriodSize, 0 )
+ >= 0 && snd_pcm_hw_params_test_period_size( self->playback.pcm, hwParamsPlayback,
+ optimalPeriodSize, 0 ) >= 0 )
+ {
break;
+ }
optimalPeriodSize /= 2;
}
+
if( optimalPeriodSize > periodSize )
periodSize = optimalPeriodSize;
@@ -1823,12 +1841,13 @@ static PaError PaAlsaStream_DetermineFramesPerBuffer( PaAlsaStream* self, double
PA_UNLESS( framesPerHostBuffer != 0, paInternalError );
self->maxFramesPerHostBuffer = framesPerHostBuffer;
- if( !accurate )
+ if( !self->playback.canMmap || !accurate )
{
/* Don't know the exact size per host buffer */
*hostBufferSizeMode = paUtilBoundedHostBufferSize;
/* Raise upper bound */
- ++self->maxFramesPerHostBuffer;
+ if( !accurate )
+ ++self->maxFramesPerHostBuffer;
}
error:
@@ -1987,11 +2006,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* Ok, buffer processor is initialized, now we can deduce it's latency */
if( numInputChannels > 0 )
- stream->streamRepresentation.streamInfo.inputLatency = inputLatency + PaUtil_GetBufferProcessorInputLatency(
- &stream->bufferProcessor );
+ stream->streamRepresentation.streamInfo.inputLatency = inputLatency + (PaTime)(
+ PaUtil_GetBufferProcessorInputLatency( &stream->bufferProcessor ) / sampleRate);
if( numOutputChannels > 0 )
- stream->streamRepresentation.streamInfo.outputLatency = outputLatency + PaUtil_GetBufferProcessorOutputLatency(
- &stream->bufferProcessor );
+ stream->streamRepresentation.streamInfo.outputLatency = outputLatency + (PaTime)(
+ PaUtil_GetBufferProcessorOutputLatency( &stream->bufferProcessor ) / sampleRate);
*s = (PaStream*)stream;
@@ -2051,9 +2070,11 @@ static PaError AlsaStart( PaAlsaStream *stream, int priming )
{
/* Buffer isn't primed, so prepare and silence */
ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
- SilenceBuffer( stream );
+ if( stream->playback.canMmap )
+ SilenceBuffer( stream );
}
- ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
+ if( stream->playback.canMmap )
+ ENSURE_( snd_pcm_start( stream->playback.pcm ), paUnanticipatedHostError );
}
else
ENSURE_( snd_pcm_prepare( stream->playback.pcm ), paUnanticipatedHostError );
@@ -2151,7 +2172,9 @@ error:
static PaError AlsaStop( PaAlsaStream *stream, int abort )
{
PaError result = paNoError;
- /* XXX: Seems that draining the dmix device may trigger a race condition in ALSA */
+ /* XXX: snd_pcm_drain tends to lock up, avoid it until we find out more */
+ abort = 1;
+ /*
if( stream->capture.pcm && !strcmp( Pa_GetDeviceInfo( stream->capture.device )->name,
"dmix" ) )
{
@@ -2162,6 +2185,7 @@ static PaError AlsaStop( PaAlsaStream *stream, int abort )
{
abort = 1;
}
+ */
if( abort )
{
@@ -2379,6 +2403,7 @@ static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
snd_pcm_status_t *st;
PaTime now = PaUtil_GetTime();
snd_timestamp_t t;
+ int errplayback = 0, errcapture = 0;
snd_pcm_status_alloca( &st );
@@ -2389,6 +2414,7 @@ static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
{
snd_pcm_status_get_trigger_tstamp( st, &t );
self->underrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
+ errplayback = snd_pcm_recover( self->playback.pcm, -EPIPE, 0 );
}
}
if( self->capture.pcm )
@@ -2398,10 +2424,12 @@ static PaError PaAlsaStream_HandleXrun( PaAlsaStream *self )
{
snd_pcm_status_get_trigger_tstamp( st, &t );
self->overrun = now * 1000 - ((PaTime) t.tv_sec * 1000 + (PaTime) t.tv_usec / 1000);
+ errcapture = snd_pcm_recover( self->capture.pcm, -EPIPE, 0 );
}
}
- PA_ENSURE( AlsaRestart( self ) );
+ if( errplayback || errcapture )
+ PA_ENSURE( AlsaRestart( self ) );
end:
return result;
@@ -2552,7 +2580,7 @@ static void CalculateTimeInfo( PaAlsaStream *stream, PaStreamCallbackTimeInfo *t
static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self, unsigned long numFrames, int *xrun )
{
PaError result = paNoError;
- int res;
+ int res = 0;
/* @concern FullDuplex It is possible that only one direction is marked ready after polling, and processed
* afterwards
@@ -2560,7 +2588,34 @@ static PaError PaAlsaStreamComponent_EndProcessing( PaAlsaStreamComponent *self,
if( !self->ready )
goto end;
- res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames );
+ if( !self->canMmap && StreamDirection_Out == self->streamDir )
+ {
+ /* Play sound */
+ if( self->hostInterleaved )
+ res = snd_pcm_writei( self->pcm, self->nonMmapBuffer, numFrames );
+ else
+ {
+ void *bufs[self->numHostChannels];
+ int bufsize = snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer + 1 );
+ unsigned char *buffer = self->nonMmapBuffer;
+ int i;
+ for( i = 0; i < self->numHostChannels; ++i )
+ {
+ bufs[i] = buffer;
+ buffer += bufsize;
+ }
+ res = snd_pcm_writen( self->pcm, bufs, numFrames );
+ }
+ }
+
+ if( self->canMmap )
+ res = snd_pcm_mmap_commit( self->pcm, self->offset, numFrames );
+ else
+ {
+ free( self->nonMmapBuffer );
+ self->nonMmapBuffer = NULL;
+ }
+
if( res == -EPIPE || res == -ESTRPIPE )
{
*xrun = 1;
@@ -2600,7 +2655,7 @@ static PaError PaAlsaStreamComponent_DoChannelAdaption( PaAlsaStreamComponent *s
if( self->hostInterleaved )
{
int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
- unsigned char *buffer = ExtractAddress( self->channelAreas, self->offset );
+ unsigned char *buffer = self->canMmap ? ExtractAddress( self->channelAreas, self->offset ) : self->nonMmapBuffer;
/* Start after the last user channel */
p = buffer + self->numUserChannels * swidth;
@@ -2980,13 +3035,23 @@ static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent* se
goto end;
}
- ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError );
+ if( self->canMmap )
+ {
+ ENSURE_( snd_pcm_mmap_begin( self->pcm, &areas, &self->offset, numFrames ), paUnanticipatedHostError );
+ /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */
+ self->channelAreas = (snd_pcm_channel_area_t *)areas;
+ }
+ else
+ {
+ free( self->nonMmapBuffer );
+ self->nonMmapBuffer = calloc( self->numHostChannels, snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer + 1 ) );
+ }
if( self->hostInterleaved )
{
int swidth = snd_pcm_format_size( self->nativeFormat, 1 );
- p = buffer = ExtractAddress( areas, self->offset );
+ p = buffer = self->canMmap ? ExtractAddress( areas, self->offset ) : self->nonMmapBuffer;
for( i = 0; i < self->numUserChannels; ++i )
{
/* We're setting the channels up to userChannels, but the stride will be hostChannels samples */
@@ -2996,16 +3061,52 @@ static PaError PaAlsaStreamComponent_RegisterChannels( PaAlsaStreamComponent* se
}
else
{
- for( i = 0; i < self->numUserChannels; ++i )
+ if( self->canMmap )
+ for( i = 0; i < self->numUserChannels; ++i )
+ {
+ area = areas + i;
+ buffer = ExtractAddress( area, self->offset );
+ setChannel( bp, i, buffer, 1 );
+ }
+ else
{
- area = areas + i;
- buffer = ExtractAddress( area, self->offset );
- setChannel( bp, i, buffer, 1 );
+ int bufsize = snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer + 1 );
+ buffer = self->nonMmapBuffer;
+ for( i = 0; i < self->numUserChannels; ++i )
+ {
+ setChannel( bp, i, buffer, 1 );
+ buffer += bufsize;
+ }
}
}
- /* @concern ChannelAdaption Buffer address is recorded so we can do some channel adaption later */
- self->channelAreas = (snd_pcm_channel_area_t *)areas;
+ if( !self->canMmap && StreamDirection_In == self->streamDir )
+ {
+ /* Read sound */
+ int res;
+ if( self->hostInterleaved )
+ res = snd_pcm_readi( self->pcm, self->nonMmapBuffer, *numFrames );
+ else
+ {
+ void *bufs[self->numHostChannels];
+ int bufsize = snd_pcm_format_size( self->nativeFormat, self->framesPerBuffer + 1 );
+ unsigned char *buffer = self->nonMmapBuffer;
+ int i;
+ for( i = 0; i < self->numHostChannels; ++i )
+ {
+ bufs[i] = buffer;
+ buffer += bufsize;
+ }
+ res = snd_pcm_readn( self->pcm, bufs, *numFrames );
+ }
+ if( res == -EPIPE || res == -ESTRPIPE )
+ {
+ *xrun = 1;
+ *numFrames = 0;
+ free( self->nonMmapBuffer );
+ self->nonMmapBuffer = NULL;
+ }
+ }
end:
error:
@@ -3519,10 +3620,31 @@ void PaAlsa_EnableWatchdog( PaStream *s, int enable )
}
#endif
+static PaError GetAlsaStreamPointer( PaStream* s, PaAlsaStream** stream )
+{
+ PaError result = paNoError;
+ PaUtilHostApiRepresentation* hostApi;
+ PaAlsaHostApiRepresentation* alsaHostApi;
+
+ PA_ENSURE( PaUtil_ValidateStreamPointer( s ) );
+ PA_ENSURE( PaUtil_GetHostApiRepresentation( &hostApi, paALSA ) );
+ alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi;
+
+ PA_UNLESS( PA_STREAM_REP( s )->streamInterface == &alsaHostApi->callbackStreamInterface
+ || PA_STREAM_REP( s )->streamInterface == &alsaHostApi->blockingStreamInterface,
+ paIncompatibleStreamHostApi );
+
+ *stream = (PaAlsaStream*)s;
+error:
+ return paNoError;
+}
+
PaError PaAlsa_GetStreamInputCard(PaStream* s, int* card) {
- PaAlsaStream *stream = (PaAlsaStream *) s;
- snd_pcm_info_t* pcmInfo;
+ PaAlsaStream *stream;
PaError result = paNoError;
+ snd_pcm_info_t* pcmInfo;
+
+ PA_ENSURE( GetAlsaStreamPointer( s, &stream ) );
/* XXX: More descriptive error? */
PA_UNLESS( stream->capture.pcm, paDeviceUnavailable );
@@ -3536,9 +3658,11 @@ error:
}
PaError PaAlsa_GetStreamOutputCard(PaStream* s, int* card) {
- PaAlsaStream *stream = (PaAlsaStream *) s;
- snd_pcm_info_t* pcmInfo;
+ PaAlsaStream *stream;
PaError result = paNoError;
+ snd_pcm_info_t* pcmInfo;
+
+ PA_ENSURE( GetAlsaStreamPointer( s, &stream ) );
/* XXX: More descriptive error? */
PA_UNLESS( stream->playback.pcm, paDeviceUnavailable );
@@ -3550,3 +3674,9 @@ PaError PaAlsa_GetStreamOutputCard(PaStream* s, int* card) {
error:
return result;
}
+
+PaError PaAlsa_SetRetriesBusy( int retries )
+{
+ busyRetries_ = retries;
+ return paNoError;
+}
diff --git a/pd/portaudio/src/hostapi/asio/pa_asio.cpp b/pd/portaudio/src/hostapi/asio/pa_asio.cpp
index 4b3fb68e..84d1c511 100644
--- a/pd/portaudio/src/hostapi/asio/pa_asio.cpp
+++ b/pd/portaudio/src/hostapi/asio/pa_asio.cpp
@@ -1,10 +1,12 @@
/*
- * $Id: pa_asio.cpp 1230 2007-06-15 16:16:33Z rossb $
+ * $Id: pa_asio.cpp 1416 2009-06-16 16:12:41Z rossb $
* Portable Audio I/O Library for ASIO Drivers
*
* Author: Stephane Letz
* Based on the Open Source API proposed by Ross Bencina
* Copyright (c) 2000-2002 Stephane Letz, Phil Burk, Ross Bencina
+ * Blocking i/o implementation by Sven Fischer, Institute of Hearing
+ * Technology and Audiology (www.hoertechnik-audiologie.de)
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files
@@ -71,18 +73,18 @@
*/
/** @file
- @ingroup hostapi_src
+ @ingroup hostapi_src
Note that specific support for paInputUnderflow, paOutputOverflow and
paNeverDropInput is not necessary or possible with this driver due to the
synchronous full duplex double-buffered architecture of ASIO.
- @todo check that CoInitialize()/CoUninitialize() are always correctly
- paired, even in error cases.
-
@todo implement host api specific extension to set i/o buffer sizes in frames
- @todo implement ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable
+ @todo review ReadStream, WriteStream, GetStreamReadAvailable, GetStreamWriteAvailable
+
+ @todo review Blocking i/o latency computations in OpenStream(), changing ring
+ buffer to a non-power-of-two structure could reduce blocking i/o latency.
@todo implement IsFormatSupported
@@ -120,6 +122,7 @@
#include <assert.h>
#include <string.h>
//#include <values.h>
+#include <new>
#include <windows.h>
#include <mmsystem.h>
@@ -133,6 +136,7 @@
#include "pa_cpuload.h"
#include "pa_process.h"
#include "pa_debugprint.h"
+#include "pa_ringbuffer.h"
/* This version of pa_asio.cpp is currently only targetted at Win32,
It would require a few tweaks to work with pre-OS X Macintosh.
@@ -164,16 +168,24 @@
#endif
*/
-/* external references */
-extern AsioDrivers* asioDrivers ;
-bool loadAsioDriver(char *name);
+/* external reference to ASIO SDK's asioDrivers.
-/* We are trying to be compatible with CARBON but this has not been thoroughly tested. */
-/* not tested at all since new code was introduced. */
-#define CARBON_COMPATIBLE (0)
+ This is a bit messy because we want to explicitly manage
+ allocation/deallocation of this structure, but some layers of the SDK
+ which we currently use (eg the implementation in asio.cpp) still
+ use this global version.
+
+ For now we keep it in sync with our local instance in the host
+ API representation structure, but later we should be able to remove
+ all dependence on it.
+*/
+extern AsioDrivers* asioDrivers;
+/* We are trying to be compatible with CARBON but this has not been thoroughly tested. */
+/* not tested at all since new V19 code was introduced. */
+#define CARBON_COMPATIBLE (0)
/* prototypes for functions declared in this file */
@@ -206,6 +218,14 @@ static PaError WriteStream( PaStream* stream, const void *buffer, unsigned long
static signed long GetStreamReadAvailable( PaStream* stream );
static signed long GetStreamWriteAvailable( PaStream* stream );
+/* Blocking i/o callback function. */
+static int BlockingIoPaCallback(const void *inputBuffer ,
+ void *outputBuffer ,
+ unsigned long framesPerBuffer,
+ const PaStreamCallbackTimeInfo *timeInfo ,
+ PaStreamCallbackFlags statusFlags ,
+ void *userData );
+
/* our ASIO callback functions */
static void bufferSwitch(long index, ASIOBool processNow);
@@ -270,12 +290,12 @@ static const char* PaAsio_GetAsioErrorText( ASIOError asioError )
// Atomic increment and decrement operations
#if MAC
- /* need to be implemented on Mac */
- inline long PaAsio_AtomicIncrement(volatile long* v) {return ++(*const_cast<long*>(v));}
- inline long PaAsio_AtomicDecrement(volatile long* v) {return --(*const_cast<long*>(v));}
+ /* need to be implemented on Mac */
+ inline long PaAsio_AtomicIncrement(volatile long* v) {return ++(*const_cast<long*>(v));}
+ inline long PaAsio_AtomicDecrement(volatile long* v) {return --(*const_cast<long*>(v));}
#elif WINDOWS
- inline long PaAsio_AtomicIncrement(volatile long* v) {return InterlockedIncrement(const_cast<long*>(v));}
- inline long PaAsio_AtomicDecrement(volatile long* v) {return InterlockedDecrement(const_cast<long*>(v));}
+ inline long PaAsio_AtomicIncrement(volatile long* v) {return InterlockedIncrement(const_cast<long*>(v));}
+ inline long PaAsio_AtomicDecrement(volatile long* v) {return InterlockedDecrement(const_cast<long*>(v));}
#endif
@@ -300,6 +320,7 @@ typedef struct
PaUtilAllocationGroup *allocations;
+ AsioDrivers *asioDrivers;
void *systemSpecific;
/* the ASIO C API only allows one ASIO driver to be open at a time,
@@ -323,7 +344,7 @@ PaAsioHostApiRepresentation;
Retrieve <driverCount> driver names from ASIO, returned in a char**
allocated in <group>.
*/
-static char **GetAsioDriverNames( PaUtilAllocationGroup *group, long driverCount )
+static char **GetAsioDriverNames( PaAsioHostApiRepresentation *asioHostApi, PaUtilAllocationGroup *group, long driverCount )
{
char **result = 0;
int i;
@@ -341,7 +362,7 @@ static char **GetAsioDriverNames( PaUtilAllocationGroup *group, long driverCount
for( i=0; i<driverCount; ++i )
result[i] = result[0] + (32 * i);
- asioDrivers->getDriverNames( result, driverCount );
+ asioHostApi->asioDrivers->getDriverNames( result, driverCount );
error:
return result;
@@ -917,7 +938,7 @@ PaAsioDeviceInfo;
PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device,
- long *minLatency, long *maxLatency, long *preferredLatency, long *granularity )
+ long *minLatency, long *maxLatency, long *preferredLatency, long *granularity )
{
PaError result;
PaUtilHostApiRepresentation *hostApi;
@@ -944,23 +965,45 @@ PaError PaAsio_GetAvailableLatencyValues( PaDeviceIndex device,
return result;
}
-
+/* Unload whatever we loaded in LoadAsioDriver().
+ Also balance the call to CoInitialize(0).
+*/
+static void UnloadAsioDriver( void )
+{
+ ASIOExit();
+ CoUninitialize();
+}
/*
load the asio driver named by <driverName> and return statistics about
the driver in info. If no error occurred, the driver will remain open
- and must be closed by the called by calling ASIOExit() - if an error
- is returned the driver will already be closed.
+ and must be closed by the called by calling UnloadAsioDriver() - if an error
+ is returned the driver will already be unloaded.
*/
-static PaError LoadAsioDriver( const char *driverName,
+static PaError LoadAsioDriver( PaAsioHostApiRepresentation *asioHostApi, const char *driverName,
PaAsioDriverInfo *driverInfo, void *systemSpecific )
{
PaError result = paNoError;
ASIOError asioError;
int asioIsInitialized = 0;
- if( !loadAsioDriver( const_cast<char*>(driverName) ) )
+ /*
+ ASIO uses CoCreateInstance() to load a driver. That requires that
+ CoInitialize(0) be called for every thread that loads a driver.
+ It is OK to call CoInitialize(0) multiple times form one thread as long
+ as it is balanced by a call to CoUninitialize(). See UnloadAsioDriver().
+
+ The V18 version called CoInitialize() starting on 2/19/02.
+ That was removed from PA V19 for unknown reasons.
+ Phil Burk added it back on 6/27/08 so that JSyn would work.
+ */
+ CoInitialize( 0 );
+
+ if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(driverName) ) )
{
+ /* If this returns an error then it might be because CoInitialize(0) was removed.
+ It should be called right before this.
+ */
result = paUnanticipatedHostError;
PA_ASIO_SET_LAST_HOST_ERROR( 0, "Failed to load ASIO driver" );
goto error;
@@ -1006,8 +1049,10 @@ static PaError LoadAsioDriver( const char *driverName,
error:
if( asioIsInitialized )
- ASIOExit();
-
+ {
+ ASIOExit();
+ }
+ CoUninitialize();
return result;
}
@@ -1039,6 +1084,8 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
goto error;
}
+ asioHostApi->asioDrivers = 0; /* avoid surprises in our error handler below */
+
asioHostApi->allocations = PaUtil_CreateAllocationGroup();
if( !asioHostApi->allocations )
{
@@ -1046,6 +1093,25 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
goto error;
}
+ /* Allocate the AsioDrivers() driver list (class from ASIO SDK) */
+ try
+ {
+ asioHostApi->asioDrivers = new AsioDrivers(); /* calls CoInitialize(0) */
+ }
+ catch (std::bad_alloc)
+ {
+ asioHostApi->asioDrivers = 0;
+ }
+ /* some implementations of new (ie MSVC, see http://support.microsoft.com/?kbid=167733)
+ don't throw std::bad_alloc, so we also explicitly test for a null return. */
+ if( asioHostApi->asioDrivers == 0 )
+ {
+ result = paInsufficientMemory;
+ goto error;
+ }
+
+ asioDrivers = asioHostApi->asioDrivers; /* keep SDK global in sync until we stop depending on it */
+
asioHostApi->systemSpecific = 0;
asioHostApi->openAsioDeviceIndex = paNoDevice;
@@ -1059,23 +1125,19 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
#ifdef WINDOWS
/* use desktop window as system specific ptr */
asioHostApi->systemSpecific = GetDesktopWindow();
- CoInitialize(NULL);
#endif
- /* MUST BE CHECKED : to force fragments loading on Mac */
- loadAsioDriver( "dummy" );
-
/* driverCount is the number of installed drivers - not necessarily
the number of installed physical devices. */
#if MAC
- driverCount = asioDrivers->getNumFragments();
+ driverCount = asioHostApi->asioDrivers->getNumFragments();
#elif WINDOWS
- driverCount = asioDrivers->asioGetNumDev();
+ driverCount = asioHostApi->asioDrivers->asioGetNumDev();
#endif
if( driverCount > 0 )
{
- names = GetAsioDriverNames( asioHostApi->allocations, driverCount );
+ names = GetAsioDriverNames( asioHostApi, asioHostApi->allocations, driverCount );
if( !names )
{
result = paInsufficientMemory;
@@ -1102,7 +1164,7 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
goto error;
}
- IsDebuggerPresent_ = GetProcAddress( LoadLibrary( "Kernel32.dll" ), "IsDebuggerPresent" );
+ IsDebuggerPresent_ = GetProcAddress( LoadLibrary( "Kernel32.dll" ), "IsDebuggerPresent" );
for( i=0; i < driverCount; ++i )
{
@@ -1120,7 +1182,6 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
|| strcmp (names[i],"ASIO Multimedia Driver") == 0
|| strncmp(names[i],"Premiere",8) == 0 //"Premiere Elements Windows Sound 1.0"
|| strncmp(names[i],"Adobe",5) == 0 //"Adobe Default Windows Sound 1.5"
- || strncmp(names[i],"ReaRoute ASIO",13) == 0 //Reaper www.reaper.fm <- fix your stuff man.
)
{
PA_DEBUG(("BLACKLISTED!!!\n"));
@@ -1141,7 +1202,7 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
/* Attempt to load the asio driver... */
- if( LoadAsioDriver( names[i], &paAsioDriverInfo, asioHostApi->systemSpecific ) == paNoError )
+ if( LoadAsioDriver( asioHostApi, names[i], &paAsioDriverInfo, asioHostApi->systemSpecific ) == paNoError )
{
PaAsioDeviceInfo *asioDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
PaDeviceInfo *deviceInfo = &asioDeviceInfo->commonDeviceInfo;
@@ -1233,7 +1294,7 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
if( !asioDeviceInfo->asioChannelInfos )
{
result = paInsufficientMemory;
- goto error;
+ goto error_unload;
}
int a;
@@ -1246,7 +1307,7 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
{
result = paUnanticipatedHostError;
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
- goto error;
+ goto error_unload;
}
}
@@ -1259,13 +1320,13 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
{
result = paUnanticipatedHostError;
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
- goto error;
+ goto error_unload;
}
}
/* unload the driver */
- ASIOExit();
+ UnloadAsioDriver();
(*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
++(*hostApi)->info.deviceCount;
@@ -1302,6 +1363,9 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
return result;
+error_unload:
+ UnloadAsioDriver();
+
error:
if( asioHostApi )
{
@@ -1311,6 +1375,9 @@ error:
PaUtil_DestroyAllocationGroup( asioHostApi->allocations );
}
+ delete asioHostApi->asioDrivers;
+ asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
+
PaUtil_FreeMemory( asioHostApi );
}
return result;
@@ -1323,7 +1390,7 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
/*
IMPLEMENT ME:
- - clean up any resources not handled by the allocation group
+ - clean up any resources not handled by the allocation group (need to review if there are any)
*/
if( asioHostApi->allocations )
@@ -1332,6 +1399,9 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
PaUtil_DestroyAllocationGroup( asioHostApi->allocations );
}
+ delete asioHostApi->asioDrivers; /* calls CoUninitialize() */
+ asioDrivers = 0; /* keep SDK global in sync until we stop depending on it */
+
PaUtil_FreeMemory( asioHostApi );
}
@@ -1418,7 +1488,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
/* if an ASIO device is open we can only get format information for the currently open device */
if( asioHostApi->openAsioDeviceIndex != paNoDevice
- && asioHostApi->openAsioDeviceIndex != asioDeviceIndex )
+ && asioHostApi->openAsioDeviceIndex != asioDeviceIndex )
{
return paDeviceUnavailable;
}
@@ -1430,7 +1500,7 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
/* open the device if it's not already open */
if( asioHostApi->openAsioDeviceIndex == paNoDevice )
{
- result = LoadAsioDriver( asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name,
+ result = LoadAsioDriver( asioHostApi, asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name,
driverInfo, asioHostApi->systemSpecific );
if( result != paNoError )
return result;
@@ -1468,7 +1538,7 @@ done:
/* close the device if it wasn't already open */
if( asioHostApi->openAsioDeviceIndex == paNoDevice )
{
- ASIOExit(); /* not sure if we should check for errors here */
+ UnloadAsioDriver(); /* not sure if we should check for errors here */
}
if( result == paNoError )
@@ -1479,6 +1549,40 @@ done:
+/** A data structure specifically for storing blocking i/o related data. */
+typedef struct PaAsioStreamBlockingState
+{
+ int stopFlag; /**< Flag indicating that block processing is to be stopped. */
+
+ unsigned long writeBuffersRequested; /**< The number of available output buffers, requested by the #WriteStream() function. */
+ unsigned long readFramesRequested; /**< The number of available input frames, requested by the #ReadStream() function. */
+
+ int writeBuffersRequestedFlag; /**< Flag to indicate that #WriteStream() has requested more output buffers to be available. */
+ int readFramesRequestedFlag; /**< Flag to indicate that #ReadStream() requires more input frames to be available. */
+
+ HANDLE writeBuffersReadyEvent; /**< Event to signal that requested output buffers are available. */
+ HANDLE readFramesReadyEvent; /**< Event to signal that requested input frames are available. */
+
+ void *writeRingBufferData; /**< The actual ring buffer memory, used by the output ring buffer. */
+ void *readRingBufferData; /**< The actual ring buffer memory, used by the input ring buffer. */
+
+ PaUtilRingBuffer writeRingBuffer; /**< Frame-aligned blocking i/o ring buffer to store output data (interleaved user format). */
+ PaUtilRingBuffer readRingBuffer; /**< Frame-aligned blocking i/o ring buffer to store input data (interleaved user format). */
+
+ long writeRingBufferInitialFrames; /**< The initial number of silent frames within the output ring buffer. */
+
+ const void **writeStreamBuffer; /**< Temp buffer, used by #WriteStream() for handling non-interleaved data. */
+ void **readStreamBuffer; /**< Temp buffer, used by #ReadStream() for handling non-interleaved data. */
+
+ PaUtilBufferProcessor bufferProcessor; /**< Buffer processor, used to handle the blocking i/o ring buffers. */
+
+ int outputUnderflowFlag; /**< Flag to signal an output underflow from within the callback function. */
+ int inputOverflowFlag; /**< Flag to signal an input overflow from within the callback function. */
+}
+PaAsioStreamBlockingState;
+
+
+
/* PaAsioStream - a stream data structure specifically for this implementation */
typedef struct PaAsioStream
@@ -1515,6 +1619,7 @@ typedef struct PaAsioStream
HANDLE completedBuffersPlayedEvent;
bool streamFinishedCallbackCalled;
+ int isStopped;
volatile int isActive;
volatile bool zeroOutput; /* all future calls to the callback will output silence */
@@ -1522,6 +1627,8 @@ typedef struct PaAsioStream
volatile long reenterError;
PaStreamCallbackFlags callbackFlags;
+
+ PaAsioStreamBlockingState *blockingState; /**< Blocking i/o data struct, or NULL when using callback interface. */
}
PaAsioStream;
@@ -1621,15 +1728,15 @@ static PaError ValidateAsioSpecificStreamInfo(
int deviceChannelCount,
int **channelSelectors )
{
- if( streamInfo )
- {
- if( streamInfo->size != sizeof( PaAsioStreamInfo )
- || streamInfo->version != 1 )
- {
- return paIncompatibleHostApiSpecificStreamInfo;
- }
+ if( streamInfo )
+ {
+ if( streamInfo->size != sizeof( PaAsioStreamInfo )
+ || streamInfo->version != 1 )
+ {
+ return paIncompatibleHostApiSpecificStreamInfo;
+ }
- if( streamInfo->flags & paAsioUseChannelSelectors )
+ if( streamInfo->flags & paAsioUseChannelSelectors )
*channelSelectors = streamInfo->channelSelectors;
if( !(*channelSelectors) )
@@ -1641,9 +1748,101 @@ static PaError ValidateAsioSpecificStreamInfo(
return paInvalidChannelCount;
}
}
- }
+ }
+
+ return paNoError;
+}
+
+
+static bool IsUsingExternalClockSource()
+{
+ bool result = false;
+ ASIOError asioError;
+ ASIOClockSource clocks[32];
+ long numSources=32;
- return paNoError;
+ /* davidv: listing ASIO Clock sources. there is an ongoing investigation by
+ me about whether or not to call ASIOSetSampleRate if an external Clock is
+ used. A few drivers expected different things here */
+
+ asioError = ASIOGetClockSources(clocks, &numSources);
+ if( asioError != ASE_OK ){
+ PA_DEBUG(("ERROR: ASIOGetClockSources: %s\n", PaAsio_GetAsioErrorText(asioError) ));
+ }else{
+ PA_DEBUG(("INFO ASIOGetClockSources listing %d clocks\n", numSources ));
+ for (int i=0;i<numSources;++i){
+ PA_DEBUG(("ASIOClockSource%d %s current:%d\n", i, clocks[i].name, clocks[i].isCurrentSource ));
+
+ if (clocks[i].isCurrentSource)
+ result = true;
+ }
+ }
+
+ return result;
+}
+
+
+static PaError ValidateAndSetSampleRate( double sampleRate )
+{
+ PaError result = paNoError;
+ ASIOError asioError;
+
+ // check that the device supports the requested sample rate
+
+ asioError = ASIOCanSampleRate( sampleRate );
+ PA_DEBUG(("ASIOCanSampleRate(%f):%d\n", sampleRate, asioError ));
+
+ if( asioError != ASE_OK )
+ {
+ result = paInvalidSampleRate;
+ PA_DEBUG(("ERROR: ASIOCanSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
+ goto error;
+ }
+
+ // retrieve the current sample rate, we only change to the requested
+ // sample rate if the device is not already in that rate.
+
+ ASIOSampleRate oldRate;
+ asioError = ASIOGetSampleRate(&oldRate);
+ if( asioError != ASE_OK )
+ {
+ result = paInvalidSampleRate;
+ PA_DEBUG(("ERROR: ASIOGetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
+ goto error;
+ }
+ PA_DEBUG(("ASIOGetSampleRate:%f\n",oldRate));
+
+ if (oldRate != sampleRate){
+ /* Set sample rate */
+
+ PA_DEBUG(("before ASIOSetSampleRate(%f)\n",sampleRate));
+
+ /*
+ If you have problems with some drivers when externally clocked,
+ try switching on the following line and commenting out the one after it.
+ See IsUsingExternalClockSource() for more info.
+ */
+ //if( IsUsingExternalClockSource() ){
+ if( false ){
+ asioError = ASIOSetSampleRate( 0 );
+ }else{
+ asioError = ASIOSetSampleRate( sampleRate );
+ }
+ if( asioError != ASE_OK )
+ {
+ result = paInvalidSampleRate;
+ PA_DEBUG(("ERROR: ASIOSetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
+ goto error;
+ }
+ PA_DEBUG(("after ASIOSetSampleRate(%f)\n",sampleRate));
+ }
+ else
+ {
+ PA_DEBUG(("No Need to change SR\n"));
+ }
+
+error:
+ return result;
}
@@ -1678,23 +1877,38 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaAsioDriverInfo *driverInfo;
int *inputChannelSelectors = 0;
int *outputChannelSelectors = 0;
- bool isExternal = false;
+
+ /* Are we using blocking i/o interface? */
+ int usingBlockingIo = ( !streamCallback ) ? TRUE : FALSE;
+ /* Blocking i/o stuff */
+ long lBlockingBufferSize = 0; /* Desired ring buffer size in samples. */
+ long lBlockingBufferSizePow2 = 0; /* Power-of-2 rounded ring buffer size. */
+ long lBytesPerFrame = 0; /* Number of bytes per input/output frame. */
+ int blockingWriteBuffersReadyEventInitialized = 0; /* Event init flag. */
+ int blockingReadFramesReadyEventInitialized = 0; /* Event init flag. */
+
+ int callbackBufferProcessorInited = FALSE;
+ int blockingBufferProcessorInited = FALSE;
/* unless we move to using lower level ASIO calls, we can only have
one device open at a time */
- if( asioHostApi->openAsioDeviceIndex != paNoDevice ){
+ if( asioHostApi->openAsioDeviceIndex != paNoDevice )
+ {
PA_DEBUG(("OpenStream paDeviceUnavailable\n"));
return paDeviceUnavailable;
}
+ assert( theAsioStream == 0 );
+
if( inputParameters && outputParameters )
{
/* full duplex ASIO stream must use the same device for input and output */
- if( inputParameters->device != outputParameters->device ){
+ if( inputParameters->device != outputParameters->device )
+ {
PA_DEBUG(("OpenStream paBadIODeviceCombination\n"));
return paBadIODeviceCombination;
- }
+ }
}
if( inputParameters )
@@ -1762,12 +1976,12 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* NOTE: we load the driver and use its current settings
rather than the ones in our device info structure which may be stale */
- result = LoadAsioDriver( asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name,
+ result = LoadAsioDriver( asioHostApi, asioHostApi->inheritedHostApiRep.deviceInfos[ asioDeviceIndex ]->name,
driverInfo, asioHostApi->systemSpecific );
if( result == paNoError )
asioIsInitialized = 1;
else{
- PA_DEBUG(("OpenStream ERROR1\n"));
+ PA_DEBUG(("OpenStream ERROR1 - LoadAsioDriver returned %d\n", result));
goto error;
}
@@ -1793,76 +2007,9 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
}
}
-
- /* davidv: listing ASIO Clock sources, there is an ongoing investigation by
- me about whether or not call ASIOSetSampleRate if an external Clock is
- used. A few drivers expected different things here */
- {
- ASIOClockSource clocks[32];
- long numSources=32;
- asioError = ASIOGetClockSources(clocks, &numSources);
- if( asioError != ASE_OK ){
- PA_DEBUG(("ERROR: ASIOGetClockSources: %s\n", PaAsio_GetAsioErrorText(asioError) ));
- }else{
- PA_DEBUG(("INFO ASIOGetClockSources listing %d clocks\n", numSources ));
- for (int i=0;i<numSources;++i){
- PA_DEBUG(("ASIOClockSource%d %s current:%d\n", i,clocks[i].name, clocks[i].isCurrentSource ));
-
- /*
- If you have problems with some drivers when externally clocked,
- uncomment the next two lines
- */
- //if (clocks[i].isCurrentSource)
- // isExternal = true;
- }
- }
- }
-
- // check that the device supports the requested sample rate
-
- asioError = ASIOCanSampleRate( sampleRate );
- PA_DEBUG(("ASIOCanSampleRate(%f):%d\n",sampleRate, asioError ));
-
- if( asioError != ASE_OK )
- {
- result = paInvalidSampleRate;
- PA_DEBUG(("ERROR: ASIOCanSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
- goto error;
- }
-
-
- // retrieve the current sample rate, we only change to the requested
- // sample rate if the device is not already in that rate.
-
- ASIOSampleRate oldRate;
- asioError = ASIOGetSampleRate(&oldRate);
- if( asioError != ASE_OK )
- {
- result = paInvalidSampleRate;
- PA_DEBUG(("ERROR: ASIOGetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
+ result = ValidateAndSetSampleRate( sampleRate );
+ if( result != paNoError )
goto error;
- }
- PA_DEBUG(("ASIOGetSampleRate:%f\n",oldRate));
-
- if (oldRate != sampleRate){
-
- PA_DEBUG(("before ASIOSetSampleRate(%f)\n",sampleRate));
-
- asioError = ASIOSetSampleRate( isExternal?0:sampleRate );
- /* Set sample rate */
- if( asioError != ASE_OK )
- {
- result = paInvalidSampleRate;
- PA_DEBUG(("ERROR: ASIOSetSampleRate: %s\n", PaAsio_GetAsioErrorText(asioError) ));
- goto error;
- }
- PA_DEBUG(("after ASIOSetSampleRate(%f)\n",sampleRate));
- }
- else
- {
- PA_DEBUG(("No Need to change SR\n"));
- }
-
/*
IMPLEMENT ME:
@@ -1884,6 +2031,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PA_DEBUG(("OpenStream ERROR5\n"));
goto error;
}
+ stream->blockingState = NULL; /* Blocking i/o not initialized, yet. */
+
stream->completedBuffersPlayedEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
if( stream->completedBuffersPlayedEvent == NULL )
@@ -1900,15 +2049,19 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream->asioChannelInfos = 0; /* for deallocation in error */
stream->bufferPtrs = 0; /* for deallocation in error */
- if( streamCallback )
+ /* Using blocking i/o interface... */
+ if( usingBlockingIo )
{
+ /* Blocking i/o is implemented by running callback mode, using a special blocking i/o callback. */
+ streamCallback = BlockingIoPaCallback; /* Setup PA to use the ASIO blocking i/o callback. */
+ userData = &theAsioStream; /* The callback user data will be the PA ASIO stream. */
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &asioHostApi->callbackStreamInterface, streamCallback, userData );
+ &asioHostApi->blockingStreamInterface, streamCallback, userData );
}
- else
+ else /* Using callback interface... */
{
PaUtil_InitializeStreamRepresentation( &stream->streamRepresentation,
- &asioHostApi->blockingStreamInterface, streamCallback, userData );
+ &asioHostApi->callbackStreamInterface, streamCallback, userData );
}
@@ -1959,13 +2112,24 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
}
- framesPerHostBuffer = SelectHostBufferSize(
- (( suggestedInputLatencyFrames > suggestedOutputLatencyFrames )
- ? suggestedInputLatencyFrames : suggestedOutputLatencyFrames),
- driverInfo );
+ /* Using blocking i/o interface... */
+ if( usingBlockingIo )
+ {
+/** @todo REVIEW selection of host buffer size for blocking i/o */
+ /* Use default host latency for blocking i/o. */
+ framesPerHostBuffer = SelectHostBufferSize( 0, driverInfo );
+
+ }
+ else /* Using callback interface... */
+ {
+ framesPerHostBuffer = SelectHostBufferSize(
+ (( suggestedInputLatencyFrames > suggestedOutputLatencyFrames )
+ ? suggestedInputLatencyFrames : suggestedOutputLatencyFrames),
+ driverInfo );
+ }
- PA_DEBUG(("PaAsioOpenStream: framesPerHostBuffer :%d\n", framesPerHostBuffer));
+ PA_DEBUG(("PaAsioOpenStream: framesPerHostBuffer :%d\n", framesPerHostBuffer));
asioError = ASIOCreateBuffers( stream->asioBufferInfos,
inputChannelCount+outputChannelCount,
@@ -2103,43 +2267,302 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream->outputBufferConverter = 0;
}
- result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
- inputChannelCount, inputSampleFormat, hostInputSampleFormat,
- outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
- sampleRate, streamFlags, framesPerBuffer,
- framesPerHostBuffer, paUtilFixedHostBufferSize,
- streamCallback, userData );
- if( result != paNoError ){
- PA_DEBUG(("OpenStream ERROR13\n"));
- goto error;
- }
-
ASIOGetLatencies( &stream->inputLatency, &stream->outputLatency );
- stream->streamRepresentation.streamInfo.inputLatency =
- (double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
- + stream->inputLatency) / sampleRate; // seconds
- stream->streamRepresentation.streamInfo.outputLatency =
- (double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
- + stream->outputLatency) / sampleRate; // seconds
- stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
-
- // the code below prints the ASIO latency which doesn't include the
- // buffer processor latency. it reports the added latency separately
- PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
- stream->inputLatency,
- (long)((stream->inputLatency*1000)/ sampleRate),
- PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor),
- (long)((PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)*1000)/ sampleRate)
- ));
-
- PA_DEBUG(("PaAsio : ASIO OuputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
- stream->outputLatency,
- (long)((stream->outputLatency*1000)/ sampleRate),
- PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor),
- (long)((PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)*1000)/ sampleRate)
- ));
+
+ /* Using blocking i/o interface... */
+ if( usingBlockingIo )
+ {
+ /* Allocate the blocking i/o input ring buffer memory. */
+ stream->blockingState = (PaAsioStreamBlockingState*)PaUtil_AllocateMemory( sizeof(PaAsioStreamBlockingState) );
+ if( !stream->blockingState )
+ {
+ result = paInsufficientMemory;
+ PA_DEBUG(("ERROR! Blocking i/o interface struct allocation failed in OpenStream()\n"));
+ goto error;
+ }
+
+ /* Initialize blocking i/o interface struct. */
+ stream->blockingState->readFramesReadyEvent = NULL; /* Uninitialized, yet. */
+ stream->blockingState->writeBuffersReadyEvent = NULL; /* Uninitialized, yet. */
+ stream->blockingState->readRingBufferData = NULL; /* Uninitialized, yet. */
+ stream->blockingState->writeRingBufferData = NULL; /* Uninitialized, yet. */
+ stream->blockingState->readStreamBuffer = NULL; /* Uninitialized, yet. */
+ stream->blockingState->writeStreamBuffer = NULL; /* Uninitialized, yet. */
+ stream->blockingState->stopFlag = TRUE; /* Not started, yet. */
+
+
+ /* If the user buffer is unspecified */
+ if( framesPerBuffer == paFramesPerBufferUnspecified )
+ {
+ /* Make the user buffer the same size as the host buffer. */
+ framesPerBuffer = framesPerHostBuffer;
+ }
+
+
+ /* Initialize callback buffer processor. */
+ result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor ,
+ inputChannelCount ,
+ inputSampleFormat & ~paNonInterleaved , /* Ring buffer. */
+ hostInputSampleFormat , /* Host format. */
+ outputChannelCount ,
+ outputSampleFormat & ~paNonInterleaved, /* Ring buffer. */
+ hostOutputSampleFormat , /* Host format. */
+ sampleRate ,
+ streamFlags ,
+ framesPerBuffer , /* Frames per ring buffer block. */
+ framesPerHostBuffer , /* Frames per asio buffer. */
+ paUtilFixedHostBufferSize ,
+ streamCallback ,
+ userData );
+ if( result != paNoError ){
+ PA_DEBUG(("OpenStream ERROR13\n"));
+ goto error;
+ }
+ callbackBufferProcessorInited = TRUE;
+
+ /* Initialize the blocking i/o buffer processor. */
+ result = PaUtil_InitializeBufferProcessor(&stream->blockingState->bufferProcessor,
+ inputChannelCount ,
+ inputSampleFormat , /* User format. */
+ inputSampleFormat & ~paNonInterleaved , /* Ring buffer. */
+ outputChannelCount ,
+ outputSampleFormat , /* User format. */
+ outputSampleFormat & ~paNonInterleaved, /* Ring buffer. */
+ sampleRate ,
+ paClipOff | paDitherOff , /* Don't use dither nor clipping. */
+ framesPerBuffer , /* Frames per user buffer. */
+ framesPerBuffer , /* Frames per ring buffer block. */
+ paUtilBoundedHostBufferSize ,
+ NULL, NULL );/* No callback! */
+ if( result != paNoError ){
+ PA_DEBUG(("ERROR! Blocking i/o buffer processor initialization failed in OpenStream()\n"));
+ goto error;
+ }
+ blockingBufferProcessorInited = TRUE;
+
+ /* If input is requested. */
+ if( inputChannelCount )
+ {
+ /* Create the callback sync-event. */
+ stream->blockingState->readFramesReadyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ if( stream->blockingState->readFramesReadyEvent == NULL )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ PA_DEBUG(("ERROR! Blocking i/o \"read frames ready\" event creation failed in OpenStream()\n"));
+ goto error;
+ }
+ blockingReadFramesReadyEventInitialized = 1;
+
+
+ /* Create pointer buffer to access non-interleaved data in ReadStream() */
+ stream->blockingState->readStreamBuffer = (void**)PaUtil_AllocateMemory( sizeof(void*) * inputChannelCount );
+ if( !stream->blockingState->readStreamBuffer )
+ {
+ result = paInsufficientMemory;
+ PA_DEBUG(("ERROR! Blocking i/o read stream buffer allocation failed in OpenStream()\n"));
+ goto error;
+ }
+
+ /* The ring buffer should store as many data blocks as needed
+ to achieve the requested latency. Whereas it must be large
+ enough to store at least two complete data blocks.
+
+ 1) Determine the amount of latency to be added to the
+ prefered ASIO latency.
+ 2) Make sure we have at lest one additional latency frame.
+ 3) Divide the number of frames by the desired block size to
+ get the number (rounded up to pure integer) of blocks to
+ be stored in the buffer.
+ 4) Add one additional block for block processing and convert
+ to samples frames.
+ 5) Get the next larger (or equal) power-of-two buffer size.
+ */
+ lBlockingBufferSize = suggestedInputLatencyFrames - stream->inputLatency;
+ lBlockingBufferSize = (lBlockingBufferSize > 0) ? lBlockingBufferSize : 1;
+ lBlockingBufferSize = (lBlockingBufferSize + framesPerBuffer - 1) / framesPerBuffer;
+ lBlockingBufferSize = (lBlockingBufferSize + 1) * framesPerBuffer;
+
+ /* Get the next larger or equal power-of-two buffersize. */
+ lBlockingBufferSizePow2 = 1;
+ while( lBlockingBufferSize > (lBlockingBufferSizePow2<<=1) );
+ lBlockingBufferSize = lBlockingBufferSizePow2;
+
+ /* Compute total intput latency in seconds */
+ stream->streamRepresentation.streamInfo.inputLatency =
+ (double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor )
+ + PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor)
+ + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer
+ + stream->inputLatency )
+ / sampleRate;
+
+ /* The code below prints the ASIO latency which doesn't include
+ the buffer processor latency nor the blocking i/o latency. It
+ reports the added latency separately.
+ */
+ PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n",
+ stream->inputLatency,
+ (long)( stream->inputLatency * (1000.0 / sampleRate) ),
+ PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor),
+ (long)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor) * (1000.0 / sampleRate) ),
+ PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer,
+ (long)( (PaUtil_GetBufferProcessorInputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) )
+ ));
+
+ /* Determine the size of ring buffer in bytes. */
+ lBytesPerFrame = inputChannelCount * Pa_GetSampleSize(inputSampleFormat );
+
+ /* Allocate the blocking i/o input ring buffer memory. */
+ stream->blockingState->readRingBufferData = (void*)PaUtil_AllocateMemory( lBlockingBufferSize * lBytesPerFrame );
+ if( !stream->blockingState->readRingBufferData )
+ {
+ result = paInsufficientMemory;
+ PA_DEBUG(("ERROR! Blocking i/o input ring buffer allocation failed in OpenStream()\n"));
+ goto error;
+ }
+
+ /* Initialize the input ring buffer struct. */
+ PaUtil_InitializeRingBuffer( &stream->blockingState->readRingBuffer ,
+ lBytesPerFrame ,
+ lBlockingBufferSize ,
+ stream->blockingState->readRingBufferData );
+ }
+
+ /* If output is requested. */
+ if( outputChannelCount )
+ {
+ stream->blockingState->writeBuffersReadyEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+ if( stream->blockingState->writeBuffersReadyEvent == NULL )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ PA_DEBUG(("ERROR! Blocking i/o \"write buffers ready\" event creation failed in OpenStream()\n"));
+ goto error;
+ }
+ blockingWriteBuffersReadyEventInitialized = 1;
+
+ /* Create pointer buffer to access non-interleaved data in WriteStream() */
+ stream->blockingState->writeStreamBuffer = (const void**)PaUtil_AllocateMemory( sizeof(const void*) * outputChannelCount );
+ if( !stream->blockingState->writeStreamBuffer )
+ {
+ result = paInsufficientMemory;
+ PA_DEBUG(("ERROR! Blocking i/o write stream buffer allocation failed in OpenStream()\n"));
+ goto error;
+ }
+
+ /* The ring buffer should store as many data blocks as needed
+ to achieve the requested latency. Whereas it must be large
+ enough to store at least two complete data blocks.
+
+ 1) Determine the amount of latency to be added to the
+ prefered ASIO latency.
+ 2) Make sure we have at lest one additional latency frame.
+ 3) Divide the number of frames by the desired block size to
+ get the number (rounded up to pure integer) of blocks to
+ be stored in the buffer.
+ 4) Add one additional block for block processing and convert
+ to samples frames.
+ 5) Get the next larger (or equal) power-of-two buffer size.
+ */
+ lBlockingBufferSize = suggestedOutputLatencyFrames - stream->outputLatency;
+ lBlockingBufferSize = (lBlockingBufferSize > 0) ? lBlockingBufferSize : 1;
+ lBlockingBufferSize = (lBlockingBufferSize + framesPerBuffer - 1) / framesPerBuffer;
+ lBlockingBufferSize = (lBlockingBufferSize + 1) * framesPerBuffer;
+
+ /* The buffer size (without the additional block) corresponds
+ to the initial number of silent samples in the output ring
+ buffer. */
+ stream->blockingState->writeRingBufferInitialFrames = lBlockingBufferSize - framesPerBuffer;
+
+ /* Get the next larger or equal power-of-two buffersize. */
+ lBlockingBufferSizePow2 = 1;
+ while( lBlockingBufferSize > (lBlockingBufferSizePow2<<=1) );
+ lBlockingBufferSize = lBlockingBufferSizePow2;
+
+ /* Compute total output latency in seconds */
+ stream->streamRepresentation.streamInfo.outputLatency =
+ (double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor )
+ + PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor)
+ + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer
+ + stream->outputLatency )
+ / sampleRate;
+
+ /* The code below prints the ASIO latency which doesn't include
+ the buffer processor latency nor the blocking i/o latency. It
+ reports the added latency separately.
+ */
+ PA_DEBUG(("PaAsio : ASIO OutputLatency = %ld (%ld ms),\n added buffProc:%ld (%ld ms),\n added blocking:%ld (%ld ms)\n",
+ stream->outputLatency,
+ (long)( stream->inputLatency * (1000.0 / sampleRate) ),
+ PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor),
+ (long)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor) * (1000.0 / sampleRate) ),
+ PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer,
+ (long)( (PaUtil_GetBufferProcessorOutputLatency(&stream->blockingState->bufferProcessor) + (lBlockingBufferSize / framesPerBuffer - 1) * framesPerBuffer) * (1000.0 / sampleRate) )
+ ));
+
+ /* Determine the size of ring buffer in bytes. */
+ lBytesPerFrame = outputChannelCount * Pa_GetSampleSize(outputSampleFormat);
+
+ /* Allocate the blocking i/o output ring buffer memory. */
+ stream->blockingState->writeRingBufferData = (void*)PaUtil_AllocateMemory( lBlockingBufferSize * lBytesPerFrame );
+ if( !stream->blockingState->writeRingBufferData )
+ {
+ result = paInsufficientMemory;
+ PA_DEBUG(("ERROR! Blocking i/o output ring buffer allocation failed in OpenStream()\n"));
+ goto error;
+ }
+
+ /* Initialize the output ring buffer struct. */
+ PaUtil_InitializeRingBuffer( &stream->blockingState->writeRingBuffer ,
+ lBytesPerFrame ,
+ lBlockingBufferSize ,
+ stream->blockingState->writeRingBufferData );
+ }
+
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+
+ }
+ else /* Using callback interface... */
+ {
+ result = PaUtil_InitializeBufferProcessor( &stream->bufferProcessor,
+ inputChannelCount, inputSampleFormat, hostInputSampleFormat,
+ outputChannelCount, outputSampleFormat, hostOutputSampleFormat,
+ sampleRate, streamFlags, framesPerBuffer,
+ framesPerHostBuffer, paUtilFixedHostBufferSize,
+ streamCallback, userData );
+ if( result != paNoError ){
+ PA_DEBUG(("OpenStream ERROR13\n"));
+ goto error;
+ }
+ callbackBufferProcessorInited = TRUE;
+
+ stream->streamRepresentation.streamInfo.inputLatency =
+ (double)( PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)
+ + stream->inputLatency) / sampleRate; // seconds
+ stream->streamRepresentation.streamInfo.outputLatency =
+ (double)( PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)
+ + stream->outputLatency) / sampleRate; // seconds
+ stream->streamRepresentation.streamInfo.sampleRate = sampleRate;
+
+ // the code below prints the ASIO latency which doesn't include the
+ // buffer processor latency. it reports the added latency separately
+ PA_DEBUG(("PaAsio : ASIO InputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
+ stream->inputLatency,
+ (long)((stream->inputLatency*1000)/ sampleRate),
+ PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor),
+ (long)((PaUtil_GetBufferProcessorInputLatency(&stream->bufferProcessor)*1000)/ sampleRate)
+ ));
+
+ PA_DEBUG(("PaAsio : ASIO OuputLatency = %ld (%ld ms), added buffProc:%ld (%ld ms)\n",
+ stream->outputLatency,
+ (long)((stream->outputLatency*1000)/ sampleRate),
+ PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor),
+ (long)((PaUtil_GetBufferProcessorOutputLatency(&stream->bufferProcessor)*1000)/ sampleRate)
+ ));
+ }
stream->asioHostApi = asioHostApi;
stream->framesPerHostCallback = framesPerHostBuffer;
@@ -2147,10 +2570,12 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream->inputChannelCount = inputChannelCount;
stream->outputChannelCount = outputChannelCount;
stream->postOutput = driverInfo->postOutput;
+ stream->isStopped = 1;
stream->isActive = 0;
-
+
asioHostApi->openAsioDeviceIndex = asioDeviceIndex;
+ theAsioStream = stream;
*s = (PaStream*)stream;
return result;
@@ -2159,6 +2584,31 @@ error:
PA_DEBUG(("goto errored\n"));
if( stream )
{
+ if( stream->blockingState )
+ {
+ if( blockingBufferProcessorInited )
+ PaUtil_TerminateBufferProcessor( &stream->blockingState->bufferProcessor );
+
+ if( stream->blockingState->writeRingBufferData )
+ PaUtil_FreeMemory( stream->blockingState->writeRingBufferData );
+ if( stream->blockingState->writeStreamBuffer )
+ PaUtil_FreeMemory( stream->blockingState->writeStreamBuffer );
+ if( blockingWriteBuffersReadyEventInitialized )
+ CloseHandle( stream->blockingState->writeBuffersReadyEvent );
+
+ if( stream->blockingState->readRingBufferData )
+ PaUtil_FreeMemory( stream->blockingState->readRingBufferData );
+ if( stream->blockingState->readStreamBuffer )
+ PaUtil_FreeMemory( stream->blockingState->readStreamBuffer );
+ if( blockingReadFramesReadyEventInitialized )
+ CloseHandle( stream->blockingState->readFramesReadyEvent );
+
+ PaUtil_FreeMemory( stream->blockingState );
+ }
+
+ if( callbackBufferProcessorInited )
+ PaUtil_TerminateBufferProcessor( &stream->bufferProcessor );
+
if( completedBuffersPlayedEventInited )
CloseHandle( stream->completedBuffersPlayedEvent );
@@ -2178,8 +2628,9 @@ error:
ASIODisposeBuffers();
if( asioIsInitialized )
- ASIOExit();
-
+ {
+ UnloadAsioDriver();
+ }
return result;
}
@@ -2205,13 +2656,34 @@ static PaError CloseStream( PaStream* s )
CloseHandle( stream->completedBuffersPlayedEvent );
+ /* Using blocking i/o interface... */
+ if( stream->blockingState )
+ {
+ PaUtil_TerminateBufferProcessor( &stream->blockingState->bufferProcessor );
+
+ if( stream->inputChannelCount ) {
+ PaUtil_FreeMemory( stream->blockingState->readRingBufferData );
+ PaUtil_FreeMemory( stream->blockingState->readStreamBuffer );
+ CloseHandle( stream->blockingState->readFramesReadyEvent );
+ }
+ if( stream->outputChannelCount ) {
+ PaUtil_FreeMemory( stream->blockingState->writeRingBufferData );
+ PaUtil_FreeMemory( stream->blockingState->writeStreamBuffer );
+ CloseHandle( stream->blockingState->writeBuffersReadyEvent );
+ }
+
+ PaUtil_FreeMemory( stream->blockingState );
+ }
+
PaUtil_FreeMemory( stream->asioBufferInfos );
PaUtil_FreeMemory( stream->asioChannelInfos );
PaUtil_FreeMemory( stream->bufferPtrs );
PaUtil_FreeMemory( stream );
ASIODisposeBuffers();
- ASIOExit();
+ UnloadAsioDriver();
+
+ theAsioStream = 0;
return result;
}
@@ -2245,10 +2717,10 @@ static void bufferSwitch(long index, ASIOBool directProcess)
// conversion from 64 bit ASIOSample/ASIOTimeStamp to double float
#if NATIVE_INT64
- #define ASIO64toDouble(a) (a)
+ #define ASIO64toDouble(a) (a)
#else
- const double twoRaisedTo32 = 4294967296.;
- #define ASIO64toDouble(a) ((a).lo + (a).hi * twoRaisedTo32)
+ const double twoRaisedTo32 = 4294967296.;
+ #define ASIO64toDouble(a) ((a).lo + (a).hi * twoRaisedTo32)
#endif
static ASIOTime *bufferSwitchTimeInfo( ASIOTime *timeInfo, long index, ASIOBool directProcess )
@@ -2430,7 +2902,9 @@ previousIndex = index;
paTimeInfo.inputBufferAdcTime = paTimeInfo.currentTime - theAsioStream->streamRepresentation.streamInfo.inputLatency;
paTimeInfo.outputBufferDacTime = paTimeInfo.currentTime + theAsioStream->streamRepresentation.streamInfo.outputLatency;
*/
-#if 1
+
+/* Disabled! Stopping and re-starting the stream causes an input overflow / output undeflow. S.Fischer */
+#if 0
// detect underflows by checking inter-callback time > 2 buffer period
static double previousTime = -1;
if( previousTime > 0 ){
@@ -2634,6 +3108,7 @@ static PaError StartStream( PaStream *s )
{
PaError result = paNoError;
PaAsioStream *stream = (PaAsioStream*)s;
+ PaAsioStreamBlockingState *blockingState = stream->blockingState;
ASIOError asioError;
if( stream->outputChannelCount > 0 )
@@ -2658,18 +3133,72 @@ static PaError StartStream( PaStream *s )
PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
}
- if( result == paNoError )
+
+ /* Using blocking i/o interface... */
+ if( blockingState )
{
- theAsioStream = stream;
- asioError = ASIOStart();
- if( asioError == ASE_OK )
+ /* Reset blocking i/o buffer processor. */
+ PaUtil_ResetBufferProcessor( &blockingState->bufferProcessor );
+
+ /* If we're about to process some input data. */
+ if( stream->inputChannelCount )
{
- stream->isActive = 1;
- stream->streamFinishedCallbackCalled = false;
+ /* Reset callback-ReadStream sync event. */
+ if( ResetEvent( blockingState->readFramesReadyEvent ) == 0 )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ }
+
+ /* Flush blocking i/o ring buffer. */
+ PaUtil_FlushRingBuffer( &blockingState->readRingBuffer );
+ (*blockingState->bufferProcessor.inputZeroer)( blockingState->readRingBuffer.buffer, 1, blockingState->bufferProcessor.inputChannelCount * blockingState->readRingBuffer.bufferSize );
}
- else
+
+ /* If we're about to process some output data. */
+ if( stream->outputChannelCount )
+ {
+ /* Reset callback-WriteStream sync event. */
+ if( ResetEvent( blockingState->writeBuffersReadyEvent ) == 0 )
+ {
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ }
+
+ /* Flush blocking i/o ring buffer. */
+ PaUtil_FlushRingBuffer( &blockingState->writeRingBuffer );
+ (*blockingState->bufferProcessor.outputZeroer)( blockingState->writeRingBuffer.buffer, 1, blockingState->bufferProcessor.outputChannelCount * blockingState->writeRingBuffer.bufferSize );
+
+ /* Initialize the output ring buffer to "silence". */
+ PaUtil_AdvanceRingBufferWriteIndex( &blockingState->writeRingBuffer, blockingState->writeRingBufferInitialFrames );
+ }
+
+ /* Clear requested frames / buffers count. */
+ blockingState->writeBuffersRequested = 0;
+ blockingState->readFramesRequested = 0;
+ blockingState->writeBuffersRequestedFlag = FALSE;
+ blockingState->readFramesRequestedFlag = FALSE;
+ blockingState->outputUnderflowFlag = FALSE;
+ blockingState->inputOverflowFlag = FALSE;
+ blockingState->stopFlag = FALSE;
+ }
+
+
+ if( result == paNoError )
+ {
+ assert( theAsioStream == stream ); /* theAsioStream should be set correctly in OpenStream */
+
+ /* initialize these variables before the callback has a chance to be invoked */
+ stream->isStopped = 0;
+ stream->isActive = 1;
+ stream->streamFinishedCallbackCalled = false;
+
+ asioError = ASIOStart();
+ if( asioError != ASE_OK )
{
- theAsioStream = 0;
+ stream->isStopped = 1;
+ stream->isActive = 0;
+
result = paUnanticipatedHostError;
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
}
@@ -2678,15 +3207,59 @@ static PaError StartStream( PaStream *s )
return result;
}
+static void EnsureCallbackHasCompleted( PaAsioStream *stream )
+{
+ // make sure that the callback is not still in-flight after ASIOStop()
+ // returns. This has been observed to happen on the Hoontech DSP24 for
+ // example.
+ int count = 2000; // only wait for 2 seconds, rather than hanging.
+ while( stream->reenterCount != -1 && count > 0 )
+ {
+ Sleep(1);
+ --count;
+ }
+}
static PaError StopStream( PaStream *s )
{
PaError result = paNoError;
PaAsioStream *stream = (PaAsioStream*)s;
+ PaAsioStreamBlockingState *blockingState = stream->blockingState;
ASIOError asioError;
if( stream->isActive )
{
+ /* If blocking i/o output is in use */
+ if( blockingState && stream->outputChannelCount )
+ {
+ /* Request the whole output buffer to be available. */
+ blockingState->writeBuffersRequested = blockingState->writeRingBuffer.bufferSize;
+ /* Signalize that additional buffers are need. */
+ blockingState->writeBuffersRequestedFlag = TRUE;
+ /* Set flag to indicate the playback is to be stopped. */
+ blockingState->stopFlag = TRUE;
+
+ /* Wait until requested number of buffers has been freed. Time
+ out after twice the blocking i/o ouput buffer could have
+ been consumed. */
+ DWORD timeout = (DWORD)( 2 * blockingState->writeRingBuffer.bufferSize * 1000
+ / stream->streamRepresentation.streamInfo.sampleRate );
+ DWORD waitResult = WaitForSingleObject( blockingState->writeBuffersReadyEvent, timeout );
+
+ /* If something seriously went wrong... */
+ if( waitResult == WAIT_FAILED )
+ {
+ PA_DEBUG(("WaitForSingleObject() failed in StopStream()\n"));
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ }
+ else if( waitResult == WAIT_TIMEOUT )
+ {
+ PA_DEBUG(("WaitForSingleObject() timed out in StopStream()\n"));
+ result = paTimedOut;
+ }
+ }
+
stream->stopProcessing = true;
/* wait for the stream to finish playing out enqueued buffers.
@@ -2696,22 +3269,26 @@ static PaError StopStream( PaStream *s )
length is longer than the asio buffer size then that should
be taken into account.
*/
- if( WaitForSingleObject( theAsioStream->completedBuffersPlayedEvent,
+ if( WaitForSingleObject( stream->completedBuffersPlayedEvent,
(DWORD)(stream->streamRepresentation.streamInfo.outputLatency * 1000. * 4.) )
- == WAIT_TIMEOUT )
+ == WAIT_TIMEOUT )
{
PA_DEBUG(("WaitForSingleObject() timed out in StopStream()\n" ));
}
}
asioError = ASIOStop();
- if( asioError != ASE_OK )
+ if( asioError == ASE_OK )
+ {
+ EnsureCallbackHasCompleted( stream );
+ }
+ else
{
result = paUnanticipatedHostError;
PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
}
- theAsioStream = 0;
+ stream->isStopped = 1;
stream->isActive = 0;
if( !stream->streamFinishedCallbackCalled )
@@ -2723,7 +3300,6 @@ static PaError StopStream( PaStream *s )
return result;
}
-
static PaError AbortStream( PaStream *s )
{
PaError result = paNoError;
@@ -2733,31 +3309,17 @@ static PaError AbortStream( PaStream *s )
stream->zeroOutput = true;
asioError = ASIOStop();
- if( asioError != ASE_OK )
+ if( asioError == ASE_OK )
{
- result = paUnanticipatedHostError;
- PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
+ EnsureCallbackHasCompleted( stream );
}
else
{
- // make sure that the callback is not still in-flight when ASIOStop()
- // returns. This has been observed to happen on the Hoontech DSP24 for
- // example.
- int count = 2000; // only wait for 2 seconds, rather than hanging.
- while( theAsioStream->reenterCount != -1 && count > 0 )
- {
- Sleep(1);
- --count;
- }
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_ASIO_ERROR( asioError );
}
- /* it is questionable whether we should zero theAsioStream if ASIOStop()
- returns an error, because the callback could still be active. We assume
- not - this is based on the fact that ASIOStop is unlikely to fail
- if the callback is running - it's more likely to fail because the
- callback is not running. */
-
- theAsioStream = 0;
+ stream->isStopped = 1;
stream->isActive = 0;
if( !stream->streamFinishedCallbackCalled )
@@ -2772,9 +3334,9 @@ static PaError AbortStream( PaStream *s )
static PaError IsStreamStopped( PaStream *s )
{
- //PaAsioStream *stream = (PaAsioStream*)s;
- (void) s; /* unused parameter */
- return theAsioStream == 0;
+ PaAsioStream *stream = (PaAsioStream*)s;
+
+ return stream->isStopped;
}
@@ -2807,33 +3369,348 @@ static double GetStreamCpuLoad( PaStream* s )
for blocking streams.
*/
-static PaError ReadStream( PaStream* s,
- void *buffer,
- unsigned long frames )
+static PaError ReadStream( PaStream *s ,
+ void *buffer,
+ unsigned long frames )
{
- PaAsioStream *stream = (PaAsioStream*)s;
+ PaError result = paNoError; /* Initial return value. */
+ PaAsioStream *stream = (PaAsioStream*)s; /* The PA ASIO stream. */
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
- (void) stream; /* unused parameters */
- (void) buffer;
- (void) frames;
+ /* Pointer to the blocking i/o data struct. */
+ PaAsioStreamBlockingState *blockingState = stream->blockingState;
- return paNoError;
-}
+ /* Get blocking i/o buffer processor and ring buffer pointers. */
+ PaUtilBufferProcessor *pBp = &blockingState->bufferProcessor;
+ PaUtilRingBuffer *pRb = &blockingState->readRingBuffer;
+
+ /* Ring buffer segment(s) used for writing. */
+ void *pRingBufferData1st = NULL; /* First segment. (Mandatory) */
+ void *pRingBufferData2nd = NULL; /* Second segment. (Optional) */
+
+ /* Number of frames per ring buffer segment. */
+ long lRingBufferSize1st = 0; /* First segment. (Mandatory) */
+ long lRingBufferSize2nd = 0; /* Second segment. (Optional) */
+
+ /* Get number of frames to be processed per data block. */
+ unsigned long lFramesPerBlock = stream->bufferProcessor.framesPerUserBuffer;
+ /* Actual number of frames that has been copied into the ring buffer. */
+ unsigned long lFramesCopied = 0;
+ /* The number of remaining unprocessed dtat frames. */
+ unsigned long lFramesRemaining = frames;
+ /* Copy the input argument to avoid pointer increment! */
+ const void *userBuffer;
+ unsigned int i; /* Just a counter. */
+
+ /* About the time, needed to process 8 data blocks. */
+ DWORD timeout = (DWORD)( 8 * lFramesPerBlock * 1000 / stream->streamRepresentation.streamInfo.sampleRate );
+ DWORD waitResult = 0;
+
+
+ /* Check if the stream is still available ready to gather new data. */
+ if( blockingState->stopFlag || !stream->isActive )
+ {
+ PA_DEBUG(("Warning! Stream no longer available for reading in ReadStream()\n"));
+ result = paStreamIsStopped;
+ return result;
+ }
+
+ /* If the stream is a input stream. */
+ if( stream->inputChannelCount )
+ {
+ /* Prepare buffer access. */
+ if( !pBp->userOutputIsInterleaved )
+ {
+ userBuffer = blockingState->readStreamBuffer;
+ for( i = 0; i<pBp->inputChannelCount; ++i )
+ {
+ ((void**)userBuffer)[i] = ((void**)buffer)[i];
+ }
+ } /* Use the unchanged buffer. */
+ else { userBuffer = buffer; }
-static PaError WriteStream( PaStream* s,
- const void *buffer,
- unsigned long frames )
+ do /* Internal block processing for too large user data buffers. */
+ {
+ /* Get the size of the current data block to be processed. */
+ lFramesPerBlock =(lFramesPerBlock < lFramesRemaining)
+ ? lFramesPerBlock : lFramesRemaining;
+ /* Use predefined block size for as long there are enough
+ buffers available, thereafter reduce the processing block
+ size to match the number of remaining buffers. So the final
+ data block is processed although it may be incomplete. */
+
+ /* If the available amount of data frames is insufficient. */
+ if( PaUtil_GetRingBufferReadAvailable(pRb) < (long) lFramesPerBlock )
+ {
+ /* Make sure, the event isn't already set! */
+ /* ResetEvent( blockingState->readFramesReadyEvent ); */
+
+ /* Set the number of requested buffers. */
+ blockingState->readFramesRequested = lFramesPerBlock;
+
+ /* Signalize that additional buffers are need. */
+ blockingState->readFramesRequestedFlag = TRUE;
+
+ /* Wait until requested number of buffers has been freed. */
+ waitResult = WaitForSingleObject( blockingState->readFramesReadyEvent, timeout );
+
+ /* If something seriously went wrong... */
+ if( waitResult == WAIT_FAILED )
+ {
+ PA_DEBUG(("WaitForSingleObject() failed in ReadStream()\n"));
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ return result;
+ }
+ else if( waitResult == WAIT_TIMEOUT )
+ {
+ PA_DEBUG(("WaitForSingleObject() timed out in ReadStream()\n"));
+
+ /* If block processing has stopped, abort! */
+ if( blockingState->stopFlag ) { return result = paStreamIsStopped; }
+
+ /* If a timeout is encountered, give up eventually. */
+ return result = paTimedOut;
+ }
+ }
+ /* Now, the ring buffer contains the required amount of data
+ frames.
+ (Therefor we don't need to check the return argument of
+ PaUtil_GetRingBufferReadRegions(). ;-) )
+ */
+
+ /* Retrieve pointer(s) to the ring buffer's current write
+ position(s). If the first buffer segment is too small to
+ store the requested number of bytes, an additional second
+ segment is returned. Otherwise, i.e. if the first segment
+ is large enough, the second segment's pointer will be NULL.
+ */
+ PaUtil_GetRingBufferReadRegions(pRb ,
+ lFramesPerBlock ,
+ &pRingBufferData1st,
+ &lRingBufferSize1st,
+ &pRingBufferData2nd,
+ &lRingBufferSize2nd);
+
+ /* Set number of frames to be copied from the ring buffer. */
+ PaUtil_SetInputFrameCount( pBp, lRingBufferSize1st );
+ /* Setup ring buffer access. */
+ PaUtil_SetInterleavedInputChannels(pBp , /* Buffer processor. */
+ 0 , /* The first channel's index. */
+ pRingBufferData1st, /* First ring buffer segment. */
+ 0 ); /* Use all available channels. */
+
+ /* If a second ring buffer segment is required. */
+ if( lRingBufferSize2nd ) {
+ /* Set number of frames to be copied from the ring buffer. */
+ PaUtil_Set2ndInputFrameCount( pBp, lRingBufferSize2nd );
+ /* Setup ring buffer access. */
+ PaUtil_Set2ndInterleavedInputChannels(pBp , /* Buffer processor. */
+ 0 , /* The first channel's index. */
+ pRingBufferData2nd, /* Second ring buffer segment. */
+ 0 ); /* Use all available channels. */
+ }
+
+ /* Let the buffer processor handle "copy and conversion" and
+ update the ring buffer indices manually. */
+ lFramesCopied = PaUtil_CopyInput( pBp, &buffer, lFramesPerBlock );
+ PaUtil_AdvanceRingBufferReadIndex( pRb, lFramesCopied );
+
+ /* Decrease number of unprocessed frames. */
+ lFramesRemaining -= lFramesCopied;
+
+ } /* Continue with the next data chunk. */
+ while( lFramesRemaining );
+
+
+ /* If there has been an input overflow within the callback */
+ if( blockingState->inputOverflowFlag )
+ {
+ blockingState->inputOverflowFlag = FALSE;
+
+ /* Return the corresponding error code. */
+ result = paInputOverflowed;
+ }
+
+ } /* If this is not an input stream. */
+ else {
+ result = paCanNotReadFromAnOutputOnlyStream;
+ }
+
+ return result;
+}
+
+static PaError WriteStream( PaStream *s ,
+ const void *buffer,
+ unsigned long frames )
{
- PaAsioStream *stream = (PaAsioStream*)s;
+ PaError result = paNoError; /* Initial return value. */
+ PaAsioStream *stream = (PaAsioStream*)s; /* The PA ASIO stream. */
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
- (void) stream; /* unused parameters */
- (void) buffer;
- (void) frames;
+ /* Pointer to the blocking i/o data struct. */
+ PaAsioStreamBlockingState *blockingState = stream->blockingState;
- return paNoError;
+ /* Get blocking i/o buffer processor and ring buffer pointers. */
+ PaUtilBufferProcessor *pBp = &blockingState->bufferProcessor;
+ PaUtilRingBuffer *pRb = &blockingState->writeRingBuffer;
+
+ /* Ring buffer segment(s) used for writing. */
+ void *pRingBufferData1st = NULL; /* First segment. (Mandatory) */
+ void *pRingBufferData2nd = NULL; /* Second segment. (Optional) */
+
+ /* Number of frames per ring buffer segment. */
+ long lRingBufferSize1st = 0; /* First segment. (Mandatory) */
+ long lRingBufferSize2nd = 0; /* Second segment. (Optional) */
+
+ /* Get number of frames to be processed per data block. */
+ unsigned long lFramesPerBlock = stream->bufferProcessor.framesPerUserBuffer;
+ /* Actual number of frames that has been copied into the ring buffer. */
+ unsigned long lFramesCopied = 0;
+ /* The number of remaining unprocessed dtat frames. */
+ unsigned long lFramesRemaining = frames;
+
+ /* About the time, needed to process 8 data blocks. */
+ DWORD timeout = (DWORD)( 8 * lFramesPerBlock * 1000 / stream->streamRepresentation.streamInfo.sampleRate );
+ DWORD waitResult = 0;
+
+ /* Copy the input argument to avoid pointer increment! */
+ const void *userBuffer;
+ unsigned int i; /* Just a counter. */
+
+
+ /* Check if the stream ist still available ready to recieve new data. */
+ if( blockingState->stopFlag || !stream->isActive )
+ {
+ PA_DEBUG(("Warning! Stream no longer available for writing in WriteStream()\n"));
+ result = paStreamIsStopped;
+ return result;
+ }
+
+ /* If the stream is a output stream. */
+ if( stream->outputChannelCount )
+ {
+ /* Prepare buffer access. */
+ if( !pBp->userOutputIsInterleaved )
+ {
+ userBuffer = blockingState->writeStreamBuffer;
+ for( i = 0; i<pBp->outputChannelCount; ++i )
+ {
+ ((const void**)userBuffer)[i] = ((const void**)buffer)[i];
+ }
+ } /* Use the unchanged buffer. */
+ else { userBuffer = buffer; }
+
+
+ do /* Internal block processing for too large user data buffers. */
+ {
+ /* Get the size of the current data block to be processed. */
+ lFramesPerBlock =(lFramesPerBlock < lFramesRemaining)
+ ? lFramesPerBlock : lFramesRemaining;
+ /* Use predefined block size for as long there are enough
+ frames available, thereafter reduce the processing block
+ size to match the number of remaining frames. So the final
+ data block is processed although it may be incomplete. */
+
+ /* If the available amount of buffers is insufficient. */
+ if( PaUtil_GetRingBufferWriteAvailable(pRb) < (long) lFramesPerBlock )
+ {
+ /* Make sure, the event isn't already set! */
+ /* ResetEvent( blockingState->writeBuffersReadyEvent ); */
+
+ /* Set the number of requested buffers. */
+ blockingState->writeBuffersRequested = lFramesPerBlock;
+
+ /* Signalize that additional buffers are need. */
+ blockingState->writeBuffersRequestedFlag = TRUE;
+
+ /* Wait until requested number of buffers has been freed. */
+ waitResult = WaitForSingleObject( blockingState->writeBuffersReadyEvent, timeout );
+
+ /* If something seriously went wrong... */
+ if( waitResult == WAIT_FAILED )
+ {
+ PA_DEBUG(("WaitForSingleObject() failed in WriteStream()\n"));
+ result = paUnanticipatedHostError;
+ PA_ASIO_SET_LAST_SYSTEM_ERROR( GetLastError() );
+ return result;
+ }
+ else if( waitResult == WAIT_TIMEOUT )
+ {
+ PA_DEBUG(("WaitForSingleObject() timed out in WriteStream()\n"));
+
+ /* If block processing has stopped, abort! */
+ if( blockingState->stopFlag ) { return result = paStreamIsStopped; }
+
+ /* If a timeout is encountered, give up eventually. */
+ return result = paTimedOut;
+ }
+ }
+ /* Now, the ring buffer contains the required amount of free
+ space to store the provided number of data frames.
+ (Therefor we don't need to check the return argument of
+ PaUtil_GetRingBufferWriteRegions(). ;-) )
+ */
+
+ /* Retrieve pointer(s) to the ring buffer's current write
+ position(s). If the first buffer segment is too small to
+ store the requested number of bytes, an additional second
+ segment is returned. Otherwise, i.e. if the first segment
+ is large enough, the second segment's pointer will be NULL.
+ */
+ PaUtil_GetRingBufferWriteRegions(pRb ,
+ lFramesPerBlock ,
+ &pRingBufferData1st,
+ &lRingBufferSize1st,
+ &pRingBufferData2nd,
+ &lRingBufferSize2nd);
+
+ /* Set number of frames to be copied to the ring buffer. */
+ PaUtil_SetOutputFrameCount( pBp, lRingBufferSize1st );
+ /* Setup ring buffer access. */
+ PaUtil_SetInterleavedOutputChannels(pBp , /* Buffer processor. */
+ 0 , /* The first channel's index. */
+ pRingBufferData1st, /* First ring buffer segment. */
+ 0 ); /* Use all available channels. */
+
+ /* If a second ring buffer segment is required. */
+ if( lRingBufferSize2nd ) {
+ /* Set number of frames to be copied to the ring buffer. */
+ PaUtil_Set2ndOutputFrameCount( pBp, lRingBufferSize2nd );
+ /* Setup ring buffer access. */
+ PaUtil_Set2ndInterleavedOutputChannels(pBp , /* Buffer processor. */
+ 0 , /* The first channel's index. */
+ pRingBufferData2nd, /* Second ring buffer segment. */
+ 0 ); /* Use all available channels. */
+ }
+
+ /* Let the buffer processor handle "copy and conversion" and
+ update the ring buffer indices manually. */
+ lFramesCopied = PaUtil_CopyOutput( pBp, &userBuffer, lFramesPerBlock );
+ PaUtil_AdvanceRingBufferWriteIndex( pRb, lFramesCopied );
+
+ /* Decrease number of unprocessed frames. */
+ lFramesRemaining -= lFramesCopied;
+
+ } /* Continue with the next data chunk. */
+ while( lFramesRemaining );
+
+
+ /* If there has been an output underflow within the callback */
+ if( blockingState->outputUnderflowFlag )
+ {
+ blockingState->outputUnderflowFlag = FALSE;
+
+ /* Return the corresponding error code. */
+ result = paOutputUnderflowed;
+ }
+
+ } /* If this is not an output stream. */
+ else
+ {
+ result = paCanNotWriteToAnInputOnlyStream;
+ }
+
+ return result;
}
@@ -2841,10 +3718,8 @@ static signed long GetStreamReadAvailable( PaStream* s )
{
PaAsioStream *stream = (PaAsioStream*)s;
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
- (void) stream; /* unused parameter */
-
- return 0;
+ /* Call buffer utility routine to get the number of available frames. */
+ return PaUtil_GetRingBufferReadAvailable( &stream->blockingState->readRingBuffer );
}
@@ -2852,20 +3727,130 @@ static signed long GetStreamWriteAvailable( PaStream* s )
{
PaAsioStream *stream = (PaAsioStream*)s;
- /* IMPLEMENT ME, see portaudio.h for required behavior*/
- (void) stream; /* unused parameter */
+ /* Call buffer utility routine to get the number of empty buffers. */
+ return PaUtil_GetRingBufferWriteAvailable( &stream->blockingState->writeRingBuffer );
+}
+
+
+/* This routine will be called by the PortAudio engine when audio is needed.
+** It may called at interrupt level on some machines so don't do anything
+** that could mess up the system like calling malloc() or free().
+*/
+static int BlockingIoPaCallback(const void *inputBuffer ,
+ void *outputBuffer ,
+ unsigned long framesPerBuffer,
+ const PaStreamCallbackTimeInfo *timeInfo ,
+ PaStreamCallbackFlags statusFlags ,
+ void *userData )
+{
+ PaError result = paNoError; /* Initial return value. */
+ PaAsioStream *stream = *(PaAsioStream**)userData; /* The PA ASIO stream. */
+ PaAsioStreamBlockingState *blockingState = stream->blockingState; /* Persume blockingState is valid, otherwise the callback wouldn't be running. */
+
+ /* Get a pointer to the stream's blocking i/o buffer processor. */
+ PaUtilBufferProcessor *pBp = &blockingState->bufferProcessor;
+ PaUtilRingBuffer *pRb = NULL;
+
+ /* If output data has been requested. */
+ if( stream->outputChannelCount )
+ {
+ /* If the callback input argument signalizes a output underflow,
+ make sure the WriteStream() function knows about it, too! */
+ if( statusFlags & paOutputUnderflowed ) {
+ blockingState->outputUnderflowFlag = TRUE;
+ }
+
+ /* Access the corresponding ring buffer. */
+ pRb = &blockingState->writeRingBuffer;
+
+ /* If the blocking i/o buffer contains enough output data, */
+ if( PaUtil_GetRingBufferReadAvailable(pRb) >= (long) framesPerBuffer )
+ {
+ /* Extract the requested data from the ring buffer. */
+ PaUtil_ReadRingBuffer( pRb, outputBuffer, framesPerBuffer );
+ }
+ else /* If no output data is available :-( */
+ {
+ /* Signalize a write-buffer underflow. */
+ blockingState->outputUnderflowFlag = TRUE;
+
+ /* Fill the output buffer with silence. */
+ (*pBp->outputZeroer)( outputBuffer, 1, pBp->outputChannelCount * framesPerBuffer );
- return 0;
+ /* If playback is to be stopped */
+ if( blockingState->stopFlag && PaUtil_GetRingBufferReadAvailable(pRb) < (long) framesPerBuffer )
+ {
+ /* Extract all the remaining data from the ring buffer,
+ whether it is a complete data block or not. */
+ PaUtil_ReadRingBuffer( pRb, outputBuffer, PaUtil_GetRingBufferReadAvailable(pRb) );
+ }
+ }
+
+ /* Set blocking i/o event? */
+ if( blockingState->writeBuffersRequestedFlag && PaUtil_GetRingBufferWriteAvailable(pRb) >= (long) blockingState->writeBuffersRequested )
+ {
+ /* Reset buffer request. */
+ blockingState->writeBuffersRequestedFlag = FALSE;
+ blockingState->writeBuffersRequested = 0;
+ /* Signalize that requested buffers are ready. */
+ SetEvent( blockingState->writeBuffersReadyEvent );
+ /* What do we do if SetEvent() returns zero, i.e. the event
+ could not be set? How to return errors from within the
+ callback? - S.Fischer */
+ }
+ }
+
+ /* If input data has been supplied. */
+ if( stream->inputChannelCount )
+ {
+ /* If the callback input argument signalizes a input overflow,
+ make sure the ReadStream() function knows about it, too! */
+ if( statusFlags & paInputOverflowed ) {
+ blockingState->inputOverflowFlag = TRUE;
+ }
+
+ /* Access the corresponding ring buffer. */
+ pRb = &blockingState->readRingBuffer;
+
+ /* If the blocking i/o buffer contains not enough input buffers */
+ if( PaUtil_GetRingBufferWriteAvailable(pRb) < (long) framesPerBuffer )
+ {
+ /* Signalize a read-buffer overflow. */
+ blockingState->inputOverflowFlag = TRUE;
+
+ /* Remove some old data frames from the buffer. */
+ PaUtil_AdvanceRingBufferReadIndex( pRb, framesPerBuffer );
+ }
+
+ /* Insert the current input data into the ring buffer. */
+ PaUtil_WriteRingBuffer( pRb, inputBuffer, framesPerBuffer );
+
+ /* Set blocking i/o event? */
+ if( blockingState->readFramesRequestedFlag && PaUtil_GetRingBufferReadAvailable(pRb) >= (long) blockingState->readFramesRequested )
+ {
+ /* Reset buffer request. */
+ blockingState->readFramesRequestedFlag = FALSE;
+ blockingState->readFramesRequested = 0;
+ /* Signalize that requested buffers are ready. */
+ SetEvent( blockingState->readFramesReadyEvent );
+ /* What do we do if SetEvent() returns zero, i.e. the event
+ could not be set? How to return errors from within the
+ callback? - S.Fischer */
+ /** @todo report an error with PA_DEBUG */
+ }
+ }
+
+ return paContinue;
}
PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific )
{
- PaError result = paNoError;
+ PaError result = paNoError;
PaUtilHostApiRepresentation *hostApi;
PaDeviceIndex hostApiDevice;
ASIODriverInfo asioDriverInfo;
- ASIOError asioError;
+ ASIOError asioError;
int asioIsInitialized = 0;
PaAsioHostApiRepresentation *asioHostApi;
PaAsioDeviceInfo *asioDeviceInfo;
@@ -2896,7 +3881,10 @@ PaError PaAsio_ShowControlPanel( PaDeviceIndex device, void* systemSpecific )
asioDeviceInfo = (PaAsioDeviceInfo*)hostApi->deviceInfos[hostApiDevice];
- if( !loadAsioDriver( const_cast<char*>(asioDeviceInfo->commonDeviceInfo.name) ) )
+ /* See notes about CoInitialize(0) in LoadAsioDriver(). */
+ CoInitialize(0);
+
+ if( !asioHostApi->asioDrivers->loadDriver( const_cast<char*>(asioDeviceInfo->commonDeviceInfo.name) ) )
{
result = paUnanticipatedHostError;
goto error;
@@ -2944,15 +3932,19 @@ PA_DEBUG(("PaAsio_ShowControlPanel: ASIOControlPanel(): %s\n", PaAsio_GetAsioErr
goto error;
}
+ CoUninitialize();
PA_DEBUG(("PaAsio_ShowControlPanel: ASIOExit(): %s\n", PaAsio_GetAsioErrorText(asioError) ));
- return result;
+ return result;
error:
if( asioIsInitialized )
- ASIOExit();
+ {
+ ASIOExit();
+ }
+ CoUninitialize();
- return result;
+ return result;
}
@@ -3021,3 +4013,53 @@ PaError PaAsio_GetOutputChannelName( PaDeviceIndex device, int channelIndex,
error:
return result;
}
+
+
+/* NOTE: the following functions are ASIO-stream specific, and are called directly
+ by client code. We need to check for many more error conditions here because
+ we don't have the benefit of pa_front.c's parameter checking.
+*/
+
+static PaError GetAsioStreamPointer( PaAsioStream **stream, PaStream *s )
+{
+ PaError result;
+ PaUtilHostApiRepresentation *hostApi;
+ PaAsioHostApiRepresentation *asioHostApi;
+
+ result = PaUtil_ValidateStreamPointer( s );
+ if( result != paNoError )
+ return result;
+
+ result = PaUtil_GetHostApiRepresentation( &hostApi, paASIO );
+ if( result != paNoError )
+ return result;
+
+ asioHostApi = (PaAsioHostApiRepresentation*)hostApi;
+
+ if( PA_STREAM_REP( s )->streamInterface == &asioHostApi->callbackStreamInterface
+ || PA_STREAM_REP( s )->streamInterface == &asioHostApi->blockingStreamInterface )
+ {
+ /* s is an ASIO stream */
+ *stream = (PaAsioStream *)s;
+ return paNoError;
+ }
+ else
+ {
+ return paIncompatibleStreamHostApi;
+ }
+}
+
+
+PaError PaAsio_SetStreamSampleRate( PaStream* s, double sampleRate )
+{
+ PaAsioStream *stream;
+ PaError result = GetAsioStreamPointer( &stream, s );
+ if( result != paNoError )
+ return result;
+
+ if( stream != theAsioStream )
+ return paBadStreamPtr;
+
+ return ValidateAndSetSampleRate( sampleRate );
+}
+
diff --git a/pd/portaudio/src/hostapi/coreaudio/notes.txt b/pd/portaudio/src/hostapi/coreaudio/notes.txt
index ffe96962..145afe15 100644
--- a/pd/portaudio/src/hostapi/coreaudio/notes.txt
+++ b/pd/portaudio/src/hostapi/coreaudio/notes.txt
@@ -73,17 +73,24 @@ the stream with it. See below for creating a channel map.
Known issues:
-- Latency: Latency settings are ignored in most cases. Exceptions are when
-doing I/O between different devices and as a hint for selecting a realtively
-low or relatively high latency in conjunction with
-paHostFramesPerBufferUnspecified. Latency settings are always automatically
-bound to "safe" values, however, so setting extreme values here should not be
+- Buffering: No buffering beyond that provided by core audio is provided
+except where absolutely needed for the implementation to work. This may cause
+issues with large framesPerBuffer settings and it also means that no additional
+latency will be provided even if a large latency setting is selected.
+
+- Latency: Latency settings are generally ignored. They may be used as a
+hint for buffer size in paHostFramesPerBufferUnspecified, or the value may
+be used in cases where additional buffering is needed, such as doing input and
+output on seperate devices. Latency settings are always automatically bound
+to "safe" values, however, so setting extreme values here should not be
an issue.
- Buffer Size: paHostFramesPerBufferUnspecified and specific host buffer sizes
are supported. paHostFramesPerBufferUnspecified works best in "pro" mode,
where the buffer size and sample rate of the audio device is most likely
-to match the expected values.
+to match the expected values. In the case of paHostFramesPerBuffer, an
+appropriate framesPerBuffer value will be used that guarantees minimum
+requested latency if that's possible.
- Timing info. It reports on stream time, but I'm probably doing something
wrong since patest_sine_time often reports negative latency numbers. Also,
@@ -111,8 +118,7 @@ render quyality property is used to set the sample rate conversion quality
as "documented" here:
http://lists.apple.com/archives/coreaudio-api/2004/Jan/msg00141.html
-- x86/Universal Binary: to build a universal binary, be sure to use
-the darwin makefile and not the usual configure && make combo.
+- x86/Universal Binary: Universal binaries can be build.
diff --git a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core.c b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core.c
index 7c887bd6..98cfbb51 100644
--- a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core.c
+++ b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core.c
@@ -65,6 +65,7 @@
#include "pa_mac_core_internal.h"
#include <string.h> /* strlen(), memcmp() etc. */
+#include <libkern/OSAtomic.h>
#include "pa_mac_core.h"
#include "pa_mac_core_utilities.h"
@@ -127,6 +128,8 @@ const char *PaMacCore_GetChannelName( int device, int channelIndex, bool input )
OSStatus error;
err = PaUtil_GetHostApiRepresentation( &hostApi, paCoreAudio );
assert(err == paNoError);
+ if( err != paNoError )
+ return NULL;
PaMacAUHAL *macCoreHostApi = (PaMacAUHAL*)hostApi;
AudioDeviceID hostApiDevice = macCoreHostApi->devIds[device];
@@ -261,11 +264,12 @@ static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi,
int isInput);
static PaError OpenAndSetupOneAudioUnit(
+ const PaMacCoreStream *stream,
const PaStreamParameters *inStreamParams,
const PaStreamParameters *outStreamParams,
- const unsigned long requestedFramesPerBuffer,
- unsigned long *actualInputFramesPerBuffer,
- unsigned long *actualOutputFramesPerBuffer,
+ const UInt32 requestedFramesPerBuffer,
+ UInt32 *actualInputFramesPerBuffer,
+ UInt32 *actualOutputFramesPerBuffer,
const PaMacAUHAL *auhalHostApi,
AudioUnit *audioUnit,
AudioConverterRef *srConverter,
@@ -277,6 +281,37 @@ static PaError OpenAndSetupOneAudioUnit(
#define PA_AUHAL_SET_LAST_HOST_ERROR( errorCode, errorText ) \
PaUtil_SetLastHostErrorInfo( paInDevelopment, errorCode, errorText )
+/*
+ * Callback called when starting or stopping a stream.
+ */
+static void startStopCallback(
+ void * inRefCon,
+ AudioUnit ci,
+ AudioUnitPropertyID inID,
+ AudioUnitScope inScope,
+ AudioUnitElement inElement )
+{
+ PaMacCoreStream *stream = (PaMacCoreStream *) inRefCon;
+ UInt32 isRunning;
+ UInt32 size = sizeof( isRunning );
+ OSStatus err;
+ err = AudioUnitGetProperty( ci, kAudioOutputUnitProperty_IsRunning, inScope, inElement, &isRunning, &size );
+ assert( !err );
+ if( err )
+ isRunning = false; //it's very unclear what to do in case of error here. There's no real way to notify the user, and crashing seems unreasonable.
+ if( isRunning )
+ return; //We are only interested in when we are stopping
+ // -- if we are using 2 I/O units, we only need one notification!
+ if( stream->inputUnit && stream->outputUnit && stream->inputUnit != stream->outputUnit && ci == stream->inputUnit )
+ return;
+ PaStreamFinishedCallback *sfc = stream->streamRepresentation.streamFinishedCallback;
+ if( stream->state == STOPPING )
+ stream->state = STOPPED ;
+ if( sfc )
+ sfc( stream->streamRepresentation.userData );
+}
+
+
/*currently, this is only used in initialization, but it might be modified
to be used when the list of devices changes.*/
static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi)
@@ -497,9 +532,15 @@ PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIn
int i;
PaMacAUHAL *auhalHostApi;
PaDeviceInfo *deviceInfoArray;
+ int unixErr;
VVDBUG(("PaMacCore_Initialize(): hostApiIndex=%d\n", hostApiIndex));
+ unixErr = initializeXRunListenerList();
+ if( 0 != unixErr ) {
+ return UNIX_ERR(unixErr);
+ }
+
auhalHostApi = (PaMacAUHAL*)PaUtil_AllocateMemory( sizeof(PaMacAUHAL) );
if( !auhalHostApi )
{
@@ -618,10 +659,16 @@ error:
static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
{
+ int unixErr;
+
PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi;
VVDBUG(("Terminate()\n"));
+ unixErr = destroyXRunListenerList();
+ if( 0 != unixErr )
+ UNIX_ERR(unixErr);
+
/*
IMPLEMENT ME:
- clean up any resources not handled by the allocation group
@@ -737,11 +784,12 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
}
static PaError OpenAndSetupOneAudioUnit(
+ const PaMacCoreStream *stream,
const PaStreamParameters *inStreamParams,
const PaStreamParameters *outStreamParams,
- const unsigned long requestedFramesPerBuffer,
- unsigned long *actualInputFramesPerBuffer,
- unsigned long *actualOutputFramesPerBuffer,
+ const UInt32 requestedFramesPerBuffer,
+ UInt32 *actualInputFramesPerBuffer,
+ UInt32 *actualOutputFramesPerBuffer,
const PaMacAUHAL *auhalHostApi,
AudioUnit *audioUnit,
AudioConverterRef *srConverter,
@@ -753,7 +801,7 @@ static PaError OpenAndSetupOneAudioUnit(
Component comp;
/*An Apple TN suggests using CAStreamBasicDescription, but that is C++*/
AudioStreamBasicDescription desiredFormat;
- OSErr result = noErr;
+ OSStatus result = noErr;
PaError paResult = paNoError;
int line = 0;
UInt32 callbackKey;
@@ -881,7 +929,7 @@ static PaError OpenAndSetupOneAudioUnit(
audioDevice,
sizeof(AudioDeviceID) ) );
}
- if( outStreamParams )
+ if( outStreamParams && outStreamParams != inStreamParams )
{
*audioDevice = auhalHostApi->devIds[outStreamParams->device] ;
ERR_WRAP( AudioUnitSetProperty( *audioUnit,
@@ -891,6 +939,24 @@ static PaError OpenAndSetupOneAudioUnit(
audioDevice,
sizeof(AudioDeviceID) ) );
}
+ /* -- add listener for dropouts -- */
+ result = AudioDeviceAddPropertyListener( *audioDevice,
+ 0,
+ outStreamParams ? false : true,
+ kAudioDeviceProcessorOverload,
+ xrunCallback,
+ addToXRunListenerList( (void *)stream ) ) ;
+ if( result == kAudioHardwareIllegalOperationError ) {
+ // -- already registered, we're good
+ } else {
+ // -- not already registered, just check for errors
+ ERR_WRAP( result );
+ }
+ /* -- listen for stream start and stop -- */
+ ERR_WRAP( AudioUnitAddPropertyListener( *audioUnit,
+ kAudioOutputUnitProperty_IsRunning,
+ startStopCallback,
+ (void *)stream ) );
/* -- set format -- */
bzero( &desiredFormat, sizeof(desiredFormat) );
@@ -1010,7 +1076,7 @@ static PaError OpenAndSetupOneAudioUnit(
kAudioUnitScope_Input,
OUTPUT_ELEMENT,
actualOutputFramesPerBuffer,
- sizeof(unsigned long) ) );
+ sizeof(*actualOutputFramesPerBuffer) ) );
ERR_WRAP( AudioUnitGetProperty( *audioUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
kAudioUnitScope_Global,
@@ -1025,7 +1091,7 @@ static PaError OpenAndSetupOneAudioUnit(
kAudioUnitScope_Output,
INPUT_ELEMENT,
actualInputFramesPerBuffer,
- sizeof(unsigned long) ) );
+ sizeof(*actualInputFramesPerBuffer) ) );
/* Don't know why this causes problems
ERR_WRAP( AudioUnitGetProperty( *audioUnit,
kAudioUnitProperty_MaximumFramesPerSlice,
@@ -1305,7 +1371,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/*requested a realtively low latency. make sure this is in range of devices */
/*try to get the device's min natural buffer size and use that (but no smaller than 64).*/
AudioValueRange audioRange;
- size_t size = sizeof( audioRange );
+ UInt32 size = sizeof( audioRange );
if( inputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device],
0,
@@ -1315,6 +1381,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( result )
requested = MAX( requested, audioRange.mMinimum );
}
+ size = sizeof( audioRange );
if( outputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device],
0,
@@ -1328,7 +1395,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* requested a realtively high latency. make sure this is in range of devices */
/*try to get the device's max natural buffer size and use that (but no larger than 1024).*/
AudioValueRange audioRange;
- size_t size = sizeof( audioRange );
+ UInt32 size = sizeof( audioRange );
requested = MIN( requested, 1024 );
if( inputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[inputParameters->device],
@@ -1339,6 +1406,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( result )
requested = MIN( requested, audioRange.mMaximum );
}
+ size = sizeof( audioRange );
if( outputParameters ) {
WARNING( result = AudioDeviceGetProperty( auhalHostApi->devIds[outputParameters->device],
0,
@@ -1359,17 +1427,22 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* -- Now we actually open and setup streams. -- */
if( inputParameters && outputParameters && outputParameters->device == inputParameters->device )
{ /* full duplex. One device. */
- result = OpenAndSetupOneAudioUnit( inputParameters,
+ UInt32 inputFramesPerBuffer = (UInt32) stream->inputFramesPerBuffer;
+ UInt32 outputFramesPerBuffer = (UInt32) stream->outputFramesPerBuffer;
+ result = OpenAndSetupOneAudioUnit( stream,
+ inputParameters,
outputParameters,
framesPerBuffer,
- &(stream->inputFramesPerBuffer),
- &(stream->outputFramesPerBuffer),
+ &inputFramesPerBuffer,
+ &outputFramesPerBuffer,
auhalHostApi,
&(stream->inputUnit),
&(stream->inputSRConverter),
&(stream->inputDevice),
sampleRate,
stream );
+ stream->inputFramesPerBuffer = inputFramesPerBuffer;
+ stream->outputFramesPerBuffer = outputFramesPerBuffer;
stream->outputUnit = stream->inputUnit;
stream->outputDevice = stream->inputDevice;
if( result != paNoError )
@@ -1377,11 +1450,14 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
}
else
{ /* full duplex, different devices OR simplex */
- result = OpenAndSetupOneAudioUnit( NULL,
+ UInt32 outputFramesPerBuffer = (UInt32) stream->outputFramesPerBuffer;
+ UInt32 inputFramesPerBuffer = (UInt32) stream->inputFramesPerBuffer;
+ result = OpenAndSetupOneAudioUnit( stream,
+ NULL,
outputParameters,
framesPerBuffer,
NULL,
- &(stream->outputFramesPerBuffer),
+ &outputFramesPerBuffer,
auhalHostApi,
&(stream->outputUnit),
NULL,
@@ -1390,10 +1466,11 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream );
if( result != paNoError )
goto error;
- result = OpenAndSetupOneAudioUnit( inputParameters,
+ result = OpenAndSetupOneAudioUnit( stream,
+ inputParameters,
NULL,
framesPerBuffer,
- &(stream->inputFramesPerBuffer),
+ &inputFramesPerBuffer,
NULL,
auhalHostApi,
&(stream->inputUnit),
@@ -1403,6 +1480,8 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
stream );
if( result != paNoError )
goto error;
+ stream->inputFramesPerBuffer = inputFramesPerBuffer;
+ stream->outputFramesPerBuffer = outputFramesPerBuffer;
}
if( stream->inputUnit ) {
@@ -1456,8 +1535,16 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
}
/* now we can initialize the ring buffer */
+ //FIXME: element size whould probably be szfl*inputchan
+ // but that will require some work all over the
+ // place to patch up. szfl may be sufficient and would
+ // be way easier to handle, but it seems clear from the
+ // discussion that buffer processor compatibility
+ // requires szfl*inputchan.
+ // See revision 1346 and discussion:
+ // http://techweb.rfa.org/pipermail/portaudio/2008-February/008295.html
PaUtil_InitializeRingBuffer( &stream->inputRingBuffer,
- ringSize*szfl, data ) ;
+ 1, ringSize*szfl, data ) ;
/* advance the read point a little, so we are reading from the
middle of the buffer */
if( stream->outputUnit )
@@ -1727,14 +1814,14 @@ static OSStatus AudioIOProc( void *inRefCon,
* we do not use the input SR converter or the input ring buffer.
*
*/
- OSErr err = 0;
+ OSStatus err = 0;
unsigned long frames;
/* -- start processing -- */
PaUtil_BeginBufferProcessing( &(stream->bufferProcessor),
&timeInfo,
stream->xrunFlags );
- stream->xrunFlags = 0;
+ stream->xrunFlags = 0; //FIXME: this flag also gets set outside by a callback, which calls the xrunCallback function. It should be in the same thread as the main audio callback, but the apple docs just use the word "usually" so it may be possible to loose an xrun notification, if that callback happens here.
/* -- compute frames. do some checks -- */
assert( ioData->mNumberBuffers == 1 );
@@ -1748,7 +1835,8 @@ static OSStatus AudioIOProc( void *inRefCon,
INPUT_ELEMENT,
inNumberFrames,
&stream->inputAudioBufferList );
- /* FEEDBACK: I'm not sure what to do when this call fails */
+ /* FEEDBACK: I'm not sure what to do when this call fails. There's nothing in the PA API to
+ * do about failures in the callback system. */
assert( !err );
PaUtil_SetInputFrameCount( &(stream->bufferProcessor), frames );
@@ -1868,7 +1956,7 @@ static OSStatus AudioIOProc( void *inRefCon,
PaUtil_AdvanceRingBufferReadIndex(&stream->inputRingBuffer, size1 );
} else if( ( size1 + size2 ) / ( flsz * inChan ) < frames ) {
/*we underflowed. take what data we can, zero the rest.*/
- float data[frames*inChan];
+ unsigned char data[frames*inChan*flsz];
if( size1 )
memcpy( data, data1, size1 );
if( size2 )
@@ -1922,7 +2010,7 @@ static OSStatus AudioIOProc( void *inRefCon,
* if this is an input-only stream, we need to process it more,
* otherwise, we let the output case deal with it.
*/
- OSErr err = 0;
+ OSStatus err = 0;
int chan = stream->inputAudioBufferList.mBuffers[0].mNumberChannels ;
/* FIXME: looping here may not actually be necessary, but it was something I tried in testing. */
do {
@@ -2054,6 +2142,24 @@ static PaError CloseStream( PaStream* s )
VDBUG( ( "Closing stream.\n" ) );
if( stream ) {
+ if( stream->outputUnit ) {
+ int count = removeFromXRunListenerList( stream );
+ if( count == 0 )
+ AudioDeviceRemovePropertyListener( stream->outputDevice,
+ 0,
+ false,
+ kAudioDeviceProcessorOverload,
+ xrunCallback );
+ }
+ if( stream->inputUnit && stream->outputUnit != stream->inputUnit ) {
+ int count = removeFromXRunListenerList( stream );
+ if( count == 0 )
+ AudioDeviceRemovePropertyListener( stream->inputDevice,
+ 0,
+ true,
+ kAudioDeviceProcessorOverload,
+ xrunCallback );
+ }
if( stream->outputUnit && stream->outputUnit != stream->inputUnit ) {
AudioUnitUninitialize( stream->outputUnit );
CloseComponent( stream->outputUnit );
@@ -2089,11 +2195,10 @@ static PaError CloseStream( PaStream* s )
return result;
}
-
static PaError StartStream( PaStream *s )
{
PaMacCoreStream *stream = (PaMacCoreStream*)s;
- OSErr result = noErr;
+ OSStatus result = noErr;
VVDBUG(("StartStream()\n"));
VDBUG( ( "Starting stream.\n" ) );
@@ -2138,7 +2243,7 @@ static ComponentResult BlockWhileAudioUnitIsRunning( AudioUnit audioUnit, AudioU
static PaError StopStream( PaStream *s )
{
PaMacCoreStream *stream = (PaMacCoreStream*)s;
- OSErr result = noErr;
+ OSStatus result = noErr;
PaError paErr;
VVDBUG(("StopStream()\n"));
diff --git a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c
index 3b81389d..6d31a713 100644
--- a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c
+++ b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.c
@@ -53,7 +53,7 @@
/**
@file
- @ingroup hostaip_src
+ @ingroup hostapi_src
This file contains the implementation
required for blocking I/O. It is separated from pa_mac_core.c simply to ease
@@ -85,6 +85,22 @@ static size_t computeSampleSizeFromFormat( PaSampleFormat format )
default: return 0;
}
}
+/*
+ * Same as computeSampleSizeFromFormat, except that if
+ * the size is not a power of two, it returns the next power of two up
+ */
+static size_t computeSampleSizeFromFormatPow2( PaSampleFormat format )
+{
+ switch( format ) {
+ case paFloat32: return 4;
+ case paInt32: return 4;
+ case paInt24: return 4;
+ case paInt16: return 2;
+ case paInt8: case paUInt8: return 1;
+ default: return 0;
+ }
+}
+
/*
@@ -105,6 +121,7 @@ PaError initializeBlioRingBuffers(
{
void *data;
int result;
+ OSStatus err;
/* zeroify things */
bzero( blio, sizeof( PaMacBlio ) );
@@ -114,10 +131,14 @@ PaError initializeBlioRingBuffers(
blio->outputRingBuffer.buffer = NULL;
/* initialize simple data */
+ blio->ringBufferFrames = ringBufferSize;
blio->inputSampleFormat = inputSampleFormat;
- blio->inputSampleSize = computeSampleSizeFromFormat(inputSampleFormat);
+ blio->inputSampleSizeActual = computeSampleSizeFromFormat(inputSampleFormat);
+ blio->inputSampleSizePow2 = computeSampleSizeFromFormatPow2(inputSampleFormat);
blio->outputSampleFormat = outputSampleFormat;
- blio->outputSampleSize = computeSampleSizeFromFormat(outputSampleFormat);
+ blio->outputSampleSizeActual = computeSampleSizeFromFormat(outputSampleFormat);
+ blio->outputSampleSizePow2 = computeSampleSizeFromFormatPow2(outputSampleFormat);
+
blio->framesPerBuffer = framesPerBuffer;
blio->inChan = inChan;
blio->outChan = outChan;
@@ -142,30 +163,32 @@ PaError initializeBlioRingBuffers(
result = UNIX_ERR( pthread_cond_init( &(blio->outputCond), NULL ) );
#endif
if( inChan ) {
- data = calloc( ringBufferSize, blio->inputSampleSize );
+ data = calloc( ringBufferSize, blio->inputSampleSizePow2*inChan );
if( !data )
{
result = paInsufficientMemory;
goto error;
}
- assert( 0 == PaUtil_InitializeRingBuffer(
+ err = PaUtil_InitializeRingBuffer(
&blio->inputRingBuffer,
- ringBufferSize*blio->inputSampleSize,
- data ) );
+ 1, ringBufferSize*blio->inputSampleSizePow2*inChan,
+ data );
+ assert( !err );
}
if( outChan ) {
- data = calloc( ringBufferSize, blio->outputSampleSize );
+ data = calloc( ringBufferSize, blio->outputSampleSizePow2*outChan );
if( !data )
{
result = paInsufficientMemory;
goto error;
}
- assert( 0 == PaUtil_InitializeRingBuffer(
+ err = PaUtil_InitializeRingBuffer(
&blio->outputRingBuffer,
- ringBufferSize*blio->outputSampleSize,
- data ) );
+ 1, ringBufferSize*blio->outputSampleSizePow2*outChan,
+ data );
+ assert( !err );
}
result = resetBlioRingBuffers( blio );
@@ -247,7 +270,8 @@ PaError resetBlioRingBuffers( PaMacBlio *blio )
bzero( blio->outputRingBuffer.buffer,
blio->outputRingBuffer.bufferSize );
/* Advance buffer */
- PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize );
+ PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->ringBufferFrames*blio->outputSampleSizeActual*blio->outChan );
+ //PaUtil_AdvanceRingBufferWriteIndex( &blio->outputRingBuffer, blio->outputRingBuffer.bufferSize );
/* Update isOutputFull. */
#ifdef PA_MAC__BLIO_MUTEX
@@ -323,6 +347,8 @@ int BlioCallback( const void *input, void *output, unsigned long frameCount,
long avail;
long toRead;
long toWrite;
+ long read;
+ long written;
/* set flags returned by OS: */
OSAtomicOr32( statusFlags, &blio->statusFlags ) ;
@@ -332,14 +358,15 @@ int BlioCallback( const void *input, void *output, unsigned long frameCount,
avail = PaUtil_GetRingBufferWriteAvailable( &blio->inputRingBuffer );
/* check for underflow */
- if( avail < frameCount * blio->inputSampleSize * blio->inChan )
+ if( avail < frameCount * blio->inputSampleSizeActual * blio->inChan )
OSAtomicOr32( paInputOverflow, &blio->statusFlags );
- toRead = MIN( avail, frameCount * blio->inputSampleSize * blio->inChan );
+ toRead = MIN( avail, frameCount * blio->inputSampleSizeActual * blio->inChan );
/* copy the data */
/*printf( "reading %d\n", toRead );*/
- assert( toRead == PaUtil_WriteRingBuffer( &blio->inputRingBuffer, input, toRead ) );
+ read = PaUtil_WriteRingBuffer( &blio->inputRingBuffer, input, toRead );
+ assert( toRead == read );
#ifdef PA_MAC__BLIO_MUTEX
/* Priority inversion. See notes below. */
blioSetIsInputEmpty( blio, false );
@@ -352,17 +379,18 @@ int BlioCallback( const void *input, void *output, unsigned long frameCount,
avail = PaUtil_GetRingBufferReadAvailable( &blio->outputRingBuffer );
/* check for underflow */
- if( avail < frameCount * blio->outputSampleSize * blio->outChan )
+ if( avail < frameCount * blio->outputSampleSizeActual * blio->outChan )
OSAtomicOr32( paOutputUnderflow, &blio->statusFlags );
- toWrite = MIN( avail, frameCount * blio->outputSampleSize * blio->outChan );
+ toWrite = MIN( avail, frameCount * blio->outputSampleSizeActual * blio->outChan );
- if( toWrite != frameCount * blio->outputSampleSize * blio->outChan )
+ if( toWrite != frameCount * blio->outputSampleSizeActual * blio->outChan )
bzero( ((char *)output)+toWrite,
- frameCount * blio->outputSampleSize * blio->outChan - toWrite );
+ frameCount * blio->outputSampleSizeActual * blio->outChan - toWrite );
/* copy the data */
/*printf( "writing %d\n", toWrite );*/
- assert( toWrite == PaUtil_ReadRingBuffer( &blio->outputRingBuffer, output, toWrite ) );
+ written = PaUtil_ReadRingBuffer( &blio->outputRingBuffer, output, toWrite );
+ assert( toWrite == written );
#ifdef PA_MAC__BLIO_MUTEX
/* We have a priority inversion here. However, we will only have to
wait if this was true and is now false, which means we've got
@@ -413,11 +441,11 @@ PaError ReadStream( PaStream* stream,
#endif
}
} while( avail == 0 );
- toRead = MIN( avail, frames * blio->inputSampleSize * blio->inChan );
- toRead -= toRead % blio->inputSampleSize * blio->inChan ;
+ toRead = MIN( avail, frames * blio->inputSampleSizeActual * blio->inChan );
+ toRead -= toRead % blio->inputSampleSizeActual * blio->inChan ;
PaUtil_ReadRingBuffer( &blio->inputRingBuffer, (void *)cbuf, toRead );
cbuf += toRead;
- frames -= toRead / ( blio->inputSampleSize * blio->inChan );
+ frames -= toRead / ( blio->inputSampleSizeActual * blio->inChan );
if( toRead == avail ) {
#ifdef PA_MAC_BLIO_MUTEX
@@ -443,7 +471,7 @@ PaError ReadStream( PaStream* stream,
/* report underflow only once: */
if( ret ) {
- OSAtomicAnd32( ~paInputOverflow, &blio->statusFlags );
+ OSAtomicAnd32( (uint32_t)(~paInputOverflow), &blio->statusFlags );
ret = paInputOverflowed;
}
@@ -491,11 +519,11 @@ PaError WriteStream( PaStream* stream,
}
} while( avail == 0 );
- toWrite = MIN( avail, frames * blio->outputSampleSize * blio->outChan );
- toWrite -= toWrite % blio->outputSampleSize * blio->outChan ;
+ toWrite = MIN( avail, frames * blio->outputSampleSizeActual * blio->outChan );
+ toWrite -= toWrite % blio->outputSampleSizeActual * blio->outChan ;
PaUtil_WriteRingBuffer( &blio->outputRingBuffer, (void *)cbuf, toWrite );
cbuf += toWrite;
- frames -= toWrite / ( blio->outputSampleSize * blio->outChan );
+ frames -= toWrite / ( blio->outputSampleSizeActual * blio->outChan );
#ifdef PA_MAC_BLIO_MUTEX
if( toWrite == avail ) {
@@ -520,7 +548,7 @@ PaError WriteStream( PaStream* stream,
/* report underflow only once: */
if( ret ) {
- OSAtomicAnd32( ~paOutputUnderflow, &blio->statusFlags );
+ OSAtomicAnd32( (uint32_t)(~paOutputUnderflow), &blio->statusFlags );
ret = paOutputUnderflowed;
}
@@ -549,7 +577,7 @@ signed long GetStreamReadAvailable( PaStream* stream )
VVDBUG(("GetStreamReadAvailable()\n"));
return PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer )
- / ( blio->outputSampleSize * blio->outChan );
+ / ( blio->inputSampleSizeActual * blio->inChan );
}
@@ -559,6 +587,6 @@ signed long GetStreamWriteAvailable( PaStream* stream )
VVDBUG(("GetStreamWriteAvailable()\n"));
return PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer )
- / ( blio->outputSampleSize * blio->outChan );
+ / ( blio->outputSampleSizeActual * blio->outChan );
}
diff --git a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.h b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.h
index 8ad79eaa..971223b3 100644
--- a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.h
+++ b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_blocking.h
@@ -53,7 +53,7 @@
/**
@file
- @ingroup hostaip_src
+ @ingroup hostapi_src
*/
#ifndef PA_MAC_CORE_BLOCKING_H_
@@ -79,10 +79,13 @@
typedef struct {
PaUtilRingBuffer inputRingBuffer;
PaUtilRingBuffer outputRingBuffer;
+ size_t ringBufferFrames;
PaSampleFormat inputSampleFormat;
- size_t inputSampleSize;
+ size_t inputSampleSizeActual;
+ size_t inputSampleSizePow2;
PaSampleFormat outputSampleFormat;
- size_t outputSampleSize;
+ size_t outputSampleSizeActual;
+ size_t outputSampleSizePow2;
size_t framesPerBuffer;
diff --git a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_internal.h b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_internal.h
index 998b819c..1797cbaf 100644
--- a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_internal.h
+++ b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_internal.h
@@ -61,10 +61,11 @@
#ifndef PA_MAC_CORE_INTERNAL_H__
#define PA_MAC_CORE_INTERNAL_H__
+#include <CoreAudio/CoreAudio.h>
+#include <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
-
#include "portaudio.h"
#include "pa_util.h"
#include "pa_hostapi.h"
@@ -139,6 +140,7 @@ typedef struct PaMacCoreStream
/* We need to preallocate an inputBuffer for reading data. */
AudioBufferList inputAudioBufferList;
AudioTimeStamp startTime;
+ /* FIXME: instead of volatile, these should be properly memory barriered */
volatile PaStreamCallbackFlags xrunFlags;
volatile bool isTimeSet;
volatile enum {
@@ -146,7 +148,8 @@ typedef struct PaMacCoreStream
and the user has called StopStream(). */
CALLBACK_STOPPED = 1, /* callback has requested stop,
but user has not yet called StopStream(). */
- STOPPING = 2, /* The stream is in the process of closing.
+ STOPPING = 2, /* The stream is in the process of closing
+ because the user has called StopStream.
This state is just used internally;
externally it is indistinguishable from
ACTIVE.*/
diff --git a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c
index 37403251..5bc592e8 100644
--- a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c
+++ b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.c
@@ -57,6 +57,10 @@
*/
#include "pa_mac_core_utilities.h"
+#include "pa_mac_core_internal.h"
+#include <libkern/OSAtomic.h>
+#include <strings.h>
+#include <pthread.h>
PaError PaMacCore_SetUnixError( int err, int line )
{
@@ -199,10 +203,19 @@ PaError PaMacCore_SetError(OSStatus error, int line, int isError)
else
errorType = "Warning";
- if ((int)error < -99999 || (int)error > 99999)
- DBUG(("%s on line %d: err='%4s', msg='%s'\n", errorType, line, (const char *)&error, errorText));
- else
- DBUG(("%s on line %d: err=%d, 0x%x, msg='%s'\n", errorType, line, (int)error, (unsigned)error, errorText));
+ char str[20];
+ // see if it appears to be a 4-char-code
+ *(UInt32 *)(str + 1) = CFSwapInt32HostToBig(error);
+ if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4]))
+ {
+ str[0] = str[5] = '\'';
+ str[6] = '\0';
+ } else {
+ // no, format it as an integer
+ sprintf(str, "%d", (int)error);
+ }
+
+ DBUG(("%s on line %d: err='%s', msg=%s\n", errorType, line, str, errorText));
PaUtil_SetLastHostErrorInfo( paCoreAudio, error, errorText );
@@ -520,9 +533,9 @@ PaError setBestSampleRateForDevice( const AudioDeviceID device,
not usually catastrophic.
*/
PaError setBestFramesPerBuffer( const AudioDeviceID device,
- const bool isOutput,
- unsigned long requestedFramesPerBuffer,
- unsigned long *actualFramesPerBuffer )
+ const bool isOutput,
+ UInt32 requestedFramesPerBuffer,
+ UInt32 *actualFramesPerBuffer )
{
UInt32 afpb;
const bool isInput = !isOutput;
@@ -609,3 +622,117 @@ PaError setBestFramesPerBuffer( const AudioDeviceID device,
return paNoError;
}
+
+/**********************
+ *
+ * XRun stuff
+ *
+ **********************/
+
+struct PaMacXRunListNode_s {
+ PaMacCoreStream *stream;
+ struct PaMacXRunListNode_s *next;
+} ;
+
+typedef struct PaMacXRunListNode_s PaMacXRunListNode;
+
+/** Always empty, so that it can always be the one returned by
+ addToXRunListenerList. note that it's not a pointer. */
+static PaMacXRunListNode firstXRunListNode;
+static int xRunListSize;
+static pthread_mutex_t xrunMutex;
+
+OSStatus xrunCallback(
+ AudioDeviceID inDevice,
+ UInt32 inChannel,
+ Boolean isInput,
+ AudioDevicePropertyID inPropertyID,
+ void* inClientData)
+{
+ PaMacXRunListNode *node = (PaMacXRunListNode *) inClientData;
+
+ int ret = pthread_mutex_trylock( &xrunMutex ) ;
+
+ if( ret == 0 ) {
+
+ node = node->next ; //skip the first node
+
+ for( ; node; node=node->next ) {
+ PaMacCoreStream *stream = node->stream;
+
+ if( stream->state != ACTIVE )
+ continue; //if the stream isn't active, we don't care if the device is dropping
+
+ if( isInput ) {
+ if( stream->inputDevice == inDevice )
+ OSAtomicOr32( paInputOverflow, (uint32_t *)&(stream->xrunFlags) );
+ } else {
+ if( stream->outputDevice == inDevice )
+ OSAtomicOr32( paOutputUnderflow, (uint32_t *)&(stream->xrunFlags) );
+ }
+ }
+
+ pthread_mutex_unlock( &xrunMutex );
+ }
+
+ return 0;
+}
+
+int initializeXRunListenerList()
+{
+ xRunListSize = 0;
+ bzero( (void *) &firstXRunListNode, sizeof(firstXRunListNode) );
+ return pthread_mutex_init( &xrunMutex, NULL );
+}
+int destroyXRunListenerList()
+{
+ PaMacXRunListNode *node;
+ node = firstXRunListNode.next;
+ while( node ) {
+ PaMacXRunListNode *tmp = node;
+ node = node->next;
+ free( tmp );
+ }
+ xRunListSize = 0;
+ return pthread_mutex_destroy( &xrunMutex );
+}
+
+void *addToXRunListenerList( void *stream )
+{
+ pthread_mutex_lock( &xrunMutex );
+ PaMacXRunListNode *newNode;
+ // setup new node:
+ newNode = (PaMacXRunListNode *) malloc( sizeof( PaMacXRunListNode ) );
+ newNode->stream = (PaMacCoreStream *) stream;
+ newNode->next = firstXRunListNode.next;
+ // insert:
+ firstXRunListNode.next = newNode;
+ pthread_mutex_unlock( &xrunMutex );
+
+ return &firstXRunListNode;
+}
+
+int removeFromXRunListenerList( void *stream )
+{
+ pthread_mutex_lock( &xrunMutex );
+ PaMacXRunListNode *node, *prev;
+ prev = &firstXRunListNode;
+ node = firstXRunListNode.next;
+ while( node ) {
+ if( node->stream == stream ) {
+ //found it:
+ --xRunListSize;
+ prev->next = node->next;
+ free( node );
+ pthread_mutex_unlock( &xrunMutex );
+ return xRunListSize;
+ }
+ prev = prev->next;
+ node = node->next;
+ }
+
+ pthread_mutex_unlock( &xrunMutex );
+ // failure
+ return xRunListSize;
+}
+
diff --git a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.h b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.h
index 8a69c25a..899826d5 100644
--- a/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.h
+++ b/pd/portaudio/src/hostapi/coreaudio/pa_mac_core_utilities.h
@@ -199,7 +199,32 @@ PaError setBestSampleRateForDevice( const AudioDeviceID device,
not usually catastrophic.
*/
PaError setBestFramesPerBuffer( const AudioDeviceID device,
- const bool isOutput,
- unsigned long requestedFramesPerBuffer,
- unsigned long *actualFramesPerBuffer );
+ const bool isOutput,
+ UInt32 requestedFramesPerBuffer,
+ UInt32 *actualFramesPerBuffer );
+
+
+/*********************
+ *
+ * xrun handling
+ *
+ *********************/
+
+OSStatus xrunCallback(
+ AudioDeviceID inDevice,
+ UInt32 inChannel,
+ Boolean isInput,
+ AudioDevicePropertyID inPropertyID,
+ void* inClientData ) ;
+
+/** returns zero on success or a unix style error code. */
+int initializeXRunListenerList();
+/** returns zero on success or a unix style error code. */
+int destroyXRunListenerList();
+
+/**Returns the list, so that it can be passed to CorAudio.*/
+void *addToXRunListenerList( void *stream );
+/**Returns the number of Listeners in the list remaining.*/
+int removeFromXRunListenerList( void *stream );
+
#endif /* PA_MAC_CORE_UTILITIES_H__*/
diff --git a/pd/portaudio/src/hostapi/jack/pa_jack.c b/pd/portaudio/src/hostapi/jack/pa_jack.c
index 6b6c2120..6732c9b1 100644
--- a/pd/portaudio/src/hostapi/jack/pa_jack.c
+++ b/pd/portaudio/src/hostapi/jack/pa_jack.c
@@ -1,5 +1,5 @@
/*
- * $Id: pa_jack.c 1238 2007-07-15 16:58:50Z aknudsen $
+ * $Id: pa_jack.c 1346 2008-02-20 10:09:20Z rossb $
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* JACK Implementation by Joshua Haberman
@@ -254,7 +254,7 @@ static PaError BlockingInitFIFO( PaUtilRingBuffer *rbuf, long numFrames, long by
char *buffer = (char *) malloc( numBytes );
if( buffer == NULL ) return paInsufficientMemory;
memset( buffer, 0, numBytes );
- return (PaError) PaUtil_InitializeRingBuffer( rbuf, numBytes, buffer );
+ return (PaError) PaUtil_InitializeRingBuffer( rbuf, 1, numBytes, buffer );
}
/* Free buffer. */
@@ -717,14 +717,19 @@ PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi,
ASSERT_CALL( pthread_cond_init( &jackHostApi->cond, NULL ), 0 );
/* Try to become a client of the JACK server. If we cannot do
- * this, then this API cannot be used. */
+ * this, then this API cannot be used.
+ *
+ * Without the JackNoStartServer option, the jackd server is started
+ * automatically which we do not want.
+ */
- jackHostApi->jack_client = jack_client_open( clientName_, 0, &jackStatus );
+ jackHostApi->jack_client = jack_client_open( clientName_, JackNoStartServer, &jackStatus );
if( !jackHostApi->jack_client )
{
/* the V19 development docs say that if an implementation
* detects that it cannot be used, it should return a NULL
* interface and paNoError */
+ PA_DEBUG(( "%s: Couldn't connect to JACK, status: %d\n", __FUNCTION__, jackStatus ));
result = paNoError;
goto error;
}
@@ -737,7 +742,6 @@ PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi,
(*hostApi)->info.name = "JACK Audio Connection Kit";
/* Build a device list by querying the JACK server */
-
ENSURE_PA( BuildDeviceList( jackHostApi ) );
/* Register functions */
@@ -1748,7 +1752,8 @@ PaError PaJack_GetClientName(const char** clientName)
{
PaError result = paNoError;
PaJackHostApiRepresentation* jackHostApi = NULL;
- ENSURE_PA( PaUtil_GetHostApiRepresentation( (PaUtilHostApiRepresentation**)&jackHostApi, paJACK ) );
+ PaJackHostApiRepresentation** ref = &jackHostApi;
+ ENSURE_PA( PaUtil_GetHostApiRepresentation( (PaUtilHostApiRepresentation**)ref, paJACK ) );
*clientName = jack_get_client_name( jackHostApi->jack_client );
error:
diff --git a/pd/portaudio/src/hostapi/oss/pa_unix_oss.c b/pd/portaudio/src/hostapi/oss/pa_unix_oss.c
index 516f5a45..8d8424fc 100644
--- a/pd/portaudio/src/hostapi/oss/pa_unix_oss.c
+++ b/pd/portaudio/src/hostapi/oss/pa_unix_oss.c
@@ -1,5 +1,5 @@
/*
- * $Id: pa_unix_oss.c 1238 2007-07-15 16:58:50Z aknudsen $
+ * $Id: pa_unix_oss.c 1385 2008-06-05 21:13:54Z aknudsen $
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* OSS implementation by:
@@ -65,7 +65,11 @@
#ifdef HAVE_SYS_SOUNDCARD_H
# include <sys/soundcard.h>
-# define DEVICE_NAME_BASE "/dev/dsp"
+# ifdef __NetBSD__
+# define DEVICE_NAME_BASE "/dev/audio"
+# else
+# define DEVICE_NAME_BASE "/dev/dsp"
+# endif
#elif defined(HAVE_LINUX_SOUNDCARD_H)
# include <linux/soundcard.h>
# define DEVICE_NAME_BASE "/dev/dsp"
@@ -97,7 +101,7 @@ static pthread_t mainThread_;
/* PaUtil_SetLastHostErrorInfo should only be used in the main thread */ \
if( (code) == paUnanticipatedHostError && pthread_self() == mainThread_ ) \
{ \
- PaUtil_SetLastHostErrorInfo( paALSA, sysErr_, strerror( errno ) ); \
+ PaUtil_SetLastHostErrorInfo( paOSS, sysErr_, strerror( errno ) ); \
} \
\
PaUtil_DebugPrint(( "Expression '" #expr "' failed in '" __FILE__ "', line: " STRINGIZE( __LINE__ ) "\n" )); \
@@ -332,7 +336,11 @@ static PaError QueryDirection( const char *deviceName, StreamMode mode, double *
}
else
{
- PA_DEBUG(( "%s: Can't access device: %s\n", __FUNCTION__, strerror( errno ) ));
+ /* Ignore ENOENT, which means we've tried a non-existent device */
+ if( errno != ENOENT )
+ {
+ PA_DEBUG(( "%s: Can't access device %s: %s\n", __FUNCTION__, deviceName, strerror( errno ) ));
+ }
}
return paDeviceUnavailable;
@@ -401,11 +409,7 @@ static PaError QueryDirection( const char *deviceName, StreamMode mode, double *
if( *defaultSampleRate < 0 )
{
sr = 44100;
- if( ioctl( devHandle, SNDCTL_DSP_SPEED, &sr ) < 0 )
- {
- result = paUnanticipatedHostError;
- goto error;
- }
+ ENSURE_( ioctl( devHandle, SNDCTL_DSP_SPEED, &sr ), paUnanticipatedHostError );
*defaultSampleRate = sr;
}
@@ -508,27 +512,20 @@ static PaError BuildDeviceList( PaOSSHostApiRepresentation *ossApi )
/* Find devices by calling QueryDevice on each
* potential device names. When we find a valid one,
* add it to a linked list.
- * A: Can there only be 10 devices? */
+ * A: Set an arbitrary of 100 devices, should probably be a smarter way. */
- for( i = 0; i < 10; i++ )
+ for( i = 0; i < 100; i++ )
{
char deviceName[32];
PaDeviceInfo *deviceInfo;
int testResult;
- struct stat stbuf;
if( i == 0 )
snprintf(deviceName, sizeof (deviceName), "%s", DEVICE_NAME_BASE);
else
snprintf(deviceName, sizeof (deviceName), "%s%d", DEVICE_NAME_BASE, i);
- /* PA_DEBUG(("PaOSS BuildDeviceList: trying device %s\n", deviceName )); */
- if( stat( deviceName, &stbuf ) < 0 )
- {
- if( ENOENT != errno )
- PA_DEBUG(( "%s: Error stat'ing %s: %s\n", __FUNCTION__, deviceName, strerror( errno ) ));
- continue;
- }
+ /* PA_DEBUG(("%s: trying device %s\n", __FUNCTION__, deviceName )); */
if( (testResult = QueryDevice( deviceName, ossApi, &deviceInfo )) != paNoError )
{
if( testResult != paDeviceUnavailable )
@@ -785,11 +782,15 @@ error:
return result;
}
+/** Open input and output devices.
+ *
+ * @param idev: Returned input device file descriptor.
+ * @param odev: Returned output device file descriptor.
+ */
static PaError OpenDevices( const char *idevName, const char *odevName, int *idev, int *odev )
{
PaError result = paNoError;
int flags = O_NONBLOCK, duplex = 0;
- int enableBits = 0;
*idev = *odev = -1;
if( idevName && odevName )
@@ -809,10 +810,6 @@ static PaError OpenDevices( const char *idevName, const char *odevName, int *ide
{
ENSURE_( *idev = open( idevName, flags ), paDeviceUnavailable );
PA_ENSURE( ModifyBlocking( *idev, 1 ) ); /* Blocking */
-
- /* Initially disable */
- enableBits = ~PCM_ENABLE_INPUT;
- ENSURE_( ioctl( *idev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
}
if( odevName )
{
@@ -820,10 +817,6 @@ static PaError OpenDevices( const char *idevName, const char *odevName, int *ide
{
ENSURE_( *odev = open( odevName, flags ), paDeviceUnavailable );
PA_ENSURE( ModifyBlocking( *odev, 1 ) ); /* Blocking */
-
- /* Initially disable */
- enableBits = ~PCM_ENABLE_OUTPUT;
- ENSURE_( ioctl( *odev, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
}
else
{
@@ -976,8 +969,10 @@ static int CalcHigherLogTwo( int n )
return log2;
}
-static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long framesPerBuffer,
- StreamMode streamMode, PaOssStreamComponent *master )
+/** Configure stream component device parameters.
+ */
+static PaError PaOssStreamComponent_Configure( PaOssStreamComponent *component, double sampleRate, unsigned long
+ framesPerBuffer, StreamMode streamMode, PaOssStreamComponent *master )
{
PaError result = paNoError;
int temp, nativeFormat;
@@ -1189,6 +1184,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
const PaDeviceInfo *inputDeviceInfo = 0, *outputDeviceInfo = 0;
int bpInitialized = 0;
double inLatency = 0., outLatency = 0.;
+ int i = 0;
/* validate platform specific flags */
if( (streamFlags & paPlatformSpecificFlags) != 0 )
@@ -1225,6 +1221,14 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
}
}
+ /* Round framesPerBuffer to the next power-of-two to make OSS happy. */
+ if( framesPerBuffer != paFramesPerBufferUnspecified )
+ {
+ framesPerBuffer &= INT_MAX;
+ for (i = 1; framesPerBuffer > i; i <<= 1) ;
+ framesPerBuffer = i;
+ }
+
/* allocate and do basic initialization of the stream structure */
PA_UNLESS( stream = (PaOssStream*)PaUtil_AllocateMemory( sizeof(PaOssStream) ), paInsufficientMemory );
PA_ENSURE( PaOssStream_Initialize( stream, inputParameters, outputParameters, streamCallback, userData, streamFlags, ossHostApi ) );
@@ -1428,6 +1432,12 @@ static PaError PaOssStream_Prepare( PaOssStream *stream )
if( stream->triggered )
return result;
+ /* The OSS reference instructs us to clear direction bits before setting them.*/
+ if( stream->playback )
+ ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+ if( stream->capture )
+ ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_SETTRIGGER, &enableBits ), paUnanticipatedHostError );
+
if( stream->playback )
{
size_t bufSz = PaOssStreamComponent_BufferSize( stream->playback );
@@ -1478,17 +1488,29 @@ static PaError PaOssStream_Stop( PaOssStream *stream, int abort )
PaError result = paNoError;
/* Looks like the only safe way to stop audio without reopening the device is SNDCTL_DSP_POST.
- * Also disable capture/playback till the stream is started again */
+ * Also disable capture/playback till the stream is started again.
+ */
+ int captureErr = 0, playbackErr = 0;
if( stream->capture )
{
- ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
+ if( (captureErr = ioctl( stream->capture->fd, SNDCTL_DSP_POST, 0 )) < 0 )
+ {
+ PA_DEBUG(( "%s: Failed to stop capture device, error: %d\n", __FUNCTION__, captureErr ));
+ }
}
if( stream->playback && !stream->sharedDevice )
{
- ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_POST, 0 ), paUnanticipatedHostError );
+ if( (playbackErr = ioctl( stream->playback->fd, SNDCTL_DSP_POST, 0 )) < 0 )
+ {
+ PA_DEBUG(( "%s: Failed to stop playback device, error: %d\n", __FUNCTION__, playbackErr ));
+ }
+ }
+
+ if( captureErr || playbackErr )
+ {
+ result = paUnanticipatedHostError;
}
-error:
return result;
}
@@ -1852,6 +1874,7 @@ static PaError ReadStream( PaStream* s,
void *buffer,
unsigned long frames )
{
+ PaError result = paNoError;
PaOssStream *stream = (PaOssStream*)s;
int bytesRequested, bytesRead;
unsigned long framesRequested;
@@ -1872,21 +1895,28 @@ static PaError ReadStream( PaStream* s,
framesRequested = PA_MIN( frames, stream->capture->hostFrames );
bytesRequested = framesRequested * PaOssStreamComponent_FrameSize( stream->capture );
- bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested );
+ ENSURE_( (bytesRead = read( stream->capture->fd, stream->capture->buffer, bytesRequested )),
+ paUnanticipatedHostError );
if ( bytesRequested != bytesRead )
+ {
+ PA_DEBUG(( "Requested %d bytes, read %d\n", bytesRequested, bytesRead ));
return paUnanticipatedHostError;
+ }
PaUtil_SetInputFrameCount( &stream->bufferProcessor, stream->capture->hostFrames );
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, 0, stream->capture->buffer, stream->capture->hostChannelCount );
PaUtil_CopyInput( &stream->bufferProcessor, &userBuffer, framesRequested );
frames -= framesRequested;
}
- return paNoError;
+
+error:
+ return result;
}
static PaError WriteStream( PaStream *s, const void *buffer, unsigned long frames )
{
+ PaError result = paNoError;
PaOssStream *stream = (PaOssStream*)s;
int bytesRequested, bytesWritten;
unsigned long framesConverted;
@@ -1912,35 +1942,50 @@ static PaError WriteStream( PaStream *s, const void *buffer, unsigned long frame
frames -= framesConverted;
bytesRequested = framesConverted * PaOssStreamComponent_FrameSize( stream->playback );
- bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested );
+ ENSURE_( (bytesWritten = write( stream->playback->fd, stream->playback->buffer, bytesRequested )),
+ paUnanticipatedHostError );
if ( bytesRequested != bytesWritten )
+ {
+ PA_DEBUG(( "Requested %d bytes, wrote %d\n", bytesRequested, bytesWritten ));
return paUnanticipatedHostError;
+ }
}
- return paNoError;
+
+error:
+ return result;
}
static signed long GetStreamReadAvailable( PaStream* s )
{
+ PaError result = paNoError;
PaOssStream *stream = (PaOssStream*)s;
audio_buf_info info;
- if( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ) < 0 )
- return paUnanticipatedHostError;
+ ENSURE_( ioctl( stream->capture->fd, SNDCTL_DSP_GETISPACE, &info ), paUnanticipatedHostError );
return info.fragments * stream->capture->hostFrames;
+
+error:
+ return result;
}
/* TODO: Compute number of allocated bytes somewhere else, can we use ODELAY with capture */
static signed long GetStreamWriteAvailable( PaStream* s )
{
+ PaError result = paNoError;
PaOssStream *stream = (PaOssStream*)s;
int delay = 0;
-
- if( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ) < 0 )
- return paUnanticipatedHostError;
-
+#ifdef SNDCTL_DSP_GETODELAY
+ ENSURE_( ioctl( stream->playback->fd, SNDCTL_DSP_GETODELAY, &delay ), paUnanticipatedHostError );
+#endif
return (PaOssStreamComponent_BufferSize( stream->playback ) - delay) / PaOssStreamComponent_FrameSize( stream->playback );
+
+/* Conditionally compile this to avoid warning about unused label */
+#ifdef SNDCTL_DSP_GETODELAY
+error:
+ return result;
+#endif
}
diff --git a/pd/portaudio/src/hostapi/wmme/pa_win_wmme.c b/pd/portaudio/src/hostapi/wmme/pa_win_wmme.c
index 3264ebf8..d2c13f99 100644
--- a/pd/portaudio/src/hostapi/wmme/pa_win_wmme.c
+++ b/pd/portaudio/src/hostapi/wmme/pa_win_wmme.c
@@ -1,5 +1,5 @@
/*
- * $Id: pa_win_wmme.c 1229 2007-06-15 16:11:11Z rossb $
+ * $Id: pa_win_wmme.c 1405 2009-03-08 08:10:55Z rossb $
* pa_win_wmme.c
* Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
*
@@ -62,7 +62,9 @@
*/
/** @file
- @ingroup hostaip_src
+ @ingroup hostapi_src
+
+ @brief Win32 host API implementation for the Windows MultiMedia Extensions (WMME) audio API.
@todo Fix buffer catch up code, can sometimes get stuck (perhaps fixed now,
needs to be reviewed and tested.)
@@ -88,6 +90,10 @@ Non-critical stuff for the future:
@todo define UNICODE and _UNICODE in the project settings and see what breaks
+ @todo refactor conversion of MMSYSTEM errors into PA arrors into a single function.
+
+ @todo cleanup WAVEFORMATEXTENSIBLE retry in InitializeWaveHandles to not use a for loop
+
*/
/*
@@ -133,6 +139,24 @@ Non-critical stuff for the future:
#include "pa_debugprint.h"
#include "pa_win_wmme.h"
+#include "pa_win_waveformat.h"
+
+#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
+#include "pa_win_wdmks_utils.h"
+#ifndef DRV_QUERYDEVICEINTERFACE
+#define DRV_QUERYDEVICEINTERFACE (DRV_RESERVED + 12)
+#endif
+#ifndef DRV_QUERYDEVICEINTERFACESIZE
+#define DRV_QUERYDEVICEINTERFACESIZE (DRV_RESERVED + 13)
+#endif
+#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
+
+/* use CreateThread for CYGWIN, _beginthreadex for all others */
+#ifndef __CYGWIN__
+#define CREATE_THREAD (HANDLE)_beginthreadex( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
+#else
+#define CREATE_THREAD CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId )
+#endif
#if (defined(UNDER_CE))
#pragma comment(lib, "Coredll.lib")
@@ -374,6 +398,8 @@ typedef struct
{
PaDeviceInfo inheritedDeviceInfo;
DWORD dwFormats; /**<< standard formats bitmask from the WAVEINCAPS and WAVEOUTCAPS structures */
+ char deviceInputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
+ char deviceOutputChannelCountIsKnown; /**<< if the system returns 0xFFFF then we don't really know the number of supported channels (1=>known, 0=>unknown)*/
}
PaWinMmeDeviceInfo;
@@ -445,6 +471,21 @@ static UINT LocalDeviceIndexToWinMmeDeviceId( PaWinMmeHostApiRepresentation *hos
}
+static int SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( PaSampleFormat sampleFormat, unsigned long winMmeSpecificFlags )
+{
+ int waveFormatTag = 0;
+
+ if( winMmeSpecificFlags & paWinMmeWaveFormatDolbyAc3Spdif )
+ waveFormatTag = PAWIN_WAVE_FORMAT_DOLBY_AC3_SPDIF;
+ else if( winMmeSpecificFlags & paWinMmeWaveFormatWmaSpdif )
+ waveFormatTag = PAWIN_WAVE_FORMAT_WMA_SPDIF;
+ else
+ waveFormatTag = PaWin_SampleFormatToLinearWaveFormatTag( sampleFormat );
+
+ return waveFormatTag;
+}
+
+
static PaError QueryInputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx )
{
MMRESULT mmresult;
@@ -499,41 +540,57 @@ static PaError QueryOutputWaveFormatEx( int deviceId, WAVEFORMATEX *waveFormatEx
static PaError QueryFormatSupported( PaDeviceInfo *deviceInfo,
PaError (*waveFormatExQueryFunction)(int, WAVEFORMATEX*),
- int winMmeDeviceId, int channels, double sampleRate )
+ int winMmeDeviceId, int channels, double sampleRate, unsigned long winMmeSpecificFlags )
{
PaWinMmeDeviceInfo *winMmeDeviceInfo = (PaWinMmeDeviceInfo*)deviceInfo;
- WAVEFORMATEX waveFormatEx;
+ PaWinWaveFormat waveFormat;
+ PaSampleFormat sampleFormat;
+ int waveFormatTag;
- if( sampleRate == 11025.0
- && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16))
- || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){
+ /* @todo at the moment we only query with 16 bit sample format and directout speaker config*/
- return paNoError;
- }
+ sampleFormat = paInt16;
+ waveFormatTag = SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( sampleFormat, winMmeSpecificFlags );
- if( sampleRate == 22050.0
- && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16))
- || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){
+ if( waveFormatTag == PaWin_SampleFormatToLinearWaveFormatTag( paInt16 ) ){
+
+ /* attempt bypass querying the device for linear formats */
- return paNoError;
+ if( sampleRate == 11025.0
+ && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1M16))
+ || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_1S16)) ) ){
+
+ return paNoError;
+ }
+
+ if( sampleRate == 22050.0
+ && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2M16))
+ || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_2S16)) ) ){
+
+ return paNoError;
+ }
+
+ if( sampleRate == 44100.0
+ && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16))
+ || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){
+
+ return paNoError;
+ }
}
- if( sampleRate == 44100.0
- && ( (channels == 1 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4M16))
- || (channels == 2 && (winMmeDeviceInfo->dwFormats & WAVE_FORMAT_4S16)) ) ){
+ /* first, attempt to query the device using WAVEFORMATEXTENSIBLE,
+ if this fails we fall back to WAVEFORMATEX */
+
+ PaWin_InitializeWaveFormatExtensible( &waveFormat, channels, sampleFormat, waveFormatTag,
+ sampleRate, PAWIN_SPEAKER_DIRECTOUT );
+
+ if( waveFormatExQueryFunction( winMmeDeviceId, (WAVEFORMATEX*)&waveFormat ) == paNoError )
return paNoError;
- }
- waveFormatEx.wFormatTag = WAVE_FORMAT_PCM;
- waveFormatEx.nChannels = (WORD)channels;
- waveFormatEx.nSamplesPerSec = (DWORD)sampleRate;
- waveFormatEx.nAvgBytesPerSec = waveFormatEx.nSamplesPerSec * channels * sizeof(short);
- waveFormatEx.nBlockAlign = (WORD)(channels * sizeof(short));
- waveFormatEx.wBitsPerSample = 16;
- waveFormatEx.cbSize = 0;
+ PaWin_InitializeWaveFormatEx( &waveFormat, channels, sampleFormat, waveFormatTag, sampleRate );
- return waveFormatExQueryFunction( winMmeDeviceId, &waveFormatEx );
+ return waveFormatExQueryFunction( winMmeDeviceId, (WAVEFORMATEX*)&waveFormat );
}
@@ -553,7 +610,7 @@ static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int w
for( i=0; i < PA_DEFAULTSAMPLERATESEARCHORDER_COUNT_; ++i )
{
double sampleRate = defaultSampleRateSearchOrder_[ i ];
- PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate );
+ PaError paerror = QueryFormatSupported( deviceInfo, waveFormatExQueryFunction, winMmeDeviceId, maxChannels, sampleRate, 0 );
if( paerror == paNoError )
{
deviceInfo->defaultSampleRate = sampleRate;
@@ -563,6 +620,40 @@ static void DetectDefaultSampleRate( PaWinMmeDeviceInfo *winMmeDeviceInfo, int w
}
+#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
+static int QueryWaveInKSFilterMaxChannels( int waveInDeviceId, int *maxChannels )
+{
+ void *devicePath;
+ DWORD devicePathSize;
+ int result = 0;
+
+ if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
+ (DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
+ return 0;
+
+ devicePath = PaUtil_AllocateMemory( devicePathSize );
+ if( !devicePath )
+ return 0;
+
+ /* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */
+ if( waveInMessage((HWAVEIN)waveInDeviceId, DRV_QUERYDEVICEINTERFACE,
+ (DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR )
+ {
+ int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 1 );
+ if( count > 0 )
+ {
+ *maxChannels = count;
+ result = 1;
+ }
+ }
+
+ PaUtil_FreeMemory( devicePath );
+
+ return result;
+}
+#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
+
+
static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeInputDeviceId, int *success )
{
@@ -616,17 +707,30 @@ static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeH
}
deviceInfo->name = deviceName;
- deviceInfo->maxInputChannels = wic.wChannels;
- /* Sometimes a device can return a rediculously large number of channels.
- * This happened with an SBLive card on a Windows ME box.
- * If that happens, then force it to 2 channels. PLB20010413
+ if( wic.wChannels == 0xFFFF || wic.wChannels < 1 || wic.wChannels > 255 ){
+ /* For Windows versions using WDM (possibly Windows 98 ME and later)
+ * the kernel mixer sits between the application and the driver. As a result,
+ * wave*GetDevCaps often kernel mixer channel counts, which are unlimited.
+ * When this happens we assume the device is stereo and set a flag
+ * so that other channel counts can be tried with OpenStream -- i.e. when
+ * device*ChannelCountIsKnown is false, OpenStream will try whatever
+ * channel count you supply.
+ * see also InitializeOutputDeviceInfo() below.
*/
- if( (deviceInfo->maxInputChannels < 1) || (deviceInfo->maxInputChannels > 256) )
- {
- PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", deviceInfo->maxInputChannels ));
+
+ PA_DEBUG(("Pa_GetDeviceInfo: Num input channels reported as %d! Changed to 2.\n", wic.wChannels ));
deviceInfo->maxInputChannels = 2;
+ winMmeDeviceInfo->deviceInputChannelCountIsKnown = 0;
+ }else{
+ deviceInfo->maxInputChannels = wic.wChannels;
+ winMmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
}
+#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
+ winMmeDeviceInfo->deviceInputChannelCountIsKnown =
+ QueryWaveInKSFilterMaxChannels( winMmeInputDeviceId, &deviceInfo->maxInputChannels );
+#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
+
winMmeDeviceInfo->dwFormats = wic.dwFormats;
DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId,
@@ -639,6 +743,40 @@ error:
}
+#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
+static int QueryWaveOutKSFilterMaxChannels( int waveOutDeviceId, int *maxChannels )
+{
+ void *devicePath;
+ DWORD devicePathSize;
+ int result = 0;
+
+ if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACESIZE,
+ (DWORD_PTR)&devicePathSize, 0 ) != MMSYSERR_NOERROR )
+ return 0;
+
+ devicePath = PaUtil_AllocateMemory( devicePathSize );
+ if( !devicePath )
+ return 0;
+
+ /* apparently DRV_QUERYDEVICEINTERFACE returns a unicode interface path, although this is undocumented */
+ if( waveOutMessage((HWAVEOUT)waveOutDeviceId, DRV_QUERYDEVICEINTERFACE,
+ (DWORD_PTR)devicePath, devicePathSize ) == MMSYSERR_NOERROR )
+ {
+ int count = PaWin_WDMKS_QueryFilterMaximumChannelCount( devicePath, /* isInput= */ 0 );
+ if( count > 0 )
+ {
+ *maxChannels = count;
+ result = 1;
+ }
+ }
+
+ PaUtil_FreeMemory( devicePath );
+
+ return result;
+}
+#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
+
+
static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeHostApi,
PaWinMmeDeviceInfo *winMmeDeviceInfo, UINT winMmeOutputDeviceId, int *success )
{
@@ -647,7 +785,7 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
MMRESULT mmresult;
WAVEOUTCAPS woc;
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
-
+
*success = 0;
mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) );
@@ -692,17 +830,30 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
}
deviceInfo->name = deviceName;
- deviceInfo->maxOutputChannels = woc.wChannels;
- /* Sometimes a device can return a rediculously large number of channels.
- * This happened with an SBLive card on a Windows ME box.
- * It also happens on Win XP!
+ if( woc.wChannels == 0xFFFF || woc.wChannels < 1 || woc.wChannels > 255 ){
+ /* For Windows versions using WDM (possibly Windows 98 ME and later)
+ * the kernel mixer sits between the application and the driver. As a result,
+ * wave*GetDevCaps often kernel mixer channel counts, which are unlimited.
+ * When this happens we assume the device is stereo and set a flag
+ * so that other channel counts can be tried with OpenStream -- i.e. when
+ * device*ChannelCountIsKnown is false, OpenStream will try whatever
+ * channel count you supply.
+ * see also InitializeInputDeviceInfo() above.
*/
- if( (deviceInfo->maxOutputChannels < 1) || (deviceInfo->maxOutputChannels > 256) )
- {
- PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", deviceInfo->maxOutputChannels ));
+
+ PA_DEBUG(("Pa_GetDeviceInfo: Num output channels reported as %d! Changed to 2.\n", woc.wChannels ));
deviceInfo->maxOutputChannels = 2;
+ winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 0;
+ }else{
+ deviceInfo->maxOutputChannels = woc.wChannels;
+ winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
}
+#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
+ winMmeDeviceInfo->deviceOutputChannelCountIsKnown =
+ QueryWaveOutKSFilterMaxChannels( winMmeOutputDeviceId, &deviceInfo->maxOutputChannels );
+#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
+
winMmeDeviceInfo->dwFormats = woc.dwFormats;
DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId,
@@ -748,6 +899,8 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
PaWinMmeDeviceInfo *deviceInfoArray;
int deviceInfoInitializationSucceeded;
PaTime defaultLowLatency, defaultHighLatency;
+ DWORD waveInPreferredDevice, waveOutPreferredDevice;
+ DWORD preferredDeviceStatusFlags;
winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) );
if( !winMmeHostApi )
@@ -779,6 +932,19 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
winMmeHostApi->inputDeviceCount = 0;
winMmeHostApi->outputDeviceCount = 0;
+#if !defined(DRVM_MAPPER_PREFERRED_GET)
+/* DRVM_MAPPER_PREFERRED_GET is defined in mmddk.h but we avoid a dependency on the DDK by defining it here */
+#define DRVM_MAPPER_PREFERRED_GET (0x2000+21)
+#endif
+
+ /* the following calls assume that if wave*Message fails the preferred device parameter won't be modified */
+ preferredDeviceStatusFlags = 0;
+ waveInPreferredDevice = -1;
+ waveInMessage( (HWAVEIN)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD)&waveInPreferredDevice, (DWORD)&preferredDeviceStatusFlags );
+
+ preferredDeviceStatusFlags = 0;
+ waveOutPreferredDevice = -1;
+ waveOutMessage( (HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD)&waveOutPreferredDevice, (DWORD)&preferredDeviceStatusFlags );
maximumPossibleDeviceCount = 0;
@@ -830,7 +996,9 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
deviceInfo->hostApi = hostApiIndex;
deviceInfo->maxInputChannels = 0;
+ wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
deviceInfo->maxOutputChannels = 0;
+ wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
deviceInfo->defaultLowInputLatency = defaultLowLatency;
deviceInfo->defaultLowOutputLatency = defaultLowLatency;
@@ -843,8 +1011,14 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
goto error;
if( deviceInfoInitializationSucceeded ){
- if( (*hostApi)->info.defaultInputDevice == paNoDevice )
+ if( (*hostApi)->info.defaultInputDevice == paNoDevice ){
+ /* if there is currently no default device, use the first one available */
+ (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
+
+ }else if( winMmeDeviceId == waveInPreferredDevice ){
+ /* set the default device to the system preferred device */
(*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount;
+ }
winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
(*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
@@ -865,7 +1039,9 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
deviceInfo->hostApi = hostApiIndex;
deviceInfo->maxInputChannels = 0;
+ wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
deviceInfo->maxOutputChannels = 0;
+ wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
deviceInfo->defaultLowInputLatency = defaultLowLatency;
deviceInfo->defaultLowOutputLatency = defaultLowLatency;
@@ -878,8 +1054,14 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
goto error;
if( deviceInfoInitializationSucceeded ){
- if( (*hostApi)->info.defaultOutputDevice == paNoDevice )
+ if( (*hostApi)->info.defaultOutputDevice == paNoDevice ){
+ /* if there is currently no default device, use the first one available */
+ (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
+
+ }else if( winMmeDeviceId == waveOutPreferredDevice ){
+ /* set the default device to the system preferred device */
(*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount;
+ }
winMmeHostApi->winMmeDeviceIds[ (*hostApi)->info.deviceCount ] = winMmeDeviceId;
(*hostApi)->deviceInfos[ (*hostApi)->info.deviceCount ] = deviceInfo;
@@ -891,7 +1073,6 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
}
}
-
InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
(*hostApi)->Terminate = Terminate;
@@ -941,6 +1122,34 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
}
+static PaError IsInputChannelCountSupported( PaWinMmeDeviceInfo* deviceInfo, int channelCount )
+{
+ PaError result = paNoError;
+
+ if( channelCount > 0
+ && deviceInfo->deviceInputChannelCountIsKnown
+ && channelCount > deviceInfo->inheritedDeviceInfo.maxInputChannels ){
+
+ result = paInvalidChannelCount;
+ }
+
+ return result;
+}
+
+static PaError IsOutputChannelCountSupported( PaWinMmeDeviceInfo* deviceInfo, int channelCount )
+{
+ PaError result = paNoError;
+
+ if( channelCount > 0
+ && deviceInfo->deviceOutputChannelCountIsKnown
+ && channelCount > deviceInfo->inheritedDeviceInfo.maxOutputChannels ){
+
+ result = paInvalidChannelCount;
+ }
+
+ return result;
+}
+
static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
@@ -985,13 +1194,19 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
inputDeviceInfo = hostApi->deviceInfos[ inputStreamInfo->devices[i].device ];
/* check that input device can support inputChannelCount */
- if( inputStreamInfo->devices[i].channelCount <= 0
- || inputStreamInfo->devices[i].channelCount > inputDeviceInfo->maxInputChannels )
+ if( inputStreamInfo->devices[i].channelCount < 1 )
return paInvalidChannelCount;
+ paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo,
+ inputStreamInfo->devices[i].channelCount );
+ if( paerror != paNoError )
+ return paerror;
+
/* test for valid sample rate, see comment above */
winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputStreamInfo->devices[i].device );
- paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate );
+ paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx,
+ winMmeInputDeviceId, inputStreamInfo->devices[i].channelCount, sampleRate,
+ ((inputStreamInfo) ? inputStreamInfo->flags : 0) );
if( paerror != paNoError )
return paInvalidSampleRate;
}
@@ -1007,12 +1222,15 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
inputDeviceInfo = hostApi->deviceInfos[ inputParameters->device ];
/* check that input device can support inputChannelCount */
- if( inputChannelCount > inputDeviceInfo->maxInputChannels )
- return paInvalidChannelCount;
+ paerror = IsInputChannelCountSupported( (PaWinMmeDeviceInfo*)inputDeviceInfo, inputChannelCount );
+ if( paerror != paNoError )
+ return paerror;
/* test for valid sample rate, see comment above */
winMmeInputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, inputParameters->device );
- paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx, winMmeInputDeviceId, inputChannelCount, sampleRate );
+ paerror = QueryFormatSupported( inputDeviceInfo, QueryInputWaveFormatEx,
+ winMmeInputDeviceId, inputChannelCount, sampleRate,
+ ((inputStreamInfo) ? inputStreamInfo->flags : 0) );
if( paerror != paNoError )
return paInvalidSampleRate;
}
@@ -1040,13 +1258,19 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
outputDeviceInfo = hostApi->deviceInfos[ outputStreamInfo->devices[i].device ];
/* check that output device can support outputChannelCount */
- if( outputStreamInfo->devices[i].channelCount <= 0
- || outputStreamInfo->devices[i].channelCount > outputDeviceInfo->maxOutputChannels )
+ if( outputStreamInfo->devices[i].channelCount < 1 )
return paInvalidChannelCount;
+ paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo,
+ outputStreamInfo->devices[i].channelCount );
+ if( paerror != paNoError )
+ return paerror;
+
/* test for valid sample rate, see comment above */
winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputStreamInfo->devices[i].device );
- paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate );
+ paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx,
+ winMmeOutputDeviceId, outputStreamInfo->devices[i].channelCount, sampleRate,
+ ((outputStreamInfo) ? outputStreamInfo->flags : 0) );
if( paerror != paNoError )
return paInvalidSampleRate;
}
@@ -1062,12 +1286,15 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
outputDeviceInfo = hostApi->deviceInfos[ outputParameters->device ];
/* check that output device can support outputChannelCount */
- if( outputChannelCount > outputDeviceInfo->maxOutputChannels )
- return paInvalidChannelCount;
+ paerror = IsOutputChannelCountSupported( (PaWinMmeDeviceInfo*)outputDeviceInfo, outputChannelCount );
+ if( paerror != paNoError )
+ return paerror;
/* test for valid sample rate, see comment above */
winMmeOutputDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, outputParameters->device );
- paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx, winMmeOutputDeviceId, outputChannelCount, sampleRate );
+ paerror = QueryFormatSupported( outputDeviceInfo, QueryOutputWaveFormatEx,
+ winMmeOutputDeviceId, outputChannelCount, sampleRate,
+ ((outputStreamInfo) ? outputStreamInfo->flags : 0) );
if( paerror != paNoError )
return paInvalidSampleRate;
}
@@ -1491,9 +1718,10 @@ typedef struct
static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers );
static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+ unsigned long winMmeSpecificFlags,
unsigned long bytesPerHostSample,
double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
- unsigned int deviceCount, int isInput );
+ unsigned int deviceCount, PaWinWaveFormatChannelMask channelMask, int isInput );
static PaError TerminateWaveHandles( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers, int isInput, int currentlyProcessingAnError );
static PaError InitializeWaveHeaders( PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
unsigned long hostBufferCount,
@@ -1515,15 +1743,16 @@ static void InitializeSingleDirectionHandlesAndBuffers( PaWinMmeSingleDirectionH
static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostApi,
PaWinMmeSingleDirectionHandlesAndBuffers *handlesAndBuffers,
+ unsigned long winMmeSpecificFlags,
unsigned long bytesPerHostSample,
double sampleRate, PaWinMmeDeviceAndChannelCount *devices,
- unsigned int deviceCount, int isInput )
+ unsigned int deviceCount, PaWinWaveFormatChannelMask channelMask, int isInput )
{
PaError result;
MMRESULT mmresult;
- unsigned long bytesPerFrame;
- WAVEFORMATEX wfx;
- signed int i;
+ signed int i, j;
+ PaSampleFormat sampleFormat;
+ int waveFormatTag;
/* for error cleanup we expect that InitializeSingleDirectionHandlesAndBuffers()
has already been called to zero some fields */
@@ -1551,64 +1780,98 @@ static PaError InitializeWaveHandles( PaWinMmeHostApiRepresentation *winMmeHostA
((HWAVEOUT*)handlesAndBuffers->waveHandles)[i] = 0;
}
- wfx.wFormatTag = WAVE_FORMAT_PCM;
- wfx.nSamplesPerSec = (DWORD) sampleRate;
- wfx.cbSize = 0;
-
+ /* @todo at the moment we only use 16 bit sample format */
+ sampleFormat = paInt16;
+ waveFormatTag = SampleFormatAndWinWmmeSpecificFlagsToLinearWaveFormatTag( sampleFormat, winMmeSpecificFlags );
+
for( i = 0; i < (signed int)deviceCount; ++i )
{
- UINT winMmeDeviceId;
+ PaWinWaveFormat waveFormat;
+ UINT winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
+
+ /* @todo: consider providing a flag or #define to not try waveformat extensible
+ this could just initialize j to 1 the first time round. */
- winMmeDeviceId = LocalDeviceIndexToWinMmeDeviceId( winMmeHostApi, devices[i].device );
- wfx.nChannels = (WORD)devices[i].channelCount;
+ for( j = 0; j < 2; ++j )
+ {
+ if( j == 0 )
+ {
+ /* first, attempt to open the device using WAVEFORMATEXTENSIBLE,
+ if this fails we fall back to WAVEFORMATEX */
- bytesPerFrame = wfx.nChannels * bytesPerHostSample;
+ PaWin_InitializeWaveFormatExtensible( &waveFormat, devices[i].channelCount,
+ sampleFormat, waveFormatTag, sampleRate, channelMask );
- wfx.nAvgBytesPerSec = (DWORD)(bytesPerFrame * sampleRate);
- wfx.nBlockAlign = (WORD)bytesPerFrame;
- wfx.wBitsPerSample = (WORD)((bytesPerFrame/wfx.nChannels) * 8);
+ }
+ else
+ {
+ /* retry with WAVEFORMATEX */
- /* REVIEW: consider not firing an event for input when a full duplex
- stream is being used. this would probably depend on the
- neverDropInput flag. */
+ PaWin_InitializeWaveFormatEx( &waveFormat, devices[i].channelCount,
+ sampleFormat, waveFormatTag, sampleRate );
+ }
- if( isInput )
- mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
+ /* REVIEW: consider not firing an event for input when a full duplex
+ stream is being used. this would probably depend on the
+ neverDropInput flag. */
+
+ if( isInput )
+ {
+ mmresult = waveInOpen( &((HWAVEIN*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId,
+ (WAVEFORMATEX*)&waveFormat,
(DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
- else
- mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId, &wfx,
+ }
+ else
+ {
+ mmresult = waveOutOpen( &((HWAVEOUT*)handlesAndBuffers->waveHandles)[i], winMmeDeviceId,
+ (WAVEFORMATEX*)&waveFormat,
(DWORD_PTR)handlesAndBuffers->bufferEvent, (DWORD_PTR)0, CALLBACK_EVENT );
+ }
- if( mmresult != MMSYSERR_NOERROR )
- {
- switch( mmresult )
+ if( mmresult == MMSYSERR_NOERROR )
{
- case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
- result = paDeviceUnavailable;
- break;
- case MMSYSERR_NODRIVER: /* No device driver is present. */
- result = paDeviceUnavailable;
- break;
- case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
- result = paInsufficientMemory;
- break;
+ break; /* success */
+ }
+ else if( j == 0 )
+ {
+ continue; /* try again with WAVEFORMATEX */
+ }
+ else
+ {
+ switch( mmresult )
+ {
+ case MMSYSERR_ALLOCATED: /* Specified resource is already allocated. */
+ result = paDeviceUnavailable;
+ break;
+ case MMSYSERR_NODRIVER: /* No device driver is present. */
+ result = paDeviceUnavailable;
+ break;
+ case MMSYSERR_NOMEM: /* Unable to allocate or lock memory. */
+ result = paInsufficientMemory;
+ break;
- case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
- /* falls through */
- case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
- /* falls through */
- default:
- result = paUnanticipatedHostError;
- if( isInput )
- {
- PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
- }
- else
- {
- PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
- }
+ case MMSYSERR_BADDEVICEID: /* Specified device identifier is out of range. */
+ /* falls through */
+
+ case WAVERR_BADFORMAT: /* Attempted to open with an unsupported waveform-audio format. */
+ /* This can also occur if we try to open the device with an unsupported
+ * number of channels. This is attempted when device*ChannelCountIsKnown is
+ * set to 0.
+ */
+ /* falls through */
+ default:
+ result = paUnanticipatedHostError;
+ if( isInput )
+ {
+ PA_MME_SET_LAST_WAVEIN_ERROR( mmresult );
+ }
+ else
+ {
+ PA_MME_SET_LAST_WAVEOUT_ERROR( mmresult );
+ }
+ }
+ goto error;
}
- goto error;
}
}
@@ -1850,6 +2113,7 @@ struct PaWinMmeStream
static PaError ValidateWinMmeSpecificStreamInfo(
const PaStreamParameters *streamParameters,
const PaWinMmeStreamInfo *streamInfo,
+ unsigned long *winMmeSpecificFlags,
char *throttleProcessingThreadOnOverload,
unsigned long *deviceCount )
{
@@ -1861,6 +2125,8 @@ static PaError ValidateWinMmeSpecificStreamInfo(
return paIncompatibleHostApiSpecificStreamInfo;
}
+ *winMmeSpecificFlags = streamInfo->flags;
+
if( streamInfo->flags & paWinMmeDontThrottleOverloadedProcessingThread )
*throttleProcessingThreadOnOverload = 0;
@@ -1926,12 +2192,20 @@ static PaError ValidateInputChannelCounts(
unsigned long deviceCount )
{
unsigned int i;
+ PaWinMmeDeviceInfo *inputDeviceInfo;
+ PaError paerror;
for( i=0; i < deviceCount; ++i )
{
- if( devices[i].channelCount < 1 || devices[i].channelCount
- > hostApi->deviceInfos[ devices[i].device ]->maxInputChannels )
+ if( devices[i].channelCount < 1 )
return paInvalidChannelCount;
+
+ inputDeviceInfo =
+ (PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ];
+
+ paerror = IsInputChannelCountSupported( inputDeviceInfo, devices[i].channelCount );
+ if( paerror != paNoError )
+ return paerror;
}
return paNoError;
@@ -1943,12 +2217,20 @@ static PaError ValidateOutputChannelCounts(
unsigned long deviceCount )
{
unsigned int i;
+ PaWinMmeDeviceInfo *outputDeviceInfo;
+ PaError paerror;
for( i=0; i < deviceCount; ++i )
{
- if( devices[i].channelCount < 1 || devices[i].channelCount
- > hostApi->deviceInfos[ devices[i].device ]->maxOutputChannels )
+ if( devices[i].channelCount < 1 )
return paInvalidChannelCount;
+
+ outputDeviceInfo =
+ (PaWinMmeDeviceInfo*)hostApi->deviceInfos[ devices[i].device ];
+
+ paerror = IsOutputChannelCountSupported( outputDeviceInfo, devices[i].channelCount );
+ if( paerror != paNoError )
+ return paerror;
}
return paNoError;
@@ -1981,14 +2263,17 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
PaSampleFormat inputSampleFormat, outputSampleFormat;
double suggestedInputLatency, suggestedOutputLatency;
PaWinMmeStreamInfo *inputStreamInfo, *outputStreamInfo;
+ PaWinWaveFormatChannelMask inputChannelMask, outputChannelMask;
unsigned long framesPerHostInputBuffer;
unsigned long hostInputBufferCount;
unsigned long framesPerHostOutputBuffer;
unsigned long hostOutputBufferCount;
unsigned long framesPerBufferProcessorCall;
PaWinMmeDeviceAndChannelCount *inputDevices = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
+ unsigned long winMmeSpecificInputFlags = 0;
unsigned long inputDeviceCount = 0;
PaWinMmeDeviceAndChannelCount *outputDevices = 0;
+ unsigned long winMmeSpecificOutputFlags = 0;
unsigned long outputDeviceCount = 0; /* contains all devices and channel counts as local host api ids, even when PaWinMmeUseMultipleDevices is not used */
char throttleProcessingThreadOnOverload = 1;
@@ -2004,6 +2289,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* validate input hostApiSpecificStreamInfo */
inputStreamInfo = (PaWinMmeStreamInfo*)inputParameters->hostApiSpecificStreamInfo;
result = ValidateWinMmeSpecificStreamInfo( inputParameters, inputStreamInfo,
+ &winMmeSpecificInputFlags,
&throttleProcessingThreadOnOverload,
&inputDeviceCount );
if( result != paNoError ) return result;
@@ -2019,6 +2305,18 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
hostInputSampleFormat =
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, inputSampleFormat );
+
+ if( inputDeviceCount != 1 ){
+ /* always use direct speakers when using multi-device multichannel mode */
+ inputChannelMask = PAWIN_SPEAKER_DIRECTOUT;
+ }
+ else
+ {
+ if( inputStreamInfo && inputStreamInfo->flags & paWinMmeUseChannelMask )
+ inputChannelMask = inputStreamInfo->channelMask;
+ else
+ inputChannelMask = PaWin_DefaultChannelMask( inputDevices[0].channelCount );
+ }
}
else
{
@@ -2041,6 +2339,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
/* validate output hostApiSpecificStreamInfo */
outputStreamInfo = (PaWinMmeStreamInfo*)outputParameters->hostApiSpecificStreamInfo;
result = ValidateWinMmeSpecificStreamInfo( outputParameters, outputStreamInfo,
+ &winMmeSpecificOutputFlags,
&throttleProcessingThreadOnOverload,
&outputDeviceCount );
if( result != paNoError ) return result;
@@ -2056,6 +2355,18 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
hostOutputSampleFormat =
PaUtil_SelectClosestAvailableFormat( paInt16 /* native formats */, outputSampleFormat );
+
+ if( outputDeviceCount != 1 ){
+ /* always use direct speakers when using multi-device multichannel mode */
+ outputChannelMask = PAWIN_SPEAKER_DIRECTOUT;
+ }
+ else
+ {
+ if( outputStreamInfo && outputStreamInfo->flags & paWinMmeUseChannelMask )
+ outputChannelMask = outputStreamInfo->channelMask;
+ else
+ outputChannelMask = PaWin_DefaultChannelMask( outputDevices[0].channelCount );
+ }
}
else
{
@@ -2078,6 +2389,14 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
return paInvalidFlag; /* unexpected platform specific flag */
+ /* always disable clipping and dithering if we are outputting a raw spdif stream */
+ if( (winMmeSpecificOutputFlags & paWinMmeWaveFormatDolbyAc3Spdif)
+ || (winMmeSpecificOutputFlags & paWinMmeWaveFormatWmaSpdif) ){
+
+ streamFlags = streamFlags | paClipOff | paDitherOff;
+ }
+
+
result = CalculateBufferSettings( &framesPerHostInputBuffer, &hostInputBufferCount,
&framesPerHostOutputBuffer, &hostOutputBufferCount,
inputChannelCount, hostInputSampleFormat, suggestedInputLatency, inputStreamInfo,
@@ -2176,16 +2495,18 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi,
if( inputParameters )
{
result = InitializeWaveHandles( winMmeHostApi, &stream->input,
+ winMmeSpecificInputFlags,
stream->bufferProcessor.bytesPerHostInputSample, sampleRate,
- inputDevices, inputDeviceCount, 1 /* isInput */ );
+ inputDevices, inputDeviceCount, inputChannelMask, 1 /* isInput */ );
if( result != paNoError ) goto error;
}
if( outputParameters )
{
result = InitializeWaveHandles( winMmeHostApi, &stream->output,
+ winMmeSpecificOutputFlags,
stream->bufferProcessor.bytesPerHostOutputSample, sampleRate,
- outputDevices, outputDeviceCount, 0 /* isInput */ );
+ outputDevices, outputDeviceCount, outputChannelMask, 0 /* isInput */ );
if( result != paNoError ) goto error;
}
@@ -2340,6 +2661,7 @@ static PaError AdvanceToNextInputBuffer( PaWinMmeStream *stream )
for( i=0; i < stream->input.deviceCount; ++i )
{
+ stream->input.waveHeaders[i][ stream->input.currentBufferIndex ].dwFlags &= ~WHDR_DONE;
mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[i],
&stream->input.waveHeaders[i][ stream->input.currentBufferIndex ],
sizeof(WAVEHDR) );
@@ -2642,7 +2964,7 @@ static DWORD WINAPI ProcessingThreadProc( void *pArg )
for( i=0; i<stream->input.deviceCount; ++i )
{
/* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
+ int channelCount = (int)stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
@@ -2663,7 +2985,7 @@ static DWORD WINAPI ProcessingThreadProc( void *pArg )
for( i=0; i<stream->output.deviceCount; ++i )
{
/* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
+ int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
@@ -2866,6 +3188,7 @@ static PaError StartStream( PaStream *s )
{
for( j=0; j<stream->input.deviceCount; ++j )
{
+ stream->input.waveHeaders[j][i].dwFlags &= ~WHDR_DONE;
mmresult = waveInAddBuffer( ((HWAVEIN*)stream->input.waveHandles)[j], &stream->input.waveHeaders[j][i], sizeof(WAVEHDR) );
if( mmresult != MMSYSERR_NOERROR )
{
@@ -2912,7 +3235,7 @@ static PaError StartStream( PaStream *s )
for( j=0; j<stream->output.deviceCount; ++j )
{
/* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->output.waveHeaders[j][i].dwUser;
+ int channelCount = (int)stream->output.waveHeaders[j][i].dwUser;
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
stream->output.waveHeaders[j][i].lpData +
@@ -2988,7 +3311,7 @@ static PaError StartStream( PaStream *s )
if( result != paNoError ) goto error;
/* Create thread that waits for audio buffers to be ready for processing. */
- stream->processingThread = CreateThread( 0, 0, ProcessingThreadProc, stream, 0, &stream->processingThreadId );
+ stream->processingThread = CREATE_THREAD;
if( !stream->processingThread )
{
result = paUnanticipatedHostError;
@@ -3120,7 +3443,7 @@ static PaError StopStream( PaStream *s )
for( i=0; i<stream->output.deviceCount; ++i )
{
/* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
+ int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +
@@ -3344,7 +3667,7 @@ static PaError ReadStream( PaStream* s,
}
else
{
- userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount );
+ userBuffer = (void*)alloca( sizeof(void*) * stream->bufferProcessor.inputChannelCount );
if( !userBuffer )
return paInsufficientMemory;
for( i = 0; i<stream->bufferProcessor.inputChannelCount; ++i )
@@ -3372,7 +3695,7 @@ static PaError ReadStream( PaStream* s,
for( i=0; i<stream->input.deviceCount; ++i )
{
/* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
+ int channelCount = (int)stream->input.waveHeaders[i][ hostInputBufferIndex ].dwUser;
PaUtil_SetInterleavedInputChannels( &stream->bufferProcessor, channel,
stream->input.waveHeaders[i][ hostInputBufferIndex ].lpData +
@@ -3448,7 +3771,7 @@ static PaError WriteStream( PaStream* s,
}
else
{
- userBuffer = alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount );
+ userBuffer = (const void*)alloca( sizeof(void*) * stream->bufferProcessor.outputChannelCount );
if( !userBuffer )
return paInsufficientMemory;
for( i = 0; i<stream->bufferProcessor.outputChannelCount; ++i )
@@ -3477,7 +3800,7 @@ static PaError WriteStream( PaStream* s,
for( i=0; i<stream->output.deviceCount; ++i )
{
/* we have stored the number of channels in the buffer in dwUser */
- int channelCount = stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
+ int channelCount = (int)stream->output.waveHeaders[i][ hostOutputBufferIndex ].dwUser;
PaUtil_SetInterleavedOutputChannels( &stream->bufferProcessor, channel,
stream->output.waveHeaders[i][ hostOutputBufferIndex ].lpData +