From b77a1f828c5cd683b7f10e419b815f31bbefeba6 Mon Sep 17 00:00:00 2001 From: Ross Bencina Date: Sat, 3 Sep 2016 22:29:58 +1000 Subject: [PATCH] Adds portaudio sources [adds hotplug support for pa_linux_alsa, pa_mac_core, plus some other tweaks] damencho committed on Mar 29, 2012 d2076b1 from https://github.com/jitsi/libsrc/commits/master/portaudio.zip --- Makefile.in | 2 + configure | Bin 411364 -> 411460 bytes configure.in | 8 +- src/common/pa_front.c | 3 +- src/hostapi/alsa/pa_linux_alsa.c | 198 +++++++++++-- src/hostapi/asio/pa_asio.cpp | 5 +- src/hostapi/coreaudio/pa_mac_core.c | 314 +++++++++++++++------ src/hostapi/jack/pa_jack.c | 7 +- src/hostapi/oss/pa_unix_oss.c | 5 +- src/hostapi/skeleton/pa_hostapi_skeleton.c | 8 +- src/hostapi/wasapi/pa_win_wasapi.c | 3 + src/hostapi/wdmks/pa_win_wdmks.c | 7 +- src/os/mac_osx/pa_osx_hotplug.c | 76 +++++ src/os/unix/pa_linux_hotplug.c | 147 ++++++++++ src/os/win/pa_win_hostapis.c | 4 +- src/os/win/pa_win_hotplug.c | 3 + test/patest_update_available_device_list.c | 6 +- 17 files changed, 671 insertions(+), 125 deletions(-) create mode 100644 src/os/mac_osx/pa_osx_hotplug.c create mode 100644 src/os/unix/pa_linux_hotplug.c diff --git a/Makefile.in b/Makefile.in index 3f3fb4a..26273cd 100644 --- a/Makefile.in +++ b/Makefile.in @@ -107,6 +107,7 @@ TESTS = \ bin/patest_wire \ bin/patest_write_sine \ bin/patest_write_sine_nonint \ + bin/patest_update_available_device_list \ bin/pa_devs \ bin/pa_fuzz \ bin/pa_minlat @@ -146,6 +147,7 @@ SRC_DIRS = \ src/hostapi/wdmks \ src/hostapi/wmme \ src/os/unix \ + src/os/mac_osx \ src/os/win SUBDIRS = diff --git a/configure b/configure index 0af577567b6424b3f8efec7d6dc6bce96a3d2844..18640d05ea068a1da5ed077f41a8db3cb2476ac8 100755 GIT binary patch delta 136 zcmaFTD|w_(vY~~sg{g(Pg=GutHEu-(h2o-Q{rqD6+{EPg{Nf7zfL2-Cn8BmMsndS{E@B delta 61 zcmX@oC;6mTvY~~sg{g(Pg=GutHSXyOhOAuEqfA&uwu|zxeq)<{Uw~C{yN@udmkdZs PrTwNp>-L-aY}w)fcZ(BI diff --git a/configure.in b/configure.in index 0c2e67a..0749eb0 100644 --- a/configure.in +++ b/configure.in @@ -216,7 +216,7 @@ case "${host_os}" in fi SHARED_FLAGS="$LIBS -dynamiclib $mac_arches $mac_sysroot $mac_version_min" CFLAGS="-std=c99 $CFLAGS $mac_arches $mac_sysroot $mac_version_min" - OTHER_OBJS="src/os/unix/pa_unix_hostapis.o src/os/unix/pa_unix_util.o src/hostapi/coreaudio/pa_mac_core.o src/hostapi/coreaudio/pa_mac_core_utilities.o src/hostapi/coreaudio/pa_mac_core_blocking.o src/common/pa_ringbuffer.o" + OTHER_OBJS="src/os/unix/pa_unix_hostapis.o src/os/unix/pa_unix_util.o src/hostapi/coreaudio/pa_mac_core.o src/hostapi/coreaudio/pa_mac_core_utilities.o src/hostapi/coreaudio/pa_mac_core_blocking.o src/common/pa_ringbuffer.o src/os/mac_osx/pa_osx_hotplug.o" PADLL="libportaudio.dylib" ;; @@ -230,7 +230,7 @@ case "${host_os}" in if [[ "x$with_directx" = "xyes" ]]; then DXDIR="$with_dxdir" - add_objects src/hostapi/dsound/pa_win_ds.o src/hostapi/dsound/pa_win_ds_dynlink.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o src/os/win/pa_win_waveformat.o + add_objects src/hostapi/dsound/pa_win_ds.o src/hostapi/dsound/pa_win_ds_dynlink.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o src/os/win/pa_win_waveformat.o src/os/win/pa_win_hotplug.o LIBS="-lwinmm -lm -ldsound -lole32" DLL_LIBS="${DLL_LIBS} -lwinmm -lm -L$DXDIR/lib -ldsound -lole32" #VC98="\"/c/Program Files/Microsoft Visual Studio/VC98/Include\"" @@ -249,7 +249,7 @@ case "${host_os}" in if [[ "x$with_wdmks" = "xyes" ]]; then DXDIR="$with_dxdir" - add_objects src/hostapi/wdmks/pa_win_wdmks.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o + add_objects src/hostapi/wdmks/pa_win_wdmks.o src/os/win/pa_win_hostapis.o src/os/win/pa_win_util.o src/os/win/pa_win_wdmks_utils.o LIBS="-lwinmm -lm -luuid -lsetupapi -lole32" DLL_LIBS="${DLL_LIBS} -lwinmm -lm -L$DXDIR/lib -luuid -lsetupapi -lole32" #VC98="\"/c/Program Files/Microsoft Visual Studio/VC98/Include\"" @@ -319,7 +319,7 @@ case "${host_os}" in if [[ "$have_alsa" = "yes" ] && [ "$with_alsa" != "no" ]] ; then DLL_LIBS="$DLL_LIBS -lasound" LIBS="$LIBS -lasound" - OTHER_OBJS="$OTHER_OBJS src/hostapi/alsa/pa_linux_alsa.o" + OTHER_OBJS="$OTHER_OBJS src/hostapi/alsa/pa_linux_alsa.o src/os/unix/pa_linux_hotplug.o" INCLUDES="$INCLUDES pa_linux_alsa.h" AC_DEFINE(PA_USE_ALSA) fi diff --git a/src/common/pa_front.c b/src/common/pa_front.c index 13dbebf..5c6aae0 100644 --- a/src/common/pa_front.c +++ b/src/common/pa_front.c @@ -737,13 +737,12 @@ PaError Pa_UpdateAvailableDeviceList( void ) for( i = 0 ; i < paInternalInfo_.hostApisCount_ ; ++i ) { PaUtilHostApiRepresentation *hostApi = paInternalInfo_.hostApis_[i]; + PA_DEBUG(( "Scanning new device list for host api %d.\n",i)); if( hostApi->ScanDeviceInfos == NULL ) continue; - PA_DEBUG(( "Scanning new device list for host api %d.\n",i)); if( hostApi->ScanDeviceInfos( hostApi, i, &scanResults[ i ], &deviceCounts[ i ] ) != paNoError ) break; - } /* Check the result of the scan operation */ diff --git a/src/hostapi/alsa/pa_linux_alsa.c b/src/hostapi/alsa/pa_linux_alsa.c index eee10d8..4680652 100644 --- a/src/hostapi/alsa/pa_linux_alsa.c +++ b/src/hostapi/alsa/pa_linux_alsa.c @@ -668,6 +668,15 @@ typedef struct PaAlsaDeviceInfo } PaAlsaDeviceInfo; +/* used for tranferring device infos during scanning / rescanning */ +typedef struct PaLinuxScanDeviceInfosResults +{ + PaDeviceInfo **deviceInfos; + PaDeviceIndex defaultInputDevice; + PaDeviceIndex defaultOutputDevice; + int deviceCount; +} PaLinuxScanDeviceInfosResults; + /* prototypes for functions declared in this file */ static void Terminate( struct PaUtilHostApiRepresentation *hostApi ); @@ -692,10 +701,17 @@ static PaError IsStreamStopped( PaStream *s ); static PaError IsStreamActive( PaStream *stream ); static PaTime GetStreamTime( PaStream *stream ); static double GetStreamCpuLoad( PaStream* stream ); -static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi ); +static PaError BuildDeviceList( PaAlsaHostApiRepresentation *hostApi, void** scanResults, int* deviceCount ); static int SetApproximateSampleRate( snd_pcm_t *pcm, snd_pcm_hw_params_t *hwParams, double sampleRate ); static int GetExactSampleRate( snd_pcm_hw_params_t *hwParams, double *sampleRate ); +static PaError ScanDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, + void **newDeviceInfos, int *newDeviceCount ); +static PaError CommitDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, + void *deviceInfos, int deviceCount); +static PaError DisposeDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, void *deviceInfos, + int deviceCount ); + /* Callback prototypes */ static void *CallbackThreadFunc( void *userData ); @@ -722,6 +738,8 @@ PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex { PaError result = paNoError; PaAlsaHostApiRepresentation *alsaHostApi = NULL; + void* scanResults = NULL; + int deviceCount = 0; /* Try loading Alsa library. */ if (!PaAlsa_LoadLibrary()) @@ -733,20 +751,29 @@ PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex alsaHostApi->hostApiIndex = hostApiIndex; *hostApi = (PaUtilHostApiRepresentation*)alsaHostApi; + (*hostApi)->deviceInfos = NULL; (*hostApi)->info.structVersion = 1; (*hostApi)->info.type = paALSA; (*hostApi)->info.name = "ALSA"; + (*hostApi)->ScanDeviceInfos = NULL; + (*hostApi)->CommitDeviceInfos = NULL; + (*hostApi)->DisposeDeviceInfos = NULL; (*hostApi)->Terminate = Terminate; (*hostApi)->OpenStream = OpenStream; (*hostApi)->IsFormatSupported = IsFormatSupported; + (*hostApi)->ScanDeviceInfos = ScanDeviceInfos; + (*hostApi)->CommitDeviceInfos = CommitDeviceInfos; + (*hostApi)->DisposeDeviceInfos = DisposeDeviceInfos; /** If AlsaErrorHandler is to be used, do not forget to unregister callback pointer in Terminate function. */ /*ENSURE_( snd_lib_error_set_handler(AlsaErrorHandler), paUnanticipatedHostError );*/ - PA_ENSURE( BuildDeviceList( alsaHostApi ) ); + ScanDeviceInfos(&alsaHostApi->baseHostApiRep, hostApiIndex, &scanResults, + &deviceCount); + CommitDeviceInfos(&alsaHostApi->baseHostApiRep, hostApiIndex, scanResults, deviceCount); PaUtil_InitializeStreamInterface( &alsaHostApi->callbackStreamInterface, CloseStream, StartStream, @@ -802,7 +829,11 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi ) } PaUtil_FreeMemory( alsaHostApi ); - alsa_snd_config_update_free_global(); +// damencho, removed fo compability with pulseaudio versions before 0.9.16 +// segfault application: +// bugtrack alsa: 0002124: snd_config_update_free_global kills applications using user space alsa plugins +// snd_config_update_free_global(); +// alsa_snd_config_update_free_global(); /* Close Alsa library. */ PaAlsa_CloseLibrary(); @@ -1075,7 +1106,7 @@ static int OpenPcm( snd_pcm_t **pcmp, const char *name, snd_pcm_stream_t stream, } static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* deviceName, int blocking, - PaAlsaDeviceInfo* devInfo, int* devIdx ) + PaAlsaDeviceInfo* devInfo, int* devIdx, PaLinuxScanDeviceInfosResults* out ) { PaError result = 0; PaDeviceInfo *baseDeviceInfo = &devInfo->baseDeviceInfo; @@ -1128,20 +1159,20 @@ 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 || !strcmp(deviceName->alsaName, + if( (out->defaultInputDevice == paNoDevice || !strcmp(deviceName->alsaName, "default" )) && baseDeviceInfo->maxInputChannels > 0 ) { - baseApi->info.defaultInputDevice = *devIdx; + out->defaultInputDevice = *devIdx; PA_DEBUG(("Default input device: %s\n", deviceName->name)); } - if( (baseApi->info.defaultOutputDevice == paNoDevice || !strcmp(deviceName->alsaName, + if( (out->defaultOutputDevice == paNoDevice || !strcmp(deviceName->alsaName, "default" )) && baseDeviceInfo->maxOutputChannels > 0 ) { - baseApi->info.defaultOutputDevice = *devIdx; + out->defaultOutputDevice = *devIdx; PA_DEBUG(("Default output device: %s\n", deviceName->name)); } PA_DEBUG(("%s: Adding device %s: %d\n", __FUNCTION__, deviceName->name, *devIdx)); - baseApi->deviceInfos[*devIdx] = (PaDeviceInfo *) devInfo; + out->deviceInfos[*devIdx] = (PaDeviceInfo *) devInfo; (*devIdx) += 1; } else @@ -1154,9 +1185,10 @@ end: } /* Build PaDeviceInfo list, ignore devices for which we cannot determine capabilities (possibly busy, sigh) */ -static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) +static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi, void** scanResults, int* count) { PaUtilHostApiRepresentation *baseApi = &alsaApi->baseHostApiRep; + PaLinuxScanDeviceInfosResults *outArgument = NULL; PaAlsaDeviceInfo *deviceInfoArray; int cardIdx = -1, devIdx = 0; snd_ctl_card_info_t *cardInfo; @@ -1171,13 +1203,11 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) #ifdef PA_ENABLE_DEBUG_OUTPUT PaTime startTime = PaUtil_GetTime(); #endif + PaLinuxScanDeviceInfosResults* out = NULL; if( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) && atoi( getenv( "PA_ALSA_INITIALIZE_BLOCK" ) ) ) blocking = 0; - /* These two will be set to the first working input and output device, respectively */ - baseApi->info.defaultInputDevice = paNoDevice; - baseApi->info.defaultOutputDevice = paNoDevice; /* Gather info about hw devices @@ -1341,8 +1371,14 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) else PA_DEBUG(( "%s: Iterating over ALSA plugins failed: %s\n", __FUNCTION__, alsa_snd_strerror( res ) )); + out = (PaLinuxScanDeviceInfosResults *) PaUtil_GroupAllocateMemory( + alsaApi->allocations, sizeof(PaLinuxScanDeviceInfosResults) ); + + out->defaultInputDevice = paNoDevice; + out->defaultOutputDevice = paNoDevice; + /* allocate deviceInfo memory based on the number of devices */ - PA_UNLESS( baseApi->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( + PA_UNLESS( out->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( alsaApi->allocations, sizeof(PaDeviceInfo*) * (numDeviceNames) ), paInsufficientMemory ); /* allocate all device info structs in a contiguous block */ @@ -1367,7 +1403,7 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) continue; } - PA_ENSURE( FillInDevInfo( alsaApi, hwInfo, blocking, devInfo, &devIdx ) ); + PA_ENSURE( FillInDevInfo( alsaApi, hwInfo, blocking, devInfo, &devIdx, out ) ); } assert( devIdx < numDeviceNames ); /* Now inspect 'dmix' and 'default' plugins */ @@ -1381,11 +1417,13 @@ static PaError BuildDeviceList( PaAlsaHostApiRepresentation *alsaApi ) } PA_ENSURE( FillInDevInfo( alsaApi, hwInfo, blocking, devInfo, - &devIdx ) ); + &devIdx, out ) ); } free( hwDevInfos ); - baseApi->info.deviceCount = devIdx; /* Number of successfully queried devices */ + out->deviceCount = devIdx; /* Number of successfully queried devices */ + *scanResults = out; + *count = out->deviceCount; #ifdef PA_ENABLE_DEBUG_OUTPUT PA_DEBUG(( "%s: Building device list took %f seconds\n", __FUNCTION__, PaUtil_GetTime() - startTime )); @@ -2037,7 +2075,7 @@ static PaError PaAlsaStreamComponent_FinishConfigure( PaAlsaStreamComponent *sel ENSURE_( alsa_snd_pcm_sw_params_set_avail_min( self->pcm, swParams, self->framesPerBuffer ), paUnanticipatedHostError ); ENSURE_( alsa_snd_pcm_sw_params_set_xfer_align( self->pcm, swParams, 1 ), paUnanticipatedHostError ); - ENSURE_( alsa_snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_ENABLE ), paUnanticipatedHostError ); + ENSURE_( alsa_snd_pcm_sw_params_set_tstamp_mode( self->pcm, swParams, SND_PCM_TSTAMP_MMAP ), paUnanticipatedHostError ); /* Set the parameters! */ ENSURE_( alsa_snd_pcm_sw_params( self->pcm, swParams ), paUnanticipatedHostError ); @@ -3544,8 +3582,19 @@ static PaError PaAlsaStreamComponent_GetAvailableFrames( PaAlsaStreamComponent * snd_pcm_sframes_t framesAvail = alsa_snd_pcm_avail_update( self->pcm ); *xrunOccurred = 0; - if( -EPIPE == framesAvail ) - { + /* Get pcm_state and check for xrun condition. On playback I often see + * xrun but avail_update does not return -EPIPE but framesAvail larger + * than bufferSize. In case of xrun status set xrun flag, leave framesize + * as reported by avail_update, will be fixed below. In case avail_update + * returns -EPIPE process as usual. wd-xxx + */ + snd_pcm_state_t state = snd_pcm_state( self->pcm ); /* wd-xxx */ + if (state == SND_PCM_STATE_XRUN) { + // printf("xrun, fav %d\n", framesAvail); fflush(stdout); // DEBUG-WD + *xrunOccurred = 1; + } + if( -EPIPE == framesAvail) { + // printf("xrun-1, fav %d\n", framesAvail); fflush(stdout); // DEBUG-WD *xrunOccurred = 1; framesAvail = 0; } @@ -3554,6 +3603,11 @@ static PaError PaAlsaStreamComponent_GetAvailableFrames( PaAlsaStreamComponent * ENSURE_( framesAvail, paUnanticipatedHostError ); } + /* Fix frames avail, should not be bigger than bufferSize wd-xxx */ + if (framesAvail > self->bufferSize) { + // printf("xrun-2, fav %d\n", framesAvail); fflush(stdout); // DEBUG-WD + framesAvail = self->bufferSize; + } *numFrames = framesAvail; error: @@ -3603,6 +3657,13 @@ static PaError PaAlsaStreamComponent_EndPolling( PaAlsaStreamComponent* self, st *shouldPoll = 0; } + else + { + // not actually used + unsigned long framesAvail = 0; + // now check for xrun + PaAlsaStreamComponent_GetAvailableFrames(self, &framesAvail, xrun ); + } error: return result; @@ -3693,6 +3754,10 @@ static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *fr framesAvail, &xrun ) ); if( xrun ) { + if(*framesAvail == 0) + { + result = paInternalError; + } goto end; } @@ -3740,6 +3805,7 @@ static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *fr Pa_Sleep( 1 ); /* avoid hot loop */ continue; } + result = paInternalError; /* TODO: Add macro for checking system calls */ PA_ENSURE( paInternalError ); @@ -3769,6 +3835,7 @@ static PaError PaAlsaStream_WaitForFrames( PaAlsaStream *self, unsigned long *fr xrun = 1; /* try recovering device */ PA_DEBUG(( "%s: poll timed out\n", __FUNCTION__, timeouts )); + result = paTimedOut; goto end;/*PA_ENSURE( paTimedOut );*/ } } @@ -4326,9 +4393,24 @@ static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames ) while( frames > 0 ) { int xrun = 0; - PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); - framesGot = PA_MIN( framesAvail, frames ); + PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) ); + /* + * In case of overrun WaitForFrames leaves the capture stream in STATE_PREPARED + * most of the time. handleXrun() restarts the ALSA stream only in case + * snd_pcm_recover() fails, which usually does not happen. + * Here we start the pcm stream again and go for another try. Another + * option is: set result to paOverrun and return to caller. Then + * the caller needs to call ReadStream again. This takes more time and + * we lose even more frames. + */ + if (xrun) { /* wd-xxx */ + if( snd_pcm_state( stream->capture.pcm ) == SND_PCM_STATE_PREPARED ) { + ENSURE_( snd_pcm_start( stream->capture.pcm ), paUnanticipatedHostError ); + } + continue; + } + framesGot = PA_MIN( framesAvail, frames ); PA_ENSURE( PaAlsaStream_SetUpBuffers( stream, &framesGot, &xrun ) ); if( framesGot > 0 ) { @@ -4546,3 +4628,75 @@ PaError PaAlsa_SetRetriesBusy( int retries ) busyRetries_ = retries; return paNoError; } + +static PaError ScanDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex, + void **scanResults, int *newDeviceCount) +{ + PaAlsaHostApiRepresentation* alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi; + PaError result = paNoError; + PA_ENSURE(BuildDeviceList( alsaHostApi, scanResults, newDeviceCount )); + + return paNoError; + +error: + return result; +} + +static PaError CommitDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, + void *scanResults, int deviceCount) +{ + PaAlsaHostApiRepresentation* alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi; + PaError result = paNoError; + + /* These two will be set to the first working input and output device, respectively */ + hostApi->info.defaultInputDevice = paNoDevice; + hostApi->info.defaultOutputDevice = paNoDevice; + + /* Free any old memory which might be in the device info */ + if( hostApi->deviceInfos ) + { + /* all device info structs are allocated in a block so we can destroy them here */ + PaUtil_GroupFreeMemory( alsaHostApi->allocations, hostApi->deviceInfos[0] ); + PaUtil_GroupFreeMemory( alsaHostApi->allocations, hostApi->deviceInfos ); + hostApi->deviceInfos = NULL; + } + + if( scanResults != NULL ) + { + PaLinuxScanDeviceInfosResults *scanDeviceInfosResults = ( PaLinuxScanDeviceInfosResults * ) scanResults; + + if( deviceCount > 0 ) + { + /* use the array allocated in ScanDeviceInfos() as our deviceInfos */ + hostApi->deviceInfos = scanDeviceInfosResults->deviceInfos; + hostApi->info.defaultInputDevice = scanDeviceInfosResults->defaultInputDevice; + hostApi->info.defaultOutputDevice = scanDeviceInfosResults->defaultOutputDevice; + hostApi->info.deviceCount = deviceCount; + } + + PaUtil_GroupFreeMemory( alsaHostApi->allocations, scanDeviceInfosResults ); + } + + return result; +} + +static PaError DisposeDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, void *scanResults, int deviceCount) +{ + PaAlsaHostApiRepresentation *alsaHostApi = (PaAlsaHostApiRepresentation*)hostApi; + + if( scanResults != NULL ) + { + PaLinuxScanDeviceInfosResults *scanDeviceInfosResults = ( PaLinuxScanDeviceInfosResults * ) scanResults; + if( scanDeviceInfosResults->deviceInfos ) + { + /* all device info structs are allocated in a block so we can destroy them here */ + PaUtil_GroupFreeMemory( alsaHostApi->allocations, scanDeviceInfosResults->deviceInfos[0] ); + PaUtil_GroupFreeMemory( alsaHostApi->allocations, scanDeviceInfosResults->deviceInfos ); + } + + PaUtil_GroupFreeMemory(alsaHostApi->allocations, scanDeviceInfosResults ); + } + + return paNoError; +} + diff --git a/src/hostapi/asio/pa_asio.cpp b/src/hostapi/asio/pa_asio.cpp index d77c7e7..ff8ea3e 100644 --- a/src/hostapi/asio/pa_asio.cpp +++ b/src/hostapi/asio/pa_asio.cpp @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: pa_asio.cpp 1661 2011-04-28 18:54:46Z rob_bielik $ * Portable Audio I/O Library for ASIO Drivers * * Author: Stephane Letz @@ -1315,6 +1315,9 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex } + (*hostApi)->ScanDeviceInfos = NULL; + (*hostApi)->CommitDeviceInfos = NULL; + (*hostApi)->DisposeDeviceInfos = NULL; (*hostApi)->Terminate = Terminate; (*hostApi)->OpenStream = OpenStream; (*hostApi)->IsFormatSupported = IsFormatSupported; diff --git a/src/hostapi/coreaudio/pa_mac_core.c b/src/hostapi/coreaudio/pa_mac_core.c index 01376b9..a71a74c 100644 --- a/src/hostapi/coreaudio/pa_mac_core.c +++ b/src/hostapi/coreaudio/pa_mac_core.c @@ -80,6 +80,27 @@ extern "C" /* prototypes for functions declared in this file */ PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex index ); +static PaError ScanDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, + void **newDeviceInfos, int *newDeviceCount ); +static PaError CommitDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, + void *deviceInfos, int deviceCount); +static PaError DisposeDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, void *deviceInfos, + int deviceCount ); + +/* structures */ + +/* used for tranferring device infos during scanning / rescanning */ +typedef struct PaMacScanDeviceInfosResults +{ + PaDeviceInfo **deviceInfos; + PaDeviceIndex defaultInputDevice; + PaDeviceIndex defaultOutputDevice; + + AudioDeviceID* devIds; + int devCount; + AudioDeviceID devInputDevice; + AudioDeviceID devOutputDevice; +} PaMacScanDeviceInfosResults; /* * Function declared in pa_mac_core.h. Sets up a PaMacCoreStreamInfoStruct @@ -132,7 +153,6 @@ const char *PaMacCore_GetChannelName( int device, int channelIndex, bool input ) return NULL; PaMacAUHAL *macCoreHostApi = (PaMacAUHAL*)hostApi; AudioDeviceID hostApiDevice = macCoreHostApi->devIds[device]; - UInt32 size = 0; error = AudioDeviceGetPropertyInfo( hostApiDevice, @@ -311,46 +331,57 @@ static void startStopCallback( } -/*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) +//static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi) +static PaError gatherDeviceInfo(PaMacAUHAL* auhalHostApi, void** scanResults, int* count) { - UInt32 size; - UInt32 propsize; - VVDBUG(("gatherDeviceInfo()\n")); - /* -- free any previous allocations -- */ - if( auhalHostApi->devIds ) - PaUtil_GroupFreeMemory(auhalHostApi->allocations, auhalHostApi->devIds); - auhalHostApi->devIds = NULL; + UInt32 propsize = 0; + UInt32 size = sizeof(AudioDeviceID); + PaMacScanDeviceInfosResults *outArgument = NULL; + PaError result = paNoError; + VVDBUG(("gatherDeviceInfo()\n")); /* -- figure out how many devices there are -- */ AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices, &propsize, NULL ); - auhalHostApi->devCount = propsize / sizeof( AudioDeviceID ); + *count = (propsize / sizeof(AudioDeviceID)); - VDBUG( ( "Found %ld device(s).\n", auhalHostApi->devCount ) ); + VDBUG( ( "Found %ld device(s).\n", *count ) ); + + if(*count == 0) + return paNoError; + + /* Allocate the out param for all the info we need */ + outArgument = (PaMacScanDeviceInfosResults *) PaUtil_GroupAllocateMemory( + auhalHostApi->allocations, sizeof(PaMacScanDeviceInfosResults) ); + + if( !outArgument ) + { + result = paInsufficientMemory; + return result; + } + + outArgument->devCount = *count; /* -- copy the device IDs -- */ - auhalHostApi->devIds = (AudioDeviceID *)PaUtil_GroupAllocateMemory( + outArgument->devIds = (AudioDeviceID *)PaUtil_GroupAllocateMemory( auhalHostApi->allocations, propsize ); - if( !auhalHostApi->devIds ) + if( !outArgument->devIds ) return paInsufficientMemory; AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &propsize, - auhalHostApi->devIds ); + outArgument->devIds ); #ifdef MAC_CORE_VERBOSE_DEBUG { int i; - for( i=0; idevCount; ++i ) - printf( "Device %d\t: %ld\n", i, auhalHostApi->devIds[i] ); + for( i=0; idevCount; ++i ) + printf( "Device %d\t: %ld\n", i, outArgument->devIds[i] ); } #endif - size = sizeof(AudioDeviceID); - auhalHostApi->defaultIn = kAudioDeviceUnknown; - auhalHostApi->defaultOut = kAudioDeviceUnknown; + outArgument->devInputDevice = kAudioDeviceUnknown; + outArgument->devOutputDevice = kAudioDeviceUnknown; /* determine the default device. */ /* I am not sure how these calls to AudioHardwareGetProperty() @@ -358,42 +389,44 @@ static PaError gatherDeviceInfo(PaMacAUHAL *auhalHostApi) device as the default. */ if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &size, - &auhalHostApi->defaultIn) ) { + &outArgument->devInputDevice) ) { int i; - auhalHostApi->defaultIn = kAudioDeviceUnknown; + outArgument->devInputDevice = kAudioDeviceUnknown; VDBUG(("Failed to get default input device from OS.")); VDBUG((" I will substitute the first available input Device.")); - for( i=0; idevCount; ++i ) { + + for( i=0; i< outArgument->devCount; ++i ) { PaDeviceInfo devInfo; if( 0 != GetChannelInfo( auhalHostApi, &devInfo, - auhalHostApi->devIds[i], TRUE ) ) + outArgument->devIds[i], TRUE ) ) if( devInfo.maxInputChannels ) { - auhalHostApi->defaultIn = auhalHostApi->devIds[i]; + outArgument->devInputDevice = outArgument->devIds[i]; break; } } } if( 0 != AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, - &auhalHostApi->defaultOut) ) { + &outArgument->devOutputDevice) ) { int i; - auhalHostApi->defaultIn = kAudioDeviceUnknown; + outArgument->devOutputDevice = kAudioDeviceUnknown; VDBUG(("Failed to get default output device from OS.")); VDBUG((" I will substitute the first available output Device.")); - for( i=0; idevCount; ++i ) { + for( i=0; idevCount; ++i ) { PaDeviceInfo devInfo; if( 0 != GetChannelInfo( auhalHostApi, &devInfo, - auhalHostApi->devIds[i], FALSE ) ) + outArgument->devIds[i], FALSE ) ) if( devInfo.maxOutputChannels ) { - auhalHostApi->defaultOut = auhalHostApi->devIds[i]; + outArgument->devOutputDevice = outArgument->devIds[i]; break; } } } - VDBUG( ( "Default in : %ld\n", auhalHostApi->defaultIn ) ); - VDBUG( ( "Default out: %ld\n", auhalHostApi->defaultOut ) ); + VDBUG( ( "Default in : %ld\n", outArgument->devInputDevice ) ); + VDBUG( ( "Default out: %ld\n", outArgument->devOutputDevice ) ); + *scanResults = outArgument; return paNoError; } @@ -402,9 +435,9 @@ static PaError GetChannelInfo( PaMacAUHAL *auhalHostApi, AudioDeviceID macCoreDeviceId, int isInput) { - UInt32 propSize; + UInt32 propSize = 0; PaError err = paNoError; - UInt32 i; + UInt32 i = 0; int numChannels = 0; AudioBufferList *buflist = NULL; UInt32 frameLatency; @@ -528,10 +561,10 @@ static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi, PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex ) { PaError result = paNoError; - int i; PaMacAUHAL *auhalHostApi = NULL; - PaDeviceInfo *deviceInfoArray; int unixErr; + void* scanResults = NULL; + int deviceCount = 0; VVDBUG(("PaMacCore_Initialize(): hostApiIndex=%d\n", hostApiIndex)); @@ -572,65 +605,29 @@ PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIn auhalHostApi->devIds = NULL; auhalHostApi->devCount = 0; - /* get the info we need about the devices */ - result = gatherDeviceInfo( auhalHostApi ); - if( result != paNoError ) - goto error; - *hostApi = &auhalHostApi->inheritedHostApiRep; (*hostApi)->info.structVersion = 1; (*hostApi)->info.type = paCoreAudio; (*hostApi)->info.name = "Core Audio"; - + + (*hostApi)->deviceInfos = NULL; + (*hostApi)->info.defaultInputDevice = paNoDevice; (*hostApi)->info.defaultOutputDevice = paNoDevice; - (*hostApi)->info.deviceCount = 0; - if( auhalHostApi->devCount > 0 ) - { - (*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( - auhalHostApi->allocations, sizeof(PaDeviceInfo*) * auhalHostApi->devCount); - if( !(*hostApi)->deviceInfos ) - { - result = paInsufficientMemory; - goto error; - } + (*hostApi)->ScanDeviceInfos = ScanDeviceInfos; + (*hostApi)->CommitDeviceInfos = CommitDeviceInfos; + (*hostApi)->DisposeDeviceInfos = DisposeDeviceInfos; - /* allocate all device info structs in a contiguous block */ - deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( - auhalHostApi->allocations, sizeof(PaDeviceInfo) * auhalHostApi->devCount ); - if( !deviceInfoArray ) - { - result = paInsufficientMemory; - goto error; - } + result = ScanDeviceInfos(&auhalHostApi->inheritedHostApiRep, hostApiIndex, &scanResults, + &deviceCount); - for( i=0; i < auhalHostApi->devCount; ++i ) - { - int err; - err = InitializeDeviceInfo( auhalHostApi, &deviceInfoArray[i], - auhalHostApi->devIds[i], - hostApiIndex ); - if (err == paNoError) - { /* copy some info and set the defaults */ - (*hostApi)->deviceInfos[(*hostApi)->info.deviceCount] = &deviceInfoArray[i]; - if (auhalHostApi->devIds[i] == auhalHostApi->defaultIn) - (*hostApi)->info.defaultInputDevice = (*hostApi)->info.deviceCount; - if (auhalHostApi->devIds[i] == auhalHostApi->defaultOut) - (*hostApi)->info.defaultOutputDevice = (*hostApi)->info.deviceCount; - (*hostApi)->info.deviceCount++; - } - else - { /* there was an error. we need to shift the devices down, so we ignore this one */ - int j; - auhalHostApi->devCount--; - for( j=i; jdevCount; ++j ) - auhalHostApi->devIds[j] = auhalHostApi->devIds[j+1]; - i--; - } - } - } + if(result != paNoError) + goto error; + + /* FIXME for now we ignore the result of CommitDeviceInfos(), it should probably be an atomic non-failing operation */ + CommitDeviceInfos( &auhalHostApi->inheritedHostApiRep, hostApiIndex, scanResults, deviceCount ); (*hostApi)->Terminate = Terminate; (*hostApi)->OpenStream = OpenStream; @@ -2468,3 +2465,148 @@ static double GetStreamCpuLoad( PaStream* s ) return PaUtil_GetCpuLoad( &stream->cpuLoadMeasurer ); } + +static PaError ScanDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex, + void **scanResults, int *newDeviceCount) +{ + PaMacAUHAL* auhalHostApi = (PaMacAUHAL*)hostApi; + PaDeviceInfo *deviceInfoArray = NULL; + PaError result = paNoError; + int i = 0; + PaMacScanDeviceInfosResults* out = NULL; + + /* get the info we need about the devices */ + result = gatherDeviceInfo( auhalHostApi, scanResults, newDeviceCount ); + + if( result != paNoError ) + return result; + + out = (PaMacScanDeviceInfosResults*)*scanResults; + + if( out->devCount > 0 ) + { + int count = 0; + + /* allocate array for pointers to PaDeviceInfo structs */ + out->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory( + auhalHostApi->allocations, sizeof(PaDeviceInfo*) * out->devCount); + if( !out->deviceInfos ) + { + result = paInsufficientMemory; + return result; + } + + /* allocate all device info structs in a contiguous block */ + deviceInfoArray = (PaDeviceInfo*)PaUtil_GroupAllocateMemory( + auhalHostApi->allocations, sizeof(PaDeviceInfo) * out->devCount ); + if( !deviceInfoArray ) + { + result = paInsufficientMemory; + return result; + } + + for( i=0; i < out->devCount; ++i ) + { + int err; + err = InitializeDeviceInfo( auhalHostApi, &deviceInfoArray[i], + out->devIds[i], + hostApiIndex ); + if (err == paNoError) + { + /* copy some info and set the defaults */ + out->deviceInfos[count] = &deviceInfoArray[i]; + + if (out->devIds[i] == out->devInputDevice) + { + out->defaultInputDevice = count; + } + if (out->devIds[i] == out->devOutputDevice) + { + out->defaultOutputDevice = count; + } + count++; + } + else + { + /* there was an error. we need to shift the devices down, so we ignore this one */ + int j; + out->devCount--; + for( j=i; jdevCount; ++j ) + out->devIds[j] = out->devIds[j+1]; + i--; + } + } + } + *newDeviceCount = out->devCount; + + return paNoError; +} + +static PaError CommitDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, + void *scanResults, int deviceCount) +{ + PaMacAUHAL* auhalHostApi = (PaMacAUHAL*)hostApi; + PaError result = paNoError; + + hostApi->info.deviceCount = 0; + hostApi->info.defaultInputDevice = paNoDevice; + hostApi->info.defaultOutputDevice = paNoDevice; + + /* -- free any previous allocations -- */ + if( auhalHostApi->devIds ) + { + PaUtil_GroupFreeMemory(auhalHostApi->allocations, auhalHostApi->devIds); + } + auhalHostApi->devIds = NULL; + + /* Free any old memory which might be in the device info */ + if( hostApi->deviceInfos ) + { + PaUtil_GroupFreeMemory( auhalHostApi->allocations, hostApi->deviceInfos[0] ); + PaUtil_GroupFreeMemory( auhalHostApi->allocations, hostApi->deviceInfos ); + hostApi->deviceInfos = NULL; + } + + if( scanResults != NULL ) + { + PaMacScanDeviceInfosResults *scanDeviceInfosResults = ( PaMacScanDeviceInfosResults * ) scanResults; + + if( deviceCount > 0 ) + { + /* use the array allocated in ScanDeviceInfos() as our deviceInfos */ + hostApi->deviceInfos = scanDeviceInfosResults->deviceInfos; + hostApi->info.defaultInputDevice = scanDeviceInfosResults->defaultInputDevice; + hostApi->info.defaultOutputDevice = scanDeviceInfosResults->defaultOutputDevice; + hostApi->info.deviceCount = deviceCount; + auhalHostApi->devIds = scanDeviceInfosResults->devIds; + auhalHostApi->devCount = scanDeviceInfosResults->devCount; + auhalHostApi->defaultIn = scanDeviceInfosResults->devInputDevice; + auhalHostApi->defaultOut = scanDeviceInfosResults->devOutputDevice; + } + + PaUtil_GroupFreeMemory( auhalHostApi->allocations, scanDeviceInfosResults ); + } + + return result; +} + +static PaError DisposeDeviceInfos(struct PaUtilHostApiRepresentation *hostApi, void *scanResults, int deviceCount) +{ + PaMacAUHAL *auhalHostApi = (PaMacAUHAL*)hostApi; + + if( scanResults != NULL ) + { + PaMacScanDeviceInfosResults *scanDeviceInfosResults = ( PaMacScanDeviceInfosResults * ) scanResults; + if( scanDeviceInfosResults->deviceInfos ) + { + /* all device info structs are allocated in a block so we can destroy them here */ + PaUtil_GroupFreeMemory( auhalHostApi->allocations, scanDeviceInfosResults->deviceInfos[0] ); + PaUtil_GroupFreeMemory( auhalHostApi->allocations, scanDeviceInfosResults->deviceInfos ); + } + + PaUtil_GroupFreeMemory(auhalHostApi->allocations, scanDeviceInfosResults ); + } + + return paNoError; +} + diff --git a/src/hostapi/jack/pa_jack.c b/src/hostapi/jack/pa_jack.c index 24a56d9..fd78e8b 100644 --- a/src/hostapi/jack/pa_jack.c +++ b/src/hostapi/jack/pa_jack.c @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: pa_jack.c 1661 2011-04-28 18:54:46Z rob_bielik $ * PortAudio Portable Real-Time Audio Library * Latest Version at: http://www.portaudio.com * JACK Implementation by Joshua Haberman @@ -749,6 +749,9 @@ PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi, /* Register functions */ + (*hostApi)->ScanDeviceInfos = NULL; + (*hostApi)->CommitDeviceInfos = NULL; + (*hostApi)->DisposeDeviceInfos = NULL; (*hostApi)->Terminate = Terminate; (*hostApi)->OpenStream = OpenStream; (*hostApi)->IsFormatSupported = IsFormatSupported; @@ -1181,7 +1184,7 @@ static PaError OpenStream( struct PaUtilHostApiRepresentation *hostApi, minimum_buffer_frames = jackHostApi->jack_buffer_size * 3; /* setup blocking API data structures (FIXME: can fail) */ - BlockingBegin( stream, minimum_buffer_frames ); + BlockingBegin( stream, minimum_buffer_frames ); /* install our own callback for the blocking API */ streamCallback = BlockingCallback; diff --git a/src/hostapi/oss/pa_unix_oss.c b/src/hostapi/oss/pa_unix_oss.c index b523746..5da34c1 100644 --- a/src/hostapi/oss/pa_unix_oss.c +++ b/src/hostapi/oss/pa_unix_oss.c @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: pa_unix_oss.c 1661 2011-04-28 18:54:46Z rob_bielik $ * PortAudio Portable Real-Time Audio Library * Latest Version at: http://www.portaudio.com * OSS implementation by: @@ -256,6 +256,9 @@ PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex (*hostApi)->Terminate = Terminate; (*hostApi)->OpenStream = OpenStream; (*hostApi)->IsFormatSupported = IsFormatSupported; + (*hostApi)->ScanDeviceInfos = NULL; + (*hostApi)->CommitDeviceInfos = NULL; + (*hostApi)->DisposeDeviceInfos = NULL; PA_ENSURE( BuildDeviceList( ossHostApi ) ); diff --git a/src/hostapi/skeleton/pa_hostapi_skeleton.c b/src/hostapi/skeleton/pa_hostapi_skeleton.c index 84f73c7..726c407 100644 --- a/src/hostapi/skeleton/pa_hostapi_skeleton.c +++ b/src/hostapi/skeleton/pa_hostapi_skeleton.c @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: pa_hostapi_skeleton.c 1661 2011-04-28 18:54:46Z rob_bielik $ * Portable Audio I/O Library skeleton implementation * demonstrates how to use the common functions to implement support * for a host API @@ -206,9 +206,15 @@ PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiI } } + (*hostApi)->ScanDeviceInfos = NULL; + (*hostApi)->CommitDeviceInfos = NULL; + (*hostApi)->DisposeDeviceInfos = NULL; (*hostApi)->Terminate = Terminate; (*hostApi)->OpenStream = OpenStream; (*hostApi)->IsFormatSupported = IsFormatSupported; + (*hostApi)->ScanDeviceInfos = NULL; + (*hostApi)->CommitDeviceInfos = NULL; + (*hostApi)->DisposeDeviceInfos = NULL; PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, CloseStream, StartStream, StopStream, AbortStream, IsStreamStopped, IsStreamActive, diff --git a/src/hostapi/wasapi/pa_win_wasapi.c b/src/hostapi/wasapi/pa_win_wasapi.c index bd92587..0310151 100644 --- a/src/hostapi/wasapi/pa_win_wasapi.c +++ b/src/hostapi/wasapi/pa_win_wasapi.c @@ -1386,6 +1386,9 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd } } + (*hostApi)->ScanDeviceInfos = NULL; + (*hostApi)->CommitDeviceInfos = NULL; + (*hostApi)->DisposeDeviceInfos = NULL; (*hostApi)->Terminate = Terminate; (*hostApi)->OpenStream = OpenStream; (*hostApi)->IsFormatSupported = IsFormatSupported; diff --git a/src/hostapi/wdmks/pa_win_wdmks.c b/src/hostapi/wdmks/pa_win_wdmks.c index 8070dbf..40297b7 100644 --- a/src/hostapi/wdmks/pa_win_wdmks.c +++ b/src/hostapi/wdmks/pa_win_wdmks.c @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: pa_win_wdmks.c 1661 2011-04-28 18:54:46Z rob_bielik $ * PortAudio Windows WDM-KS interface * * Author: Andrew Baldwin @@ -1885,6 +1885,9 @@ PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd (*hostApi)->info.deviceCount = deviceCount; + (*hostApi)->ScanDeviceInfos = NULL; + (*hostApi)->CommitDeviceInfos = NULL; + (*hostApi)->DisposeDeviceInfos = NULL; (*hostApi)->Terminate = Terminate; (*hostApi)->OpenStream = OpenStream; (*hostApi)->IsFormatSupported = IsFormatSupported; @@ -3305,4 +3308,4 @@ static signed long GetStreamWriteAvailable( PaStream* s ) /* IMPLEMENT ME, see portaudio.h for required behavior*/ PA_LOGL_; return 0; -} \ No newline at end of file +} diff --git a/src/os/mac_osx/pa_osx_hotplug.c b/src/os/mac_osx/pa_osx_hotplug.c new file mode 100644 index 0000000..22e7471 --- /dev/null +++ b/src/os/mac_osx/pa_osx_hotplug.c @@ -0,0 +1,76 @@ + +#include "pa_util.h" +#include "pa_debugprint.h" +#include "pa_allocation.h" + +#include + +#include +#include + +/* Implemented in pa_front.c + @param first 0 = unknown, 1 = insertion, 2 = removal + @param second Host specific device change info (in windows it is the (unicode) device path) +*/ +extern void PaUtil_DevicesChanged(unsigned, void*); + +/* Callback for audio hardware property changes. */ +static OSStatus audioPropertyCallback(AudioHardwarePropertyID inPropertyID, + void *refCon) +{ + (void)refCon; + switch (inPropertyID) + { + /* + * These are the other types of notifications we might receive, however, they are beyond + * the scope of this sample and we ignore them. + */ + case kAudioHardwarePropertyDefaultInputDevice: + PA_DEBUG(("audioPropertyCallback: default input device changed\n")); + break; + case kAudioHardwarePropertyDefaultOutputDevice: + PA_DEBUG(("audioPropertyCallback: default output device changed\n")); + break; + case kAudioHardwarePropertyDefaultSystemOutputDevice: + PA_DEBUG(("audioPropertyCallback: default system output device changed\n")); + break; + case kAudioHardwarePropertyDevices: + PA_DEBUG(("audioPropertyCallback: device list changed\n")); + PaUtil_DevicesChanged(1, NULL); + break; + default: + PA_DEBUG(("audioPropertyCallback: unknown message id=%08lx\n", inPropertyID)); + break; + } + + return noErr; +} + +void PaUtil_InitializeHotPlug() +{ + AudioHardwareAddPropertyListener(kAudioHardwarePropertyDevices, + audioPropertyCallback, NULL); + AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultInputDevice, + audioPropertyCallback, NULL); + AudioHardwareAddPropertyListener(kAudioHardwarePropertyDefaultOutputDevice, + audioPropertyCallback, NULL); +} + +void PaUtil_TerminateHotPlug() +{ + AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDevices, + audioPropertyCallback); + AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDefaultInputDevice, + audioPropertyCallback); + AudioHardwareRemovePropertyListener(kAudioHardwarePropertyDefaultOutputDevice, + audioPropertyCallback); +} + +void PaUtil_LockHotPlug() +{ +} + +void PaUtil_UnlockHotPlug() +{ +} + diff --git a/src/os/unix/pa_linux_hotplug.c b/src/os/unix/pa_linux_hotplug.c new file mode 100644 index 0000000..ba6eb19 --- /dev/null +++ b/src/os/unix/pa_linux_hotplug.c @@ -0,0 +1,147 @@ + +#include "pa_util.h" +#include "pa_debugprint.h" +#include "pa_allocation.h" +#include "pa_linux_alsa.h" +#include "pa_hostapi.h" + +#include + +#include +#include +#include + +/* Implemented in pa_front.c + @param first 0 = unknown, 1 = insertion, 2 = removal + @param second Host specific device change info (in windows it is the (unicode) device path) +*/ +extern void PaUtil_DevicesChanged(unsigned, void*); + +static pthread_t g_thread_id; +static pthread_mutex_t g_mutex; +static volatile sig_atomic_t g_run = 0; + +static int device_list_size(void) +{ + snd_ctl_t *handle; + int card, err, dev, idx; + int nb = 0; + snd_ctl_card_info_t *info; + snd_pcm_info_t *pcminfo; + snd_ctl_card_info_alloca(&info); + snd_pcm_info_alloca(&pcminfo); + + card = -1; + if (snd_card_next(&card) < 0 || card < 0) + { + return nb; + } + + while (card >= 0) + { + char name[32]; + + sprintf(name, "hw:%d", card); + if ((err = snd_ctl_open(&handle, name, 0)) < 0) + { + goto next_card; + } + if ((err = snd_ctl_card_info(handle, info)) < 0) + { + snd_ctl_close(handle); + goto next_card; + } + dev = -1; + while (1) + { + unsigned int count; + int hasPlayback = 0; + int hasCapture = 0; + + snd_ctl_pcm_next_device(handle, &dev); + + if (dev < 0) + break; + snd_pcm_info_set_device(pcminfo, dev); + snd_pcm_info_set_subdevice(pcminfo, 0); + snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_CAPTURE); + if ((err = snd_ctl_pcm_info(handle, pcminfo)) >= 0) + { + hasCapture = 1; + } + + snd_pcm_info_set_stream(pcminfo, SND_PCM_STREAM_PLAYBACK); + if ((err = snd_ctl_pcm_info(handle, pcminfo)) >= 0) + { + hasPlayback = 1; + + count = snd_pcm_info_get_subdevices_count(pcminfo); + } + + if(hasPlayback == 0 && hasCapture == 0) + continue; + + nb++; + } + snd_ctl_close(handle); +next_card: + if (snd_card_next(&card) < 0) + { + break; + } + } + return nb; +} + +static void* thread_fcn(void* data) +{ + int currentDevices = 0; + + currentDevices = device_list_size(); + + while(g_run) + { + int count = 0; + + sleep(1); + count = device_list_size(); + if(count != currentDevices) + { + /* 1 = add device, 2 = remove device */ + int add = (count > currentDevices) ? 1 : 2; + + currentDevices = count; + + PaUtil_DevicesChanged(add, NULL); + } + } + + return NULL; +} + +void PaUtil_InitializeHotPlug() +{ + pthread_mutex_init(&g_mutex, NULL); + g_run = 1; + pthread_create(&g_thread_id, NULL, thread_fcn, NULL); +} + +void PaUtil_TerminateHotPlug() +{ + void* ret = NULL; + + g_run = 0; + pthread_join(g_thread_id, &ret); + pthread_mutex_destroy(&g_mutex); +} + +void PaUtil_LockHotPlug() +{ + pthread_mutex_lock(&g_mutex); +} + +void PaUtil_UnlockHotPlug() +{ + pthread_mutex_unlock(&g_mutex); +} + diff --git a/src/os/win/pa_win_hostapis.c b/src/os/win/pa_win_hostapis.c index 7db5503..b1c6cbf 100644 --- a/src/os/win/pa_win_hostapis.c +++ b/src/os/win/pa_win_hostapis.c @@ -1,5 +1,5 @@ /* - * $Id$ + * $Id: pa_win_hostapis.c 1661 2011-04-28 18:54:46Z rob_bielik $ * Portable Audio I/O Library Windows initialization table * * Based on the Open Source API proposed by Ross Bencina @@ -81,7 +81,7 @@ PaUtilHostApiInitializer *paHostApiInitializers[] = #endif #if PA_USE_WDMKS - PaWinWdm_Initialize, + //PaWinWdm_Initialize, #endif #if PA_USE_SKELETON diff --git a/src/os/win/pa_win_hotplug.c b/src/os/win/pa_win_hotplug.c index 044aaa1..f259583 100644 --- a/src/os/win/pa_win_hotplug.c +++ b/src/os/win/pa_win_hotplug.c @@ -186,6 +186,8 @@ static LRESULT CALLBACK PaMsgWinProcW(HWND hWnd, UINT msg, WPARAM wParam, LPARAM { PA_DEBUG(("Device inserted : %S\n", ptr->dbcc_name)); InsertDeviceIntoCache(pInfo, ptr->dbcc_name); + /* yield some seconds because added device may not be completely configured */ + Sleep(2000); PaUtil_DevicesChanged(1, ptr->dbcc_name); } } @@ -202,6 +204,7 @@ static LRESULT CALLBACK PaMsgWinProcW(HWND hWnd, UINT msg, WPARAM wParam, LPARAM if (RemoveDeviceFromCache(pInfo, ptr->dbcc_name)) { PA_DEBUG(("Device removed : %S\n", ptr->dbcc_name)); + Sleep(2000); PaUtil_DevicesChanged(2, ptr->dbcc_name); } } diff --git a/test/patest_update_available_device_list.c b/test/patest_update_available_device_list.c index ce6cdba..9e901e0 100644 --- a/test/patest_update_available_device_list.c +++ b/test/patest_update_available_device_list.c @@ -1,5 +1,6 @@ #include #include +#include #include "portaudio.h" @@ -33,9 +34,10 @@ int main(int argc, char* argv[]) for(;;){ printDevices(); - + printf( "press [enter] to update the device list. or q + [enter] to quit.\n" ); - if( getchar() == 'q' ) + char ch = getchar(); + if( ch == 'q' ) break; Pa_UpdateAvailableDeviceList(); -- 2.43.0