fixes for hardware samplerate changes

This commit is contained in:
bjornroche 2009-11-30 21:07:43 +00:00
commit 8901efea70
3 changed files with 47 additions and 67 deletions

View file

@ -84,6 +84,7 @@ TESTS = \
bin/patest_sine_channelmaps \ bin/patest_sine_channelmaps \
bin/patest_sine_formats \ bin/patest_sine_formats \
bin/patest_sine_time \ bin/patest_sine_time \
bin/patest_sine_srate \
bin/patest_start_stop \ bin/patest_start_stop \
bin/patest_stop \ bin/patest_stop \
bin/patest_stop_playout \ bin/patest_stop_playout \

View file

@ -61,6 +61,7 @@
#include <libkern/OSAtomic.h> #include <libkern/OSAtomic.h>
#include <strings.h> #include <strings.h>
#include <pthread.h> #include <pthread.h>
#include <sys/time.h>
PaError PaMacCore_SetUnixError( int err, int line ) PaError PaMacCore_SetUnixError( int err, int line )
{ {
@ -305,8 +306,13 @@ long computeRingBufferSize( const PaStreamParameters *inputParameters,
/* /*
* Durring testing of core audio, I found that serious crashes could occur * Durring testing of core audio, I found that serious crashes could occur
* if properties such as sample rate were changed multiple times in rapid * if properties such as sample rate were changed multiple times in rapid
* succession. The function below has some fancy logic to make sure that changes * succession. The function below could be used to with a condition variable.
* are acknowledged before another is requested. That seems to help a lot. * to prevent propertychanges from happening until the last property
* change is acknowledged. Instead, I implemented a busy-wait, which is simpler
* to implement b/c in second round of testing (nov '09) property changes occured
* quickly and so there was no real way to test the condition variable implementation.
* therefore, this function is not used, but it is aluded to in commented code below,
* since it represents a theoretically better implementation.
*/ */
OSStatus propertyProc( OSStatus propertyProc(
@ -316,9 +322,7 @@ OSStatus propertyProc(
AudioDevicePropertyID inPropertyID, AudioDevicePropertyID inPropertyID,
void* inClientData ) void* inClientData )
{ {
MutexAndBool *mab = (MutexAndBool *) inClientData; // this is where we would set the condition variable
mab->once = TRUE;
pthread_mutex_unlock( &(mab->mutex) );
return noErr; return noErr;
} }
@ -337,8 +341,6 @@ PaError AudioDeviceSetPropertyNowAndWaitForChange(
void *outPropertyData ) void *outPropertyData )
{ {
OSStatus macErr; OSStatus macErr;
int unixErr;
MutexAndBool mab;
UInt32 outPropertyDataSize = inPropertyDataSize; UInt32 outPropertyDataSize = inPropertyDataSize;
/* First, see if it already has that value. If so, return. */ /* First, see if it already has that value. If so, return. */
@ -346,74 +348,63 @@ PaError AudioDeviceSetPropertyNowAndWaitForChange(
isInput, inPropertyID, isInput, inPropertyID,
&outPropertyDataSize, outPropertyData ); &outPropertyDataSize, outPropertyData );
if( macErr ) if( macErr )
goto failMac2; goto failMac;
if( inPropertyDataSize!=outPropertyDataSize ) if( inPropertyDataSize!=outPropertyDataSize )
return paInternalError; return paInternalError;
if( 0==memcmp( outPropertyData, inPropertyData, outPropertyDataSize ) ) if( 0==memcmp( outPropertyData, inPropertyData, outPropertyDataSize ) )
return paNoError; return paNoError;
/* setup and lock mutex */ /* Ideally, we'd use a condition variable to determine changes.
mab.once = FALSE; we could set that up here. */
unixErr = pthread_mutex_init( &mab.mutex, NULL );
if( unixErr ) /* If we were using a cond variable, we'd add a property listener here.
goto failUnix2; No more notes on that, but don't forget to remove the listener as well! */
unixErr = pthread_mutex_lock( &mab.mutex ); //macErr = AudioDeviceAddPropertyListener( inDevice, inChannel, isInput,
if( unixErr ) // inPropertyID, propertyProc,
goto failUnix; // NULL );
//if( macErr )
// /* we couldn't add a listener. */
// goto failMac;
/* add property listener */
macErr = AudioDeviceAddPropertyListener( inDevice, inChannel, isInput,
inPropertyID, propertyProc,
&mab );
if( macErr )
goto failMac;
/* set property */ /* set property */
macErr = AudioDeviceSetProperty( inDevice, NULL, inChannel, macErr = AudioDeviceSetProperty( inDevice, NULL, inChannel,
isInput, inPropertyID, isInput, inPropertyID,
inPropertyDataSize, inPropertyData ); inPropertyDataSize, inPropertyData );
if( macErr ) { if( macErr )
/* we couldn't set the property, so we'll just unlock the mutex goto failMac;
and move on. */
pthread_mutex_unlock( &mab.mutex );
}
/* wait for property to change */ /* busy-wait up to 30 seconds for the property to change */
unixErr = pthread_mutex_lock( &mab.mutex ); /* busy-wait is justified here only because the correct alternative (condition variable)
if( unixErr ) was hard to test, since most of the waiting ended up being for setting rather than
goto failUnix; getting in OS X 10.5. This was not the case in earlier OS versions. */
struct timeval tv1, tv2;
/* now read the property back out */ gettimeofday( &tv1, NULL );
memcpy( &tv2, &tv1, sizeof( struct timeval ) );
while( tv2.tv_sec - tv1.tv_sec < 30 ) {
macErr = AudioDeviceGetProperty( inDevice, inChannel, macErr = AudioDeviceGetProperty( inDevice, inChannel,
isInput, inPropertyID, isInput, inPropertyID,
&outPropertyDataSize, outPropertyData ); &outPropertyDataSize, outPropertyData );
if( macErr ) if( macErr )
goto failMac; goto failMac;
/* cleanup */ if( 0==memcmp( outPropertyData, inPropertyData, outPropertyDataSize ) ) {
AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput,
inPropertyID, propertyProc );
unixErr = pthread_mutex_unlock( &mab.mutex );
if( unixErr )
goto failUnix2;
unixErr = pthread_mutex_destroy( &mab.mutex );
if( unixErr )
goto failUnix2;
return paNoError; return paNoError;
}
Pa_Sleep( 100 );
gettimeofday( &tv2, NULL );
}
DBUG( ("Timeout waiting for device setting." ) );
failUnix:
pthread_mutex_destroy( &mab.mutex );
AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput,
inPropertyID, propertyProc );
failUnix2: ///* now read the property back out */
DBUG( ("Error #%d while setting a device property: %s\n", unixErr, strerror( unixErr ) ) ); macErr = AudioDeviceGetProperty( inDevice, inChannel,
isInput, inPropertyID,
&outPropertyDataSize, outPropertyData );
if( macErr )
goto failMac;
return paUnanticipatedHostError; return paUnanticipatedHostError;
failMac: failMac:
pthread_mutex_destroy( &mab.mutex );
AudioDeviceRemovePropertyListener( inDevice, inChannel, isInput,
inPropertyID, propertyProc );
failMac2:
return ERR( macErr ); return ERR( macErr );
} }

View file

@ -143,18 +143,6 @@ long computeRingBufferSize( const PaStreamParameters *inputParameters,
long outputFramesPerBuffer, long outputFramesPerBuffer,
double sampleRate ); double sampleRate );
/*
* Durring testing of core audio, I found that serious crashes could occur
* if properties such as sample rate were changed multiple times in rapid
* succession. The function below has some fancy logic to make sure that changes
* are acknowledged before another is requested. That seems to help a lot.
*/
typedef struct {
bool once; /* I didn't end up using this. bdr */
pthread_mutex_t mutex;
} MutexAndBool ;
OSStatus propertyProc( OSStatus propertyProc(
AudioDeviceID inDevice, AudioDeviceID inDevice,
UInt32 inChannel, UInt32 inChannel,