Compare commits

...
Sign in to create a new pull request.
Author SHA1 Message Date
Ross Bencina
66bda13f84 Ports Portaudio WMME device timeout system to MacOsX CoreAudio in order to
avoid deadlock when a device is unplugged.
Vincent Lucas committed on Jan 10, 2013
2a20693 from https://github.com/jitsi/libsrc/commits/master/portaudio.zip
2016-09-03 22:53:30 +10:00
Ross Bencina
ab8b64023c Commits the C source code of a refining of the automatic audio device
selection to pick up devices from the same hardware.
lyubomir committed on Dec 19, 2012
6441b56 from https://github.com/jitsi/libsrc/commits/master/portaudio.zip
2016-09-03 22:51:44 +10:00
Ross Bencina
c534056a7b Fixes freezes in Pa_ReadStream, Pa_StopStream and Pa_WriteStream and
incorrect causes of paInvalidChannelCount errors in PortAudio on
Windows/WMME. Mitigates an issue with hightened CPU usage (on all
supported operating systems) related to hotplug.
lyubomir committed on Dec 4, 2012
a45b993 from https://github.com/jitsi/libsrc/commits/master/portaudio.zip
2016-09-03 22:49:43 +10:00
Ross Bencina
6c6269425f - Fixes multiple crashes in PortAudio.
- Removes the DirectSound support from PortAudio in order to prevent
  issues with bad audio quality.
  - Attempts to recognize USB PortAudio devices on Windows as such,
    ignores the virtual Microsoft Sound Mapper devices to avoid user
    confusion. Contributed by Vincent Lucas.
    lyubomir committed on Nov 15, 2012
    a93910c from
    https://github.com/jitsi/libsrc/commits/master/portaudio.zip
2016-09-03 22:46:54 +10:00
Ross Bencina
15d77417bf Adds hotplug to portaudio wmme host api, also fixes a NPE that can prevent
device change updates.
damencho committed on Nov 6, 2012
5ca6fed from https://github.com/jitsi/libsrc/commits/master/portaudio.zip
2016-09-03 22:44:31 +10:00
Ross Bencina
0a7b80cfce Adds device uid for wmme and dsound devices.
damencho committed on Nov 3, 2012
7e70e94 from https://github.com/jitsi/libsrc/commits/master/portaudio.zip
2016-09-03 22:36:13 +10:00
Ross Bencina
e7a274942b Improves device selection via portaudio for MacOSX (hostapi coreaudio):
adds the possibility to get the device transport type (USB, bluetooth,
etc.) and an unique identifier. Corrects problems when: loading the same
device twice (for the moment only for the same hostapi), a new plugged
device is selected during a call or if the selected device is unchanged.
Vincent Lucas committed on Oct 29, 2012
26848f0 from https://github.com/jitsi/libsrc/commits/master/portaudio.zip
2016-09-03 22:34:22 +10:00
Ross Bencina
b77a1f828c 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
2016-09-03 22:29:58 +10:00
Ross Bencina
d13fcd8ea9 Roll-up of SVN hotplug branch at most recent revision (r1677), based at the same point in the master revision history as it was originally applied. 2016-09-03 22:22:52 +10:00
39 changed files with 3567 additions and 1724 deletions

View file

@ -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 =

BIN
bindings/cpp/configure vendored

Binary file not shown.

View file

@ -35,6 +35,8 @@ Pa_GetStreamReadAvailable @31
Pa_GetStreamWriteAvailable @32
Pa_GetSampleSize @33
Pa_Sleep @34
Pa_UpdateAvailableDeviceList @35
Pa_SetDevicesChangedCallback @36
PaAsio_GetAvailableLatencyValues @50
PaAsio_ShowControlPanel @51
PaUtil_InitializeX86PlainConverters @52

BIN
configure vendored

Binary file not shown.

View file

@ -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

View file

@ -46,7 +46,7 @@
#include "portaudio.h"
#include <AudioUnit/AudioUnit.h>
//#include <AudioToolbox/AudioToolbox.h>
#include <AudioToolbox/AudioToolbox.h>
#ifdef __cplusplus
extern "C" {

View file

@ -1,7 +1,7 @@
#ifndef PORTAUDIO_H
#define PORTAUDIO_H
/*
* $Id$
* $Id: portaudio.h 1670 2011-05-04 16:37:52Z rob_bielik $
* PortAudio Portable Real-Time Audio Library
* PortAudio API Header File
* Latest version available at: http://www.portaudio.com/
@ -121,9 +121,17 @@ const char *Pa_GetErrorText( PaError errorCode );
Pairs of calls to Pa_Initialize()/Pa_Terminate() may overlap, and are not
required to be fully nested.
Note that if Pa_Initialize() returns an error code, Pa_Terminate() should
If Pa_Initialize() returns an error code, Pa_Terminate() should
NOT be called.
@note The device list returned by Pa_GetDeviceCount() et al. is frozen
at the time Pa_Initialize() is called, and not updated dynamically, even if
hardware devices (e.g. USB or firewire devices) are connected or disconnected
during the course of the program's lifetime. If it is required to re-scan the
list of devices, one must call Pa_UpdateAvailableDeviceList() or, completely
uninitialize PortAudio using Pa_Terminate(), and then reinitialize it by calling
Pa_Initialize().
@return paNoError if successful, otherwise an error code indicating the cause
of failure.
@ -397,6 +405,20 @@ PaDeviceIndex Pa_GetDefaultInputDevice( void );
*/
PaDeviceIndex Pa_GetDefaultOutputDevice( void );
/** Update the list of available devices to match currently available hardware devices.
PortAudio's list of available devices is usually frozen at the time Pa_Initialize() is
called. Pa_UpdateAvailableDeviceList() may be called to refresh PortAudio's list
of available devices at any time while PortAudio is initialized.
@note Open streams will not be affected by calls to this function, but
any previously returned PaDeviceInfo pointers are invalidated once this
function is called.
@return an error code indicating whether the device refresh was successful.
*/
PaError Pa_UpdateAvailableDeviceList( void );
/** The type used to represent monotonic time in seconds. PaTime is
used for the fields of the PaStreamCallbackTimeInfo argument to the
@ -448,7 +470,7 @@ typedef unsigned long PaSampleFormat;
*/
typedef struct PaDeviceInfo
{
int structVersion; /* this is struct version 2 */
int structVersion; /* this is struct version 3 */
const char *name;
PaHostApiIndex hostApi; /* note this is a host API index, not a type id*/
@ -463,6 +485,11 @@ typedef struct PaDeviceInfo
PaTime defaultHighOutputLatency;
double defaultSampleRate;
/*The transport type used by this device: USB, BuiltIn, Bluetooth, etc.*/
const char *transportType;
/*The unique identifier of this device (persistent across boots).*/
const char *deviceUID;
} PaDeviceInfo;
@ -1143,6 +1170,13 @@ void Pa_Sleep( long msec );
typedef void PaDevicesChangedCallback( void *userData );
PaError Pa_SetDevicesChangedCallback( void *userData, PaStreamFinishedCallback* devicesChangedCallback );
#ifdef __cplusplus
}
#endif /* __cplusplus */

Binary file not shown.

View file

@ -1,5 +1,5 @@
/*
* $Id$
* $Id: pa_converters.c 1661 2011-04-28 18:54:46Z rob_bielik $
* Portable Audio I/O Library sample conversion mechanism
*
* Based on the Open Source API proposed by Ross Bencina

View file

@ -1,5 +1,5 @@
/*
* $Id$
* $Id: pa_front.c 1673 2011-05-06 04:40:22Z rob_bielik $
* Portable Audio I/O Library Multi-Host API front end
* Validate function parameters and manage multiple host APIs.
*
@ -80,7 +80,8 @@
#define PA_VERSION_ 1899
#define PA_VERSION_TEXT_ "PortAudio V19-devel (built " __DATE__ " " __TIME__ ")"
extern void PaUtil_InitializeHotPlug();
extern void PaUtil_TerminateHotPlug();
int Pa_GetVersion( void )
@ -113,16 +114,26 @@ void PaUtil_SetLastHostErrorInfo( PaHostApiTypeId hostApiType, long errorCode,
}
typedef struct _PaInternalInfo
{
PaUtilHostApiRepresentation **hostApis_;
int hostApisCount_;
int initializationCount_;
int deviceCount_;
static PaUtilHostApiRepresentation **hostApis_ = 0;
static int hostApisCount_ = 0;
static int initializationCount_ = 0;
static int deviceCount_ = 0;
PaUtilStreamRepresentation *firstOpenStream_;
PaUtilStreamRepresentation *firstOpenStream_ = NULL;
PaDevicesChangedCallback* devicesChangedCallback_;
void* devicesChangedCallbackUserData_;
} PaInternalInfo;
static PaInternalInfo paInternalInfo_ = {0};
#define PA_IS_INITIALISED_ (initializationCount_ != 0)
#define PA_IS_INITIALISED_ (paInternalInfo_.initializationCount_ != 0)
static int CountHostApiInitializers( void )
@ -140,17 +151,17 @@ static void TerminateHostApis( void )
/* terminate in reverse order from initialization */
PA_DEBUG(("TerminateHostApis in \n"));
while( hostApisCount_ > 0 )
while( paInternalInfo_.hostApisCount_ > 0 )
{
--hostApisCount_;
hostApis_[hostApisCount_]->Terminate( hostApis_[hostApisCount_] );
--paInternalInfo_.hostApisCount_;
paInternalInfo_.hostApis_[paInternalInfo_.hostApisCount_]->Terminate( paInternalInfo_.hostApis_[paInternalInfo_.hostApisCount_] );
}
hostApisCount_ = 0;
deviceCount_ = 0;
paInternalInfo_.hostApisCount_ = 0;
paInternalInfo_.deviceCount_ = 0;
if( hostApis_ != 0 )
PaUtil_FreeMemory( hostApis_ );
hostApis_ = 0;
if( paInternalInfo_.hostApis_ != 0 )
PaUtil_FreeMemory( paInternalInfo_.hostApis_ );
paInternalInfo_.hostApis_ = 0;
PA_DEBUG(("TerminateHostApis out\n"));
}
@ -163,33 +174,33 @@ static PaError InitializeHostApis( void )
initializerCount = CountHostApiInitializers();
hostApis_ = (PaUtilHostApiRepresentation**)PaUtil_AllocateMemory(
paInternalInfo_.hostApis_ = (PaUtilHostApiRepresentation**)PaUtil_AllocateMemory(
sizeof(PaUtilHostApiRepresentation*) * initializerCount );
if( !hostApis_ )
if( !paInternalInfo_.hostApis_ )
{
result = paInsufficientMemory;
goto error;
}
hostApisCount_ = 0;
deviceCount_ = 0;
paInternalInfo_.hostApisCount_ = 0;
paInternalInfo_.deviceCount_ = 0;
baseDeviceIndex = 0;
for( i=0; i< initializerCount; ++i )
{
hostApis_[hostApisCount_] = NULL;
paInternalInfo_.hostApis_[paInternalInfo_.hostApisCount_] = NULL;
PA_DEBUG(( "before paHostApiInitializers[%d].\n",i));
result = paHostApiInitializers[i]( &hostApis_[hostApisCount_], hostApisCount_ );
result = paHostApiInitializers[i]( &paInternalInfo_.hostApis_[paInternalInfo_.hostApisCount_], paInternalInfo_.hostApisCount_ );
if( result != paNoError )
goto error;
PA_DEBUG(( "after paHostApiInitializers[%d].\n",i));
if( hostApis_[hostApisCount_] )
if( paInternalInfo_.hostApis_[paInternalInfo_.hostApisCount_] )
{
PaUtilHostApiRepresentation* hostApi = hostApis_[hostApisCount_];
PaUtilHostApiRepresentation* hostApi = paInternalInfo_.hostApis_[paInternalInfo_.hostApisCount_];
assert( hostApi->info.defaultInputDevice < hostApi->info.deviceCount );
assert( hostApi->info.defaultOutputDevice < hostApi->info.deviceCount );
@ -202,9 +213,9 @@ static PaError InitializeHostApis( void )
hostApi->info.defaultOutputDevice += baseDeviceIndex;
baseDeviceIndex += hostApi->info.deviceCount;
deviceCount_ += hostApi->info.deviceCount;
paInternalInfo_.deviceCount_ += hostApi->info.deviceCount;
++hostApisCount_;
++paInternalInfo_.hostApisCount_;
}
}
@ -233,15 +244,15 @@ static int FindHostApi( PaDeviceIndex device, int *hostSpecificDeviceIndex )
if( device < 0 )
return -1;
while( i < hostApisCount_
&& device >= hostApis_[i]->info.deviceCount )
while( i < paInternalInfo_.hostApisCount_
&& device >= paInternalInfo_.hostApis_[i]->info.deviceCount )
{
device -= hostApis_[i]->info.deviceCount;
device -= paInternalInfo_.hostApis_[i]->info.deviceCount;
++i;
}
if( i >= hostApisCount_ )
if( i >= paInternalInfo_.hostApisCount_ )
return -1;
if( hostSpecificDeviceIndex )
@ -253,15 +264,15 @@ static int FindHostApi( PaDeviceIndex device, int *hostSpecificDeviceIndex )
static void AddOpenStream( PaStream* stream )
{
((PaUtilStreamRepresentation*)stream)->nextOpenStream = firstOpenStream_;
firstOpenStream_ = (PaUtilStreamRepresentation*)stream;
((PaUtilStreamRepresentation*)stream)->nextOpenStream = paInternalInfo_.firstOpenStream_;
paInternalInfo_.firstOpenStream_ = (PaUtilStreamRepresentation*)stream;
}
static void RemoveOpenStream( PaStream* stream )
{
PaUtilStreamRepresentation *previous = NULL;
PaUtilStreamRepresentation *current = firstOpenStream_;
PaUtilStreamRepresentation *current = paInternalInfo_.firstOpenStream_;
while( current != NULL )
{
@ -269,7 +280,7 @@ static void RemoveOpenStream( PaStream* stream )
{
if( previous == NULL )
{
firstOpenStream_ = current->nextOpenStream;
paInternalInfo_.firstOpenStream_ = current->nextOpenStream;
}
else
{
@ -291,8 +302,8 @@ static void CloseOpenStreams( void )
/* we call Pa_CloseStream() here to ensure that the same destruction
logic is used for automatically closed streams */
while( firstOpenStream_ != NULL )
Pa_CloseStream( firstOpenStream_ );
while( paInternalInfo_.firstOpenStream_ != NULL )
Pa_CloseStream( paInternalInfo_.firstOpenStream_ );
}
@ -304,7 +315,7 @@ PaError Pa_Initialize( void )
if( PA_IS_INITIALISED_ )
{
++initializationCount_;
++paInternalInfo_.initializationCount_;
result = paNoError;
}
else
@ -315,9 +326,12 @@ PaError Pa_Initialize( void )
PaUtil_InitializeClock();
PaUtil_ResetTraceMessages();
/* Initialize hot plug here, so all its internal info is setup */
PaUtil_InitializeHotPlug();
result = InitializeHostApis();
if( result == paNoError )
++initializationCount_;
++paInternalInfo_.initializationCount_;
}
PA_LOGAPI_EXIT_PAERROR( "Pa_Initialize", result );
@ -334,12 +348,14 @@ PaError Pa_Terminate( void )
if( PA_IS_INITIALISED_ )
{
if( --initializationCount_ == 0 )
if( --paInternalInfo_.initializationCount_ == 0 )
{
CloseOpenStreams();
TerminateHostApis();
PaUtil_TerminateHotPlug();
PaUtil_DumpTraceMessages();
}
result = paNoError;
@ -425,9 +441,9 @@ PaHostApiIndex Pa_HostApiTypeIdToHostApiIndex( PaHostApiTypeId type )
{
result = paHostApiNotFound;
for( i=0; i < hostApisCount_; ++i )
for( i=0; i < paInternalInfo_.hostApisCount_; ++i )
{
if( hostApis_[i]->info.type == type )
if( paInternalInfo_.hostApis_[i]->info.type == type )
{
result = i;
break;
@ -455,11 +471,11 @@ PaError PaUtil_GetHostApiRepresentation( struct PaUtilHostApiRepresentation **ho
{
result = paHostApiNotFound;
for( i=0; i < hostApisCount_; ++i )
for( i=0; i < paInternalInfo_.hostApisCount_; ++i )
{
if( hostApis_[i]->info.type == type )
if( paInternalInfo_.hostApis_[i]->info.type == type )
{
*hostApi = hostApis_[i];
*hostApi = paInternalInfo_.hostApis_[i];
result = paNoError;
break;
}
@ -504,7 +520,7 @@ PaHostApiIndex Pa_GetHostApiCount( void )
}
else
{
result = hostApisCount_;
result = paInternalInfo_.hostApisCount_;
}
PA_LOGAPI_EXIT_PAERROR_OR_T_RESULT( "Pa_GetHostApiCount", "PaHostApiIndex: %d", result );
@ -530,7 +546,7 @@ PaHostApiIndex Pa_GetDefaultHostApi( void )
/* internal consistency check: make sure that the default host api
index is within range */
if( result < 0 || result >= hostApisCount_ )
if( result < 0 || result >= paInternalInfo_.hostApisCount_ )
{
result = paInternalError;
}
@ -557,7 +573,7 @@ const PaHostApiInfo* Pa_GetHostApiInfo( PaHostApiIndex hostApi )
PA_LOGAPI(("\tPaHostApiInfo*: NULL [ PortAudio not initialized ]\n" ));
}
else if( hostApi < 0 || hostApi >= hostApisCount_ )
else if( hostApi < 0 || hostApi >= paInternalInfo_.hostApisCount_ )
{
info = NULL;
@ -567,7 +583,7 @@ const PaHostApiInfo* Pa_GetHostApiInfo( PaHostApiIndex hostApi )
}
else
{
info = &hostApis_[hostApi]->info;
info = &paInternalInfo_.hostApis_[hostApi]->info;
PA_LOGAPI(("Pa_GetHostApiInfo returned:\n" ));
PA_LOGAPI(("\tPaHostApiInfo*: 0x%p\n", info ));
@ -597,20 +613,20 @@ PaDeviceIndex Pa_HostApiDeviceIndexToDeviceIndex( PaHostApiIndex hostApi, int ho
}
else
{
if( hostApi < 0 || hostApi >= hostApisCount_ )
if( hostApi < 0 || hostApi >= paInternalInfo_.hostApisCount_ )
{
result = paInvalidHostApi;
}
else
{
if( hostApiDeviceIndex < 0 ||
hostApiDeviceIndex >= hostApis_[hostApi]->info.deviceCount )
hostApiDeviceIndex >= paInternalInfo_.hostApis_[hostApi]->info.deviceCount )
{
result = paInvalidDevice;
}
else
{
result = hostApis_[hostApi]->privatePaFrontInfo.baseDeviceIndex + hostApiDeviceIndex;
result = paInternalInfo_.hostApis_[hostApi]->privatePaFrontInfo.baseDeviceIndex + hostApiDeviceIndex;
}
}
}
@ -633,7 +649,7 @@ PaDeviceIndex Pa_GetDeviceCount( void )
}
else
{
result = deviceCount_;
result = paInternalInfo_.deviceCount_;
}
PA_LOGAPI_EXIT_PAERROR_OR_T_RESULT( "Pa_GetDeviceCount", "PaDeviceIndex: %d", result );
@ -656,7 +672,7 @@ PaDeviceIndex Pa_GetDefaultInputDevice( void )
}
else
{
result = hostApis_[hostApi]->info.defaultInputDevice;
result = paInternalInfo_.hostApis_[hostApi]->info.defaultInputDevice;
}
PA_LOGAPI_EXIT_T( "Pa_GetDefaultInputDevice", "PaDeviceIndex: %d", result );
@ -679,7 +695,7 @@ PaDeviceIndex Pa_GetDefaultOutputDevice( void )
}
else
{
result = hostApis_[hostApi]->info.defaultOutputDevice;
result = paInternalInfo_.hostApis_[hostApi]->info.defaultOutputDevice;
}
PA_LOGAPI_EXIT_T( "Pa_GetDefaultOutputDevice", "PaDeviceIndex: %d", result );
@ -688,6 +704,116 @@ PaDeviceIndex Pa_GetDefaultOutputDevice( void )
}
PaError Pa_UpdateAvailableDeviceList( void )
{
PaError result = paNoError;
void **scanResults = NULL;
int *deviceCounts = NULL;
int i = 0;
PA_LOGAPI_ENTER( "Pa_UpdateAvailableDeviceList" );
if( !PA_IS_INITIALISED_ )
{
result = paNotInitialized;
goto done;
}
/* Allocate data structures used in 2-stage commit */
scanResults = (void **) PaUtil_AllocateMemory( sizeof(void*) * paInternalInfo_.hostApisCount_ );
if( !scanResults )
{
result = paInsufficientMemory;
goto done;
}
deviceCounts = ( int * ) PaUtil_AllocateMemory( sizeof( int ) * paInternalInfo_.hostApisCount_ );
if( !deviceCounts )
{
result = paInsufficientMemory;
goto done;
}
/* Phase 1: Perform a scan of new devices */
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;
if( hostApi->ScanDeviceInfos( hostApi, i, &scanResults[ i ], &deviceCounts[ i ] ) != paNoError )
break;
}
/* Check the result of the scan operation */
if( i < paInternalInfo_.hostApisCount_ )
{
/* If failure, rollback the scan changes back to original state */
int j = 0;
for( j = 0 ; j < i ; ++j )
{
PaUtilHostApiRepresentation *hostApi = paInternalInfo_.hostApis_[j];
if( hostApi->DisposeDeviceInfos == NULL )
continue;
PA_DEBUG(( "Performing rollback for device list scan for host api %d.\n",i));
hostApi->DisposeDeviceInfos( hostApi, scanResults[ j ], deviceCounts[ j ] );
}
}
else
{
int baseDeviceIndex = 0;
paInternalInfo_.deviceCount_ = 0;
/* Otherwise, commit the scan changes to each back-end */
for( i = 0 ; i < paInternalInfo_.hostApisCount_ ; ++i )
{
PaUtilHostApiRepresentation *hostApi = paInternalInfo_.hostApis_[i];
if( hostApi->CommitDeviceInfos == NULL )
{
/* Not yet implemented for this backend. Just
assume that the baseDeviceIndex and the paInternalInfo_.deviceCount_ are
incremented according to the values in the info */
baseDeviceIndex += hostApi->info.deviceCount;
paInternalInfo_.deviceCount_ += hostApi->info.deviceCount;
continue;
}
PA_DEBUG(( "Committing device list scan for host api %d.\n",i));
if( hostApi->CommitDeviceInfos( hostApi, i, scanResults[ i ], deviceCounts[ i ] ) != paNoError )
{
PA_DEBUG(( "Committing failed (shouldn't happen) %d.\n",i));
result = paInternalError;
goto done;
}
assert( hostApi->info.defaultInputDevice < hostApi->info.deviceCount );
assert( hostApi->info.defaultOutputDevice < hostApi->info.deviceCount );
hostApi->privatePaFrontInfo.baseDeviceIndex = baseDeviceIndex;
if( hostApi->info.defaultInputDevice != paNoDevice )
hostApi->info.defaultInputDevice += baseDeviceIndex;
if( hostApi->info.defaultOutputDevice != paNoDevice )
hostApi->info.defaultOutputDevice += baseDeviceIndex;
baseDeviceIndex += hostApi->info.deviceCount;
paInternalInfo_.deviceCount_ += hostApi->info.deviceCount;
}
}
done:
if( scanResults )
PaUtil_FreeMemory( scanResults );
if( deviceCounts )
PaUtil_FreeMemory( deviceCounts );
return result;
}
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device )
{
int hostSpecificDeviceIndex;
@ -708,7 +834,7 @@ const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device )
}
else
{
result = hostApis_[hostApiIndex]->deviceInfos[ hostSpecificDeviceIndex ];
result = paInternalInfo_.hostApis_[hostApiIndex]->deviceInfos[ hostSpecificDeviceIndex ];
PA_LOGAPI(("Pa_GetDeviceInfo returned:\n" ));
PA_LOGAPI(("\tPaDeviceInfo*: 0x%p:\n", result ));
@ -850,7 +976,7 @@ static PaError ValidateOpenStreamParameters(
if( inputHostApiIndex != -1 )
{
*hostApiInputDevice = paUseHostApiSpecificDeviceSpecification;
*hostApi = hostApis_[inputHostApiIndex];
*hostApi = paInternalInfo_.hostApis_[inputHostApiIndex];
}
else
{
@ -864,14 +990,14 @@ static PaError ValidateOpenStreamParameters(
}
else
{
if( inputParameters->device < 0 || inputParameters->device >= deviceCount_ )
if( inputParameters->device < 0 || inputParameters->device >= paInternalInfo_.deviceCount_ )
return paInvalidDevice;
inputHostApiIndex = FindHostApi( inputParameters->device, hostApiInputDevice );
if( inputHostApiIndex < 0 )
return paInternalError;
*hostApi = hostApis_[inputHostApiIndex];
*hostApi = paInternalInfo_.hostApis_[inputHostApiIndex];
if( inputParameters->channelCount <= 0 )
return paInvalidChannelCount;
@ -901,7 +1027,7 @@ static PaError ValidateOpenStreamParameters(
if( outputHostApiIndex != -1 )
{
*hostApiOutputDevice = paUseHostApiSpecificDeviceSpecification;
*hostApi = hostApis_[outputHostApiIndex];
*hostApi = paInternalInfo_.hostApis_[outputHostApiIndex];
}
else
{
@ -915,14 +1041,14 @@ static PaError ValidateOpenStreamParameters(
}
else
{
if( outputParameters->device < 0 || outputParameters->device >= deviceCount_ )
if( outputParameters->device < 0 || outputParameters->device >= paInternalInfo_.deviceCount_ )
return paInvalidDevice;
outputHostApiIndex = FindHostApi( outputParameters->device, hostApiOutputDevice );
if( outputHostApiIndex < 0 )
return paInternalError;
*hostApi = hostApis_[outputHostApiIndex];
*hostApi = paInternalInfo_.hostApis_[outputHostApiIndex];
if( outputParameters->channelCount <= 0 )
return paInvalidChannelCount;
@ -1751,3 +1877,29 @@ PaError Pa_GetSampleSize( PaSampleFormat format )
return (PaError) result;
}
extern void PaUtil_LockHotPlug();
extern void PaUtil_UnlockHotPlug();
PaError Pa_SetDevicesChangedCallback( void *userData, PaStreamFinishedCallback* devicesChangedCallback )
{
PaUtil_LockHotPlug();
paInternalInfo_.devicesChangedCallback_ = devicesChangedCallback;
paInternalInfo_.devicesChangedCallbackUserData_ = userData;
PaUtil_UnlockHotPlug();
return paNoError;
}
/* Called whenever a OS audio device change has been detected */
void PaUtil_DevicesChanged(unsigned state, void* pData)
{
(void)state;
(void)pData;
PaUtil_LockHotPlug();
if (paInternalInfo_.devicesChangedCallback_)
{
(paInternalInfo_.devicesChangedCallback_)(paInternalInfo_.devicesChangedCallbackUserData_);
}
PaUtil_UnlockHotPlug();
}

View file

@ -1,7 +1,7 @@
#ifndef PA_HOSTAPI_H
#define PA_HOSTAPI_H
/*
* $Id$
* $Id: pa_hostapi.h 1664 2011-05-01 16:10:58Z rossb $
* Portable Audio I/O Library
* host api representation
*
@ -275,6 +275,37 @@ typedef struct PaUtilHostApiRepresentation {
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
double sampleRate );
/**
Collect current native device information and compute a new device count.
This method should not result in the host API making any changes to its
internal data structures, instead it is the first step in a two-stage commit
process which will be completed only if all host API scans return with no errors.
Some time later the caller will either (1) pass scanResults and newDeviceCount
to CommitDeviceInfos() to complete the transaction, or (2) pass scanResults
and newDeviceCount to DisposeDeviceInfos() to abort the transaction.
*/
PaError (*ScanDeviceInfos)( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void **scanResults, int *newDeviceCount );
/**
Replace the previous device information with the device information
passed in as an argument via scanResults. This is the second/final stage of
the two-stage transaction required for updating the available device information.
This function should free any resources associated with the previous device
information.
@FIXME: this function should probably not return an error code as it should not be
permitted to fail.
*/
PaError (*CommitDeviceInfos)( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void *scanResults, int deviceCount );
/**
Free device information previously returned by ScanDeviceInfos(). This function
will be called in cases where the update available device info transaction
failed for some reason.
*/
PaError (*DisposeDeviceInfos)( struct PaUtilHostApiRepresentation *hostApi, void *scanResults, int deviceCount );
} PaUtilHostApiRepresentation;

View file

@ -1,5 +1,5 @@
/*
* $Id$
* $Id: pa_process.c 1661 2011-04-28 18:54:46Z rob_bielik $
* Portable Audio I/O Library
* streamCallback <-> host buffer processing adapter
*

View file

@ -1,5 +1,5 @@
/*
* $Id$
* $Id: pa_linux_alsa.c 1661 2011-04-28 18:54:46Z rob_bielik $
* PortAudio Portable Real-Time Audio Library
* Latest Version at: http://www.portaudio.com
* ALSA implementation by Joshua Haberman and Arve Knudsen
@ -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 );*/
}
}
@ -4327,8 +4394,23 @@ static PaError ReadStream( PaStream* s, void *buffer, unsigned long frames )
{
int xrun = 0;
PA_ENSURE( PaAlsaStream_WaitForFrames( stream, &framesAvail, &xrun ) );
framesGot = PA_MIN( framesAvail, frames );
/*
* 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;
}

View file

@ -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;

View file

@ -67,6 +67,8 @@
#include <string.h> /* strlen(), memcmp() etc. */
#include <libkern/OSAtomic.h>
#include <CoreFoundation/CFString.h>
#include "pa_mac_core.h"
#include "pa_mac_core_utilities.h"
#include "pa_mac_core_blocking.h"
@ -80,6 +82,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 +155,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 +333,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; i<auhalHostApi->devCount; ++i )
printf( "Device %d\t: %ld\n", i, auhalHostApi->devIds[i] );
for( i=0; i<outArgument->devCount; ++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 +391,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; i<auhalHostApi->devCount; ++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; i<auhalHostApi->devCount; ++i ) {
for( i=0; i<outArgument->devCount; ++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 +437,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;
@ -488,9 +523,111 @@ static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi,
memset(deviceInfo, 0, sizeof(deviceInfo));
deviceInfo->structVersion = 2;
deviceInfo->structVersion = 3;
deviceInfo->hostApi = hostApiIndex;
// target device UID property
unsigned long deviceUID = 0;
AudioObjectPropertyAddress addr;
addr.mSelector = kAudioDevicePropertyDeviceUID;
addr.mScope = kAudioObjectPropertyScopeGlobal;
addr.mElement = kAudioObjectPropertyElementMaster;
CFStringRef deviceUIDString;
propSize = sizeof(deviceUIDString);
err = ERR(AudioObjectGetPropertyData(
macCoreDeviceId,
&addr,
0,
NULL,
&propSize,
&deviceUIDString));
if (err)
return err;
CFIndex deviceUIDLength = CFStringGetLength(deviceUIDString) + 1;
char *ASCIIDeviceUID
= PaUtil_GroupAllocateMemory(auhalHostApi->allocations,deviceUIDLength);
if ( !ASCIIDeviceUID )
return paInsufficientMemory;
deviceInfo->deviceUID = NULL;
if(CFStringGetCString (
deviceUIDString,
ASCIIDeviceUID,
deviceUIDLength,
kCFStringEncodingASCII))
{
deviceInfo->deviceUID = ASCIIDeviceUID;
}
// target device transport type property
addr.mSelector = kAudioDevicePropertyTransportType;
addr.mScope = kAudioObjectPropertyScopeGlobal;
addr.mElement = kAudioObjectPropertyElementMaster;
unsigned int transportType = 0;
propSize = sizeof(transportType);
err = ERR(AudioObjectGetPropertyData(
macCoreDeviceId,
&addr,
0,
NULL,
&propSize,
&transportType));
if (err)
return err;
switch(transportType)
{
case kAudioDeviceTransportTypeAggregate:
deviceInfo->transportType = "Aggregate";
break;
case kAudioDeviceTransportTypeAirPlay:
deviceInfo->transportType = "AirPlay";
break;
case kAudioDeviceTransportTypeAutoAggregate:
deviceInfo->transportType = "Auto aggregate";
break;
case kAudioDeviceTransportTypeAVB:
deviceInfo->transportType = "AVB";
break;
case kAudioDeviceTransportTypeBluetooth:
deviceInfo->transportType = "Bluetooth";
break;
case kAudioDeviceTransportTypeBuiltIn:
deviceInfo->transportType = "Built-in";
break;
case kAudioDeviceTransportTypeDisplayPort:
deviceInfo->transportType = "DisplayPort";
break;
case kAudioDeviceTransportTypeFireWire:
deviceInfo->transportType = "FireWire";
break;
case kAudioDeviceTransportTypeHDMI:
deviceInfo->transportType = "HDMI";
break;
case kAudioDeviceTransportTypePCI:
deviceInfo->transportType = "PCI";
break;
case kAudioDeviceTransportTypeThunderbolt:
deviceInfo->transportType = "Thunderbolt";
break;
case kAudioDeviceTransportTypeUnknown:
deviceInfo->transportType = "Unknown";
break;
case kAudioDeviceTransportTypeUSB:
deviceInfo->transportType = "USB";
break;
case kAudioDeviceTransportTypeVirtual:
deviceInfo->transportType = "Virtual";
break;
default:
deviceInfo->transportType = NULL;
break;
}
/* Get the device name. Fail if we can't get it. */
err = ERR(AudioDeviceGetPropertyInfo(macCoreDeviceId, 0, 0, kAudioDevicePropertyDeviceName, &propSize, NULL));
if (err)
@ -528,10 +665,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 +709,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; j<auhalHostApi->devCount; ++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;
@ -2359,15 +2460,32 @@ static PaError StartStream( PaStream *s )
// it's not clear from appl's docs that this really waits
// until all data is flushed.
static ComponentResult BlockWhileAudioUnitIsRunning( AudioUnit audioUnit, AudioUnitElement element )
static ComponentResult BlockWhileAudioUnitIsRunning(
AudioUnit audioUnit,
AudioUnitElement element)
{
long waitTime = 100;
// If PaUtil_GetRingBufferWriteAvailable starts repetitively and
// consecutively returning that there is no data available, do eventually
// give up in order to allow the caller to handle such cases.
long totalTimeout = 0;
Boolean isRunning = 1;
while( isRunning ) {
UInt32 s = sizeof( isRunning );
ComponentResult err = AudioUnitGetProperty( audioUnit, kAudioOutputUnitProperty_IsRunning, kAudioUnitScope_Global, element, &isRunning, &s );
if( err )
return err;
Pa_Sleep( 100 );
Pa_Sleep( waitTime );
// If a timeout is encountered, continue. However, testing has
// shown that it is possible to unplug a device and to wait here
// forever. In order to allow the caller to handle such cases of
// repeated timeouts, do eventually given up.
totalTimeout += waitTime;
if( PA_COREAUDIO_MIN_TIMEOUT_MSEC_ <= totalTimeout)
{
return paTimedOut;
}
}
return noErr;
}
@ -2468,3 +2586,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; j<out->devCount; ++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;
}

View file

@ -415,6 +415,10 @@ PaError ReadStream( PaStream* stream,
while( frames > 0 ) {
long avail;
long toRead;
// If PaUtil_GetRingBufferReadAvailable starts repetitively and
// consecutively returning that there is no data available, do eventually
// give up in order to allow the caller to handle such cases.
long totalTimeout = 0;
do {
avail = PaUtil_GetRingBufferReadAvailable( &blio->inputRingBuffer );
/*
@ -438,6 +442,16 @@ PaError ReadStream( PaStream* stream,
return ret;
#else
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
// If a timeout is encountered, continue. However, testing has
// shown that it is possible to unplug a device and to wait here
// forever. In order to allow the caller to handle such cases of
// repeated timeouts, do eventually given up.
totalTimeout += PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL;
if( PA_COREAUDIO_MAX_TIMEOUT_MSEC_ <= totalTimeout)
{
return paTimedOut;
}
#endif
}
} while( avail == 0 );
@ -491,6 +505,10 @@ PaError WriteStream( PaStream* stream,
while( frames > 0 ) {
long avail = 0;
long toWrite;
// If PaUtil_GetRingBufferWriteAvailable starts repetitively and
// consecutively returning that there is no data available, do eventually
// give up in order to allow the caller to handle such cases.
long totalTimeout = 0;
do {
avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
@ -515,6 +533,16 @@ PaError WriteStream( PaStream* stream,
return ret;
#else
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
// If a timeout is encountered, continue. However, testing has
// shown that it is possible to unplug a device and to wait here
// forever. In order to allow the caller to handle such cases of
// repeated timeouts, do eventually given up.
totalTimeout += PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL;
if( PA_COREAUDIO_MAX_TIMEOUT_MSEC_ <= totalTimeout)
{
return paTimedOut;
}
#endif
}
} while( avail == 0 );
@ -561,13 +589,30 @@ PaError WriteStream( PaStream* stream,
void waitUntilBlioWriteBufferIsFlushed( PaMacBlio *blio )
{
if( blio->outputRingBuffer.buffer ) {
// If PaUtil_GetRingBufferWriteAvailable starts repetitively and
// consecutively returning that there is no data available, do eventually
// give up in order to allow the caller to handle such cases.
long totalTimeout = 0;
long avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
while( avail != blio->outputRingBuffer.bufferSize ) {
if( avail == 0 )
{
Pa_Sleep( PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL );
// If a timeout is encountered, continue. However, testing has
// shown that it is possible to unplug a device and to wait here
// forever. In order to allow the caller to handle such cases of
// repeated timeouts, do eventually given up.
totalTimeout += PA_MAC_BLIO_BUSY_WAIT_SLEEP_INTERVAL;
if( PA_COREAUDIO_MIN_TIMEOUT_MSEC_ <= totalTimeout)
{
return paTimedOut;
}
}
avail = PaUtil_GetRingBufferWriteAvailable( &blio->outputRingBuffer );
}
}
return paNoError;
}

View file

@ -76,6 +76,9 @@
#define PA_MAC_BLIO_MUTEX
*/
#define PA_COREAUDIO_MIN_TIMEOUT_MSEC_ (1000)
#define PA_COREAUDIO_MAX_TIMEOUT_MSEC_ (2000)
typedef struct {
PaUtilRingBuffer inputRingBuffer;
PaUtilRingBuffer outputRingBuffer;

View file

@ -1,5 +1,5 @@
/*
* $Id$
* $Id: pa_win_ds.c 1672 2011-05-04 20:11:30Z rossb $
* Portable Audio I/O Library DirectSound implementation
*
* Authors: Phil Burk, Robert Marsanyi & Ross Bencina
@ -75,10 +75,6 @@
#include "pa_win_waveformat.h"
#include "pa_win_wdmks_utils.h"
#ifndef PA_USE_WMME
#error "Portaudio internal error: PA_USE_WMME=0/1 not defined. pa_hostapi.h should ensure that it is."
#endif
#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
#pragma comment( lib, "dsound.lib" )
#pragma comment( lib, "winmm.lib" )
@ -141,6 +137,9 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
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 );
static PaError CloseStream( PaStream* stream );
static PaError StartStream( PaStream *stream );
static PaError StopStream( PaStream *stream );
@ -249,6 +248,12 @@ typedef struct PaWinDsStream
volatile int abortProcessing; /* stop thread immediately */
} PaWinDsStream;
typedef struct PaWinDsScanDeviceInfosResults{ /* used for tranferring device infos during scanning / rescanning */
PaDeviceInfo **deviceInfos;
PaDeviceIndex defaultInputDevice;
PaDeviceIndex defaultOutputDevice;
} PaWinDsScanDeviceInfosResults;
/************************************************************************************
** Duplicate the input string using the allocations allocator.
@ -293,7 +298,7 @@ typedef struct DSDeviceNameAndGUIDVector{
int count;
int free;
DSDeviceNameAndGUID *items; // Allocated using LocalAlloc()
DSDeviceNameAndGUID *items; /* Allocated using LocalAlloc() */
} DSDeviceNameAndGUIDVector;
typedef struct DSDeviceNamesAndGUIDs{
@ -512,7 +517,6 @@ static GUID pawin_IID_IKsPropertySet =
static void FindDevicePnpInterfaces( DSDeviceNamesAndGUIDs *deviceNamesAndGUIDs )
{
IClassFactory *pClassFactory;
if( paWinDsDSoundEntryPoints.DllGetClassObject(&pawin_CLSID_DirectSoundPrivate, &IID_IClassFactory, (PVOID *) &pClassFactory) == S_OK ){
IKsPropertySet *pPropertySet;
if( pClassFactory->lpVtbl->CreateInstance( pClassFactory, NULL, &pawin_IID_IKsPropertySet, (PVOID *) &pPropertySet) == S_OK ){
@ -590,28 +594,15 @@ static double defaultSampleRateSearchOrder_[] =
** The device will not be added to the device list if any errors are encountered.
*/
static PaError AddOutputDeviceInfoFromDirectSound(
PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
PaWinDsDeviceInfo *winDsDeviceInfo, char *name, LPGUID lpGUID, char *pnpInterface, PaUtilAllocationGroup *allocations )
{
PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
HRESULT hr;
LPDIRECTSOUND lpDirectSound;
LPDIRECTSOUND lpDirectSound = NULL;
DSCAPS caps;
int deviceOK = TRUE;
PaError result = paNoError;
int i;
/* Copy GUID to the device info structure. Set pointer. */
if( lpGUID == NULL )
{
winDsDeviceInfo->lpGUID = NULL;
}
else
{
memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
}
if( lpGUID )
{
@ -619,7 +610,7 @@ static PaError AddOutputDeviceInfoFromDirectSound(
IsEqualGUID (&IID_IRolandVSCEmulated2,lpGUID) )
{
PA_DEBUG(("BLACKLISTED: %s \n",name));
return paNoError;
return paInvalidDevice;
}
}
@ -627,23 +618,6 @@ static PaError AddOutputDeviceInfoFromDirectSound(
Note that using CoCreateInstance doesn't work on windows CE.
*/
hr = paWinDsDSoundEntryPoints.DirectSoundCreate( lpGUID, &lpDirectSound, NULL );
/** try using CoCreateInstance because DirectSoundCreate was hanging under
some circumstances - note this was probably related to the
#define BOOL short bug which has now been fixed
@todo delete this comment and the following code once we've ensured
there is no bug.
*/
/*
hr = CoCreateInstance( &CLSID_DirectSound, NULL, CLSCTX_INPROC_SERVER,
&IID_IDirectSound, (void**)&lpDirectSound );
if( hr == S_OK )
{
hr = IDirectSound_Initialize( lpDirectSound, lpGUID );
}
*/
if( hr != DS_OK )
{
if (hr == DSERR_ALLOCATED)
@ -664,7 +638,8 @@ static PaError AddOutputDeviceInfoFromDirectSound(
lpGUID->Data4[6],
lpGUID->Data4[7]));
deviceOK = FALSE;
result = paUnanticipatedHostError;
goto error;
}
else
{
@ -675,7 +650,9 @@ static PaError AddOutputDeviceInfoFromDirectSound(
if( hr != DS_OK )
{
DBUG(("Cannot GetCaps() for DirectSound device %s. Result = 0x%x\n", name, hr ));
deviceOK = FALSE;
result = paUnanticipatedHostError;
goto error;
}
else
{
@ -684,12 +661,11 @@ static PaError AddOutputDeviceInfoFromDirectSound(
if( caps.dwFlags & DSCAPS_EMULDRIVER )
{
/* If WMME supported, then reject Emulated drivers because they are lousy. */
deviceOK = FALSE;
result = paInvalidDevice;
goto error;
}
#endif
if( deviceOK )
{
deviceInfo->maxInputChannels = 0;
winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
@ -817,20 +793,39 @@ static PaError AddOutputDeviceInfoFromDirectSound(
//printf( "min %d max %d\n", caps.dwMinSecondarySampleRate, caps.dwMaxSecondarySampleRate );
// dwFlags | DSCAPS_CONTINUOUSRATE
}
}
IDirectSound_Release( lpDirectSound );
}
if( deviceOK )
/* Copy GUID to the device info structure. Set pointer. */
if( lpGUID == NULL )
{
winDsDeviceInfo->lpGUID = NULL;
}
else
{
// target device UID property
WCHAR duid[39];
char *deviceUID;
deviceUID = (char *)PaUtil_GroupAllocateMemory(allocations, 39);
StringFromGUID2(&(*lpGUID), (LPOLESTR) &duid, 39);
wcstombs(deviceUID, duid, sizeof(deviceUID));
WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
duid, -1, deviceUID, 39, NULL, NULL );
deviceInfo->deviceUID = deviceUID;
memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
}
deviceInfo->name = name;
if( lpGUID == NULL )
hostApi->info.defaultOutputDevice = hostApi->info.deviceCount;
return result;
hostApi->info.deviceCount++;
}
error:
if( lpDirectSound )
IDirectSound_Release( lpDirectSound );
return result;
}
@ -844,44 +839,20 @@ static PaError AddOutputDeviceInfoFromDirectSound(
** The device will not be added to the device list if any errors are encountered.
*/
static PaError AddInputDeviceInfoFromDirectSoundCapture(
PaWinDsHostApiRepresentation *winDsHostApi, char *name, LPGUID lpGUID, char *pnpInterface )
PaWinDsDeviceInfo *winDsDeviceInfo, char *name, LPGUID lpGUID, char *pnpInterface, PaUtilAllocationGroup *allocations )
{
PaUtilHostApiRepresentation *hostApi = &winDsHostApi->inheritedHostApiRep;
PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*) hostApi->deviceInfos[hostApi->info.deviceCount];
PaDeviceInfo *deviceInfo = &winDsDeviceInfo->inheritedDeviceInfo;
HRESULT hr;
LPDIRECTSOUNDCAPTURE lpDirectSoundCapture;
LPDIRECTSOUNDCAPTURE lpDirectSoundCapture = NULL;
DSCCAPS caps;
int deviceOK = TRUE;
PaError result = paNoError;
/* Copy GUID to the device info structure. Set pointer. */
if( lpGUID == NULL )
{
winDsDeviceInfo->lpGUID = NULL;
}
else
{
winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
}
hr = paWinDsDSoundEntryPoints.DirectSoundCaptureCreate( lpGUID, &lpDirectSoundCapture, NULL );
/** try using CoCreateInstance because DirectSoundCreate was hanging under
some circumstances - note this was probably related to the
#define BOOL short bug which has now been fixed
@todo delete this comment and the following code once we've ensured
there is no bug.
*/
/*
hr = CoCreateInstance( &CLSID_DirectSoundCapture, NULL, CLSCTX_INPROC_SERVER,
&IID_IDirectSoundCapture, (void**)&lpDirectSoundCapture );
*/
if( hr != DS_OK )
{
DBUG(("Cannot create Capture for %s. Result = 0x%x\n", name, hr ));
deviceOK = FALSE;
result = paUnanticipatedHostError;
goto error;
}
else
{
@ -892,7 +863,8 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
if( hr != DS_OK )
{
DBUG(("Cannot GetCaps() for Capture device %s. Result = 0x%x\n", name, hr ));
deviceOK = FALSE;
result = paUnanticipatedHostError;
goto error;
}
else
{
@ -900,12 +872,11 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
if( caps.dwFlags & DSCAPS_EMULDRIVER )
{
/* If WMME supported, then reject Emulated drivers because they are lousy. */
deviceOK = FALSE;
result = paInvalidDevice;
goto error;
}
#endif
if( deviceOK )
{
deviceInfo->maxInputChannels = caps.dwChannels;
winDsDeviceInfo->deviceInputChannelCountIsKnown = 1;
@ -989,20 +960,40 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
}
else deviceInfo->defaultSampleRate = 0.;
}
}
IDirectSoundCapture_Release( lpDirectSoundCapture );
}
if( deviceOK )
/* Copy GUID to the device info structure. Set pointer. */
if( lpGUID == NULL )
{
winDsDeviceInfo->lpGUID = NULL;
}
else
{
// target device UID property
WCHAR duid[39];
char *deviceUID;
deviceUID = (char *)PaUtil_GroupAllocateMemory(allocations, 39);
StringFromGUID2(&(*lpGUID), (LPOLESTR) &duid, 39);
wcstombs(deviceUID, duid, sizeof(deviceUID));
WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
duid, -1, deviceUID, 39, NULL, NULL );
deviceInfo->deviceUID = deviceUID;
winDsDeviceInfo->lpGUID = &winDsDeviceInfo->guid;
memcpy( &winDsDeviceInfo->guid, lpGUID, sizeof(GUID) );
}
deviceInfo->name = name;
if( lpGUID == NULL )
hostApi->info.defaultInputDevice = hostApi->info.deviceCount;
return result;
hostApi->info.deviceCount++;
}
error:
if( lpDirectSoundCapture )
IDirectSoundCapture_Release( lpDirectSoundCapture );
return result;
}
@ -1012,12 +1003,10 @@ static PaError AddInputDeviceInfoFromDirectSoundCapture(
PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
{
PaError result = paNoError;
int i, deviceCount;
int deviceCount;
PaWinDsHostApiRepresentation *winDsHostApi;
DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
PaWinDsDeviceInfo *deviceInfoArray;
char comWasInitialized = 0;
void *scanResults = 0;
/*
If COM is already initialized CoInitialize will either return
@ -1034,11 +1023,6 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
if( hr != RPC_E_CHANGED_MODE )
comWasInitialized = 1;
/* initialise guid vectors so they can be safely deleted on error */
deviceNamesAndGUIDs.winDsHostApi = NULL;
deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
PaWinDs_InitializeDSoundEntryPoints();
winDsHostApi = (PaWinDsHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinDsHostApiRepresentation) );
@ -1062,109 +1046,25 @@ PaError PaWinDs_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInde
(*hostApi)->info.type = paDirectSound;
(*hostApi)->info.name = "Windows DirectSound";
/* these are all updated by CommitDeviceInfos() */
(*hostApi)->info.deviceCount = 0;
(*hostApi)->info.defaultInputDevice = paNoDevice;
(*hostApi)->info.defaultOutputDevice = paNoDevice;
(*hostApi)->deviceInfos = 0;
/* DSound - enumerate devices to count them and to gather their GUIDs */
result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
if( result != paNoError )
goto error;
result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
if( result != paNoError )
goto error;
paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
paWinDsDSoundEntryPoints.DirectSoundEnumerateA( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError != paNoError )
{
result = deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError;
goto error;
}
if( deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError != paNoError )
{
result = deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError;
goto error;
}
deviceCount = deviceNamesAndGUIDs.inputNamesAndGUIDs.count + deviceNamesAndGUIDs.outputNamesAndGUIDs.count;
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
if( deviceCount > 0 )
{
deviceNamesAndGUIDs.winDsHostApi = winDsHostApi;
FindDevicePnpInterfaces( &deviceNamesAndGUIDs );
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
if( deviceCount > 0 )
{
/* allocate array for pointers to PaDeviceInfo structs */
(*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
winDsHostApi->allocations, sizeof(PaDeviceInfo*) * deviceCount );
if( !(*hostApi)->deviceInfos )
{
result = paInsufficientMemory;
goto error;
}
/* allocate all PaDeviceInfo structs in a contiguous block */
deviceInfoArray = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * deviceCount );
if( !deviceInfoArray )
{
result = paInsufficientMemory;
goto error;
}
for( i=0; i < deviceCount; ++i )
{
PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
deviceInfo->structVersion = 2;
deviceInfo->hostApi = hostApiIndex;
deviceInfo->name = 0;
(*hostApi)->deviceInfos[i] = deviceInfo;
}
for( i=0; i < deviceNamesAndGUIDs.inputNamesAndGUIDs.count; ++i )
{
result = AddInputDeviceInfoFromDirectSoundCapture( winDsHostApi,
deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].name,
deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID,
deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].pnpInterface );
if( result != paNoError )
goto error;
}
for( i=0; i < deviceNamesAndGUIDs.outputNamesAndGUIDs.count; ++i )
{
result = AddOutputDeviceInfoFromDirectSound( winDsHostApi,
deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].name,
deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID,
deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].pnpInterface );
if( result != paNoError )
goto error;
}
}
result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
if( result != paNoError )
goto error;
result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
result = ScanDeviceInfos( &winDsHostApi->inheritedHostApiRep, hostApiIndex, &scanResults, &deviceCount );
if( result != paNoError )
goto error;
/* FIXME for now we ignore the result of CommitDeviceInfos(), it should probably be an atomic non-failing operation */
CommitDeviceInfos( &winDsHostApi->inheritedHostApiRep, hostApiIndex, scanResults, deviceCount );
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
(*hostApi)->ScanDeviceInfos = ScanDeviceInfos;
(*hostApi)->CommitDeviceInfos = CommitDeviceInfos;
(*hostApi)->DisposeDeviceInfos = DisposeDeviceInfos;
PaUtil_InitializeStreamInterface( &winDsHostApi->callbackStreamInterface, CloseStream, StartStream,
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
@ -1191,8 +1091,7 @@ error:
PaUtil_FreeMemory( winDsHostApi );
}
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( comWasInitialized )
CoUninitialize();
@ -1369,6 +1268,233 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
return paFormatIsSupported;
}
/***********************************************************************************/
static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex, void **scanResults, int *newDeviceCount )
{
PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
PaWinDsDeviceInfo *deviceInfoArray;
char comWasInitialized = winDsHostApi->comWasInitialized;
PaError result = paNoError;
PaWinDsScanDeviceInfosResults *outArgument = 0;
DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
int i = 0;
int maximumNewDeviceCount = 0;
/* Check preconditions */
if( !comWasInitialized || scanResults == NULL || newDeviceCount == NULL )
return paInternalError;
/* initialize the out params */
*scanResults = NULL;
*newDeviceCount = 0;
/* initialise guid vectors so they can be safely deleted on error */
deviceNamesAndGUIDs.winDsHostApi = NULL;
deviceNamesAndGUIDs.inputNamesAndGUIDs.items = NULL;
deviceNamesAndGUIDs.outputNamesAndGUIDs.items = NULL;
result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs, winDsHostApi->allocations );
if( result != paNoError )
goto error;
result = InitializeDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs, winDsHostApi->allocations );
if( result != paNoError )
goto error;
deviceNamesAndGUIDs.winDsHostApi = winDsHostApi;
/* DSound - enumerate devices to count them and to gather their GUIDs and device names */
paWinDsDSoundEntryPoints.DirectSoundCaptureEnumerateA( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.inputNamesAndGUIDs );
if( deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError != paNoError )
{
result = deviceNamesAndGUIDs.inputNamesAndGUIDs.enumerationError;
goto error;
}
paWinDsDSoundEntryPoints.DirectSoundEnumerateA( (LPDSENUMCALLBACK)CollectGUIDsProc, (void *)&deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError != paNoError )
{
result = deviceNamesAndGUIDs.outputNamesAndGUIDs.enumerationError;
goto error;
}
maximumNewDeviceCount = deviceNamesAndGUIDs.inputNamesAndGUIDs.count +
deviceNamesAndGUIDs.outputNamesAndGUIDs.count;
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
if( maximumNewDeviceCount > 0 )
{
FindDevicePnpInterfaces( &deviceNamesAndGUIDs );
}
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
if( maximumNewDeviceCount > 0 )
{
/* Allocate the out param for all the info we need */
outArgument = (PaWinDsScanDeviceInfosResults *) PaUtil_GroupAllocateMemory(
winDsHostApi->allocations, sizeof(PaWinDsScanDeviceInfosResults) );
if( !outArgument )
{
result = paInsufficientMemory;
goto error;
}
/* allocate array for pointers to PaDeviceInfo structs */
outArgument->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
winDsHostApi->allocations, sizeof(PaDeviceInfo*) * maximumNewDeviceCount );
if( !outArgument->deviceInfos )
{
result = paInsufficientMemory;
goto error;
}
/* allocate all PaDeviceInfo structs in a contiguous block */
deviceInfoArray = (PaWinDsDeviceInfo*)PaUtil_GroupAllocateMemory(
winDsHostApi->allocations, sizeof(PaWinDsDeviceInfo) * maximumNewDeviceCount );
if( !deviceInfoArray )
{
result = paInsufficientMemory;
goto error;
}
for( i = 0 ; i < maximumNewDeviceCount; ++i )
{
PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
deviceInfo->structVersion = 3;
deviceInfo->hostApi = hostApiIndex;
deviceInfo->name = 0;
outArgument->deviceInfos[ i ] = deviceInfo;
}
for( i = 0 ; i < deviceNamesAndGUIDs.inputNamesAndGUIDs.count ; ++i )
{
PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*)outArgument->deviceInfos[*newDeviceCount];
result = AddInputDeviceInfoFromDirectSoundCapture( winDsDeviceInfo,
deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].name,
deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID,
deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].pnpInterface,
winDsHostApi->allocations );
if( result == paNoError )
{
if( deviceNamesAndGUIDs.inputNamesAndGUIDs.items[i].lpGUID == NULL )
outArgument->defaultInputDevice = *newDeviceCount;
(*newDeviceCount)++;
}
/* ignore error results here and just skip the device */
}
for( i = 0 ; i < deviceNamesAndGUIDs.outputNamesAndGUIDs.count ; ++i )
{
PaWinDsDeviceInfo *winDsDeviceInfo = (PaWinDsDeviceInfo*)outArgument->deviceInfos[*newDeviceCount];
result = AddOutputDeviceInfoFromDirectSound( winDsDeviceInfo,
deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].name,
deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID,
deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].pnpInterface,
winDsHostApi->allocations );
if( result == paNoError )
{
if( deviceNamesAndGUIDs.outputNamesAndGUIDs.items[i].lpGUID == NULL )
outArgument->defaultOutputDevice = *newDeviceCount;
(*newDeviceCount)++;
}
/* ignore error results here and just skip the device */
}
}
result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
if( result != paNoError )
goto error;
result = TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( result != paNoError )
goto error;
*scanResults = outArgument;
return result;
error:
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.inputNamesAndGUIDs );
TerminateDSDeviceNameAndGUIDVector( &deviceNamesAndGUIDs.outputNamesAndGUIDs );
if( outArgument )
{
if( outArgument->deviceInfos )
{
if( outArgument->deviceInfos[0] )
{
PaUtil_GroupFreeMemory( winDsHostApi->allocations, outArgument->deviceInfos[0] );
}
PaUtil_GroupFreeMemory( winDsHostApi->allocations, outArgument->deviceInfos );
}
}
return result;
}
static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void *scanResults, int deviceCount )
{
PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
PaError result = paNoError;
int i = 0;
hostApi->info.deviceCount = 0;
hostApi->info.defaultInputDevice = paNoDevice;
hostApi->info.defaultOutputDevice = paNoDevice;
/* Free any old memory which might be in the device info */
if( hostApi->deviceInfos )
{
PaUtil_GroupFreeMemory( winDsHostApi->allocations, hostApi->deviceInfos[0] );
PaUtil_GroupFreeMemory( winDsHostApi->allocations, hostApi->deviceInfos );
hostApi->deviceInfos = NULL;
}
if( scanResults != NULL )
{
PaWinDsScanDeviceInfosResults *scanDeviceInfosResults = ( PaWinDsScanDeviceInfosResults * ) 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( winDsHostApi->allocations, scanDeviceInfosResults );
}
return result;
}
static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, void *scanResults, int deviceCount )
{
PaWinDsHostApiRepresentation *winDsHostApi = (PaWinDsHostApiRepresentation*)hostApi;
if( scanResults != NULL )
{
PaWinDsScanDeviceInfosResults *scanDeviceInfosResults = ( PaWinDsScanDeviceInfosResults * ) scanResults;
if( scanDeviceInfosResults->deviceInfos )
{
PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults->deviceInfos[0] ); /* all device info structs are allocated in a block so we can destroy them here */
PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults->deviceInfos );
}
PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults );
}
return paNoError;
}
/*************************************************************************
** Determine minimum number of buffers required for this host based

View file

@ -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;

View file

@ -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 ) );

View file

@ -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,

View file

@ -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;

View file

@ -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
@ -59,8 +59,12 @@
#ifdef __GNUC__
#include <initguid.h>
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif /* #ifndef _WIN32_WINNT */
#ifndef WINVER
#define WINVER 0x0501
#endif /* #ifndef WINVER */
#endif
#include <string.h> /* strlen() */
@ -76,7 +80,7 @@
#include "pa_debugprint.h"
#include <windows.h>
#include <winioctl.h>
/* #include <winioctl.h> MinGW-w64 4.7.1 from TDM-GCC throws multiple redefinition errors. */
#include <process.h>
#ifdef __GNUC__
@ -1885,6 +1889,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;

View file

@ -1,5 +1,5 @@
/*
* $Id$
* $Id: pa_win_wmme.c 1663 2011-05-01 13:21:43Z rossb $
* pa_win_wmme.c
* Implementation of PortAudio for Windows MultiMedia Extensions (WMME)
*
@ -153,6 +153,9 @@
#define PA_MME_USE_HIGH_DEFAULT_LATENCY_ (0) /* For debugging glitches. */
#define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17)
#define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18)
#if PA_MME_USE_HIGH_DEFAULT_LATENCY_
#define PA_MME_WIN_9X_DEFAULT_LATENCY_ (0.4)
#define PA_MME_MIN_HOST_OUTPUT_BUFFER_COUNT_ (4)
@ -177,7 +180,7 @@
#define PA_MME_WIN_NT_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_ * 2)
#define PA_MME_WIN_WDM_DEFAULT_LATENCY_ (PA_MME_WIN_9X_DEFAULT_LATENCY_)
#define PA_MME_MAX_TIMEOUT_MSEC_ (2000)
#define PA_MME_MIN_TIMEOUT_MSEC_ (1000)
static const char constInputMapperSuffix_[] = " - Input";
@ -242,6 +245,9 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
const PaStreamParameters *inputParameters,
const PaStreamParameters *outputParameters,
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 );
static PaError CloseStream( PaStream* stream );
static PaError StartStream( PaStream *stream );
static PaError StopStream( PaStream *stream );
@ -377,6 +383,92 @@ static PaError CloseHandleWithPaError( HANDLE handle )
return result;
}
/**
* Sets the <tt>transportType</tt> of a specific <tt>PaDeviceInfo</tt>.
*
* @param winMmeDeviceId
* @param deviceInfo the <tt>PaDeviceInfo</tt> to set the <tt>transportType</tt>
* of
*/
static void
SetDeviceInfoTransportType(UINT winMmeDeviceId, PaDeviceInfo *deviceInfo)
{
deviceInfo->transportType = NULL;
#ifdef PAWIN_USE_WDMKS_DEVICE_INFO
if (!(deviceInfo->transportType))
{
DWORD deviceInterfaceSize;
LPWSTR deviceInterface = NULL;
if (deviceInfo->maxInputChannels > 0)
{
if (waveInMessage(
(HWAVEIN)winMmeDeviceId,
DRV_QUERYDEVICEINTERFACESIZE,
(DWORD_PTR)&deviceInterfaceSize,
0)
== MMSYSERR_NOERROR)
{
deviceInterface = PaUtil_AllocateMemory(deviceInterfaceSize);
if (deviceInterface
&& (waveInMessage(
(HWAVEIN)winMmeDeviceId,
DRV_QUERYDEVICEINTERFACE,
(DWORD_PTR)deviceInterface,
deviceInterfaceSize)
!= MMSYSERR_NOERROR))
{
PaUtil_FreeMemory(deviceInterface);
deviceInterface = NULL;
}
}
}
else if (deviceInfo->maxOutputChannels > 0)
{
if (waveOutMessage(
(HWAVEOUT)winMmeDeviceId,
DRV_QUERYDEVICEINTERFACESIZE,
(DWORD_PTR)&deviceInterfaceSize,
0)
== MMSYSERR_NOERROR)
{
deviceInterface = PaUtil_AllocateMemory(deviceInterfaceSize);
if (deviceInterface
&& (waveOutMessage(
(HWAVEOUT)winMmeDeviceId,
DRV_QUERYDEVICEINTERFACE,
(DWORD_PTR)deviceInterface,
deviceInterfaceSize)
!= MMSYSERR_NOERROR))
{
PaUtil_FreeMemory(deviceInterface);
deviceInterface = NULL;
}
}
}
if (deviceInterface)
{
if (wcsstr(deviceInterface, L"\\usb#")
|| wcsstr(deviceInterface, L"\\USB#"))
deviceInfo->transportType = "USB";
PaUtil_FreeMemory(deviceInterface);
}
}
#endif /* #ifdef PAWIN_USE_WDMKS_DEVICE_INFO */
/*
* As a last resort, attempt to make a decision about the transportType
* based on the name of the specified deviceInfo.
*/
if (!(deviceInfo->transportType))
{
const char *s = deviceInfo->name;
if (s && (s = strrchr(s,'(')) && (s = strstr(s,"USB")) && strchr(s,')'))
deviceInfo->transportType = "USB";
}
}
/* PaWinMmeHostApiRepresentation - host api datastructure specific to this implementation */
@ -396,6 +488,8 @@ typedef struct
device ids.
*/
UINT *winMmeDeviceIds;
//char comWasInitialized;
}
PaWinMmeHostApiRepresentation;
@ -409,6 +503,14 @@ typedef struct
}
PaWinMmeDeviceInfo;
typedef struct PaWinMmeScanDeviceInfosResults{ /* used for tranferring device infos during scanning / rescanning */
PaDeviceInfo **deviceInfos;
PaDeviceIndex defaultInputDevice;
PaDeviceIndex defaultOutputDevice;
int inputDeviceCount, outputDeviceCount;
} PaWinMmeScanDeviceInfosResults;
/*************************************************************************
* Returns recommended device ID.
@ -665,12 +767,19 @@ static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeH
{
PaError result = paNoError;
char *deviceName; /* non-const ptr */
char *deviceUID;
MMRESULT mmresult;
WAVEINCAPS wic;
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
*success = 0;
/* We do not want Microsoft's Sound Mapper because it is a virtual device
* and we want the real (hardware) devices only.
*/
if (WAVE_MAPPER == winMmeInputDeviceId)
return paNoError;
mmresult = waveInGetDevCaps( winMmeInputDeviceId, &wic, sizeof( WAVEINCAPS ) );
if( mmresult == MMSYSERR_NOMEM )
{
@ -737,6 +846,51 @@ static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeH
QueryWaveInKSFilterMaxChannels( winMmeInputDeviceId, &deviceInfo->maxInputChannels );
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
/* transportType */
SetDeviceInfoTransportType(winMmeInputDeviceId, deviceInfo);
MMRESULT mmr;
size_t cbEndpointIdSize;
// Get the size (including the terminating null) of
// the endpoint ID string of the waveOut device.
mmr = waveInMessage(
winMmeInputDeviceId,
DRV_QUERYFUNCTIONINSTANCEIDSIZE,
(DWORD_PTR)&cbEndpointIdSize, NULL);
if (mmr == MMSYSERR_NOERROR) // do sizes match?
{
WCHAR *pstrEndpointId = NULL;
pstrEndpointId = (WCHAR *)PaUtil_GroupAllocateMemory(winMmeHostApi->allocations, cbEndpointIdSize);
// Get the endpoint ID string for this waveOut device.
mmr = waveInMessage(
winMmeInputDeviceId,
DRV_QUERYFUNCTIONINSTANCEID,
(DWORD_PTR)pstrEndpointId,
cbEndpointIdSize);
if (mmr == MMSYSERR_NOERROR)
{
deviceUID = (char *)PaUtil_GroupAllocateMemory(winMmeHostApi->allocations, wcslen(pstrEndpointId));
if( !deviceUID )
{
result = paInsufficientMemory;
goto error;
}
wcstombs(deviceUID, pstrEndpointId, sizeof(deviceUID));
WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
pstrEndpointId, -1, deviceUID, wcslen(pstrEndpointId), NULL, NULL );
deviceInfo->deviceUID = deviceUID;
}
/*else
{
char mmeErrorText[ MAXERRORLENGTH ];
waveOutGetErrorText( mmr, mmeErrorText, MAXERRORLENGTH );
PA_DEBUG(("Error %i: %s\n", mmr ,mmeErrorText));
}*/
}
winMmeDeviceInfo->dwFormats = wic.dwFormats;
DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId,
@ -788,6 +942,7 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
{
PaError result = paNoError;
char *deviceName; /* non-const ptr */
char *deviceUID;
MMRESULT mmresult;
WAVEOUTCAPS woc;
PaDeviceInfo *deviceInfo = &winMmeDeviceInfo->inheritedDeviceInfo;
@ -795,6 +950,12 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
*success = 0;
/* We do not want Microsoft's Sound Mapper because it is a virtual device
* and we want the real (hardware) devices only.
*/
if (WAVE_MAPPER == winMmeOutputDeviceId)
return paNoError;
mmresult = waveOutGetDevCaps( winMmeOutputDeviceId, &woc, sizeof( WAVEOUTCAPS ) );
if( mmresult == MMSYSERR_NOMEM )
{
@ -863,6 +1024,52 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
winMmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
#endif /* PAWIN_USE_WDMKS_DEVICE_INFO */
/* transportType */
SetDeviceInfoTransportType(winMmeOutputDeviceId, deviceInfo);
MMRESULT mmr;
size_t cbEndpointIdSize;
// Get the size (including the terminating null) of
// the endpoint ID string of the waveOut device.
mmr = waveOutMessage(
winMmeOutputDeviceId,
DRV_QUERYFUNCTIONINSTANCEIDSIZE,
(DWORD_PTR)&cbEndpointIdSize, NULL);
if (mmr == MMSYSERR_NOERROR) // do sizes match?
{
WCHAR *pstrEndpointId = NULL;
pstrEndpointId = (WCHAR *)PaUtil_GroupAllocateMemory(winMmeHostApi->allocations, cbEndpointIdSize);
// Get the endpoint ID string for this waveOut device.
mmr = waveOutMessage(
winMmeOutputDeviceId,
DRV_QUERYFUNCTIONINSTANCEID,
(DWORD_PTR)pstrEndpointId,
cbEndpointIdSize);
if (mmr == MMSYSERR_NOERROR)
{
deviceUID = (char *)PaUtil_GroupAllocateMemory(winMmeHostApi->allocations, wcslen(pstrEndpointId));
if( !deviceUID )
{
result = paInsufficientMemory;
goto error;
}
wcstombs(deviceUID, pstrEndpointId, sizeof(deviceUID));
WideCharToMultiByte( CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,\
pstrEndpointId, -1, deviceUID, wcslen(pstrEndpointId), NULL, NULL );
deviceInfo->deviceUID = deviceUID;
}
/*else
{
char mmeErrorText[ MAXERRORLENGTH ];
waveOutGetErrorText( mmr, mmeErrorText, MAXERRORLENGTH );
PA_DEBUG(("Error %i: %s\n", mmr ,mmeErrorText));
}*/
}
winMmeDeviceInfo->dwFormats = woc.dwFormats;
DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId,
@ -902,14 +1109,10 @@ static void GetDefaultLatencies( PaTime *defaultLowLatency, PaTime *defaultHighL
PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex hostApiIndex )
{
PaError result = paNoError;
int i;
int deviceCount;
PaWinMmeHostApiRepresentation *winMmeHostApi;
int inputDeviceCount, outputDeviceCount, maximumPossibleDeviceCount;
PaWinMmeDeviceInfo *deviceInfoArray;
int deviceInfoInitializationSucceeded;
PaTime defaultLowLatency, defaultHighLatency;
DWORD waveInPreferredDevice, waveOutPreferredDevice;
DWORD preferredDeviceStatusFlags;
//char comWasInitialized = 1;
void *scanResults = 0;
winMmeHostApi = (PaWinMmeHostApiRepresentation*)PaUtil_AllocateMemory( sizeof(PaWinMmeHostApiRepresentation) );
if( !winMmeHostApi )
@ -918,6 +1121,8 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
goto error;
}
//winMmeHostApi->comWasInitialized = comWasInitialized;
winMmeHostApi->allocations = PaUtil_CreateAllocationGroup();
if( !winMmeHostApi->allocations )
{
@ -930,163 +1135,32 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
(*hostApi)->info.type = paMME;
(*hostApi)->info.name = "MME";
/* initialise device counts and default devices under the assumption that
there are no devices. These values are incremented below if and when
devices are successfully initialized.
*/
/* these are all updated by CommitDeviceInfos() */
(*hostApi)->info.deviceCount = 0;
(*hostApi)->info.defaultInputDevice = paNoDevice;
(*hostApi)->info.defaultOutputDevice = paNoDevice;
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_PTR)&waveInPreferredDevice, (DWORD_PTR)&preferredDeviceStatusFlags );
preferredDeviceStatusFlags = 0;
waveOutPreferredDevice = -1;
waveOutMessage( (HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&waveOutPreferredDevice, (DWORD_PTR)&preferredDeviceStatusFlags );
maximumPossibleDeviceCount = 0;
inputDeviceCount = waveInGetNumDevs();
if( inputDeviceCount > 0 )
maximumPossibleDeviceCount += inputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
outputDeviceCount = waveOutGetNumDevs();
if( outputDeviceCount > 0 )
maximumPossibleDeviceCount += outputDeviceCount + 1; /* assume there is a WAVE_MAPPER */
if( maximumPossibleDeviceCount > 0 ){
(*hostApi)->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount );
if( !(*hostApi)->deviceInfos )
{
result = paInsufficientMemory;
goto error;
}
/* allocate all device info structs in a contiguous block */
deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory(
winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount );
if( !deviceInfoArray )
{
result = paInsufficientMemory;
goto error;
}
winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory(
winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount );
if( !winMmeHostApi->winMmeDeviceIds )
{
result = paInsufficientMemory;
goto error;
}
GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency );
if( inputDeviceCount > 0 ){
/* -1 is the WAVE_MAPPER */
for( i = -1; i < inputDeviceCount; ++i ){
UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
deviceInfo->structVersion = 2;
deviceInfo->hostApi = hostApiIndex;
deviceInfo->maxInputChannels = 0;
wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
deviceInfo->maxOutputChannels = 0;
wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
deviceInfo->defaultLowInputLatency = defaultLowLatency;
deviceInfo->defaultLowOutputLatency = defaultLowLatency;
deviceInfo->defaultHighInputLatency = defaultHighLatency;
deviceInfo->defaultHighOutputLatency = defaultHighLatency;
result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
winMmeDeviceId, &deviceInfoInitializationSucceeded );
result = ScanDeviceInfos( &winMmeHostApi->inheritedHostApiRep, hostApiIndex, &scanResults, &deviceCount );
if( result != paNoError )
{
goto error;
if( deviceInfoInitializationSucceeded ){
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;
winMmeHostApi->inputDeviceCount++;
(*hostApi)->info.deviceCount++;
}
}
}
if( outputDeviceCount > 0 ){
/* -1 is the WAVE_MAPPER */
for( i = -1; i < outputDeviceCount; ++i ){
UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
deviceInfo->structVersion = 2;
deviceInfo->hostApi = hostApiIndex;
deviceInfo->maxInputChannels = 0;
wmmeDeviceInfo->deviceInputChannelCountIsKnown = 1;
deviceInfo->maxOutputChannels = 0;
wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 1;
deviceInfo->defaultLowInputLatency = defaultLowLatency;
deviceInfo->defaultLowOutputLatency = defaultLowLatency;
deviceInfo->defaultHighInputLatency = defaultHighLatency;
deviceInfo->defaultHighOutputLatency = defaultHighLatency;
result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
winMmeDeviceId, &deviceInfoInitializationSucceeded );
if( result != paNoError )
goto error;
if( deviceInfoInitializationSucceeded ){
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;
winMmeHostApi->outputDeviceCount++;
(*hostApi)->info.deviceCount++;
}
}
}
}
InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
/* FIXME for now we ignore the result of CommitDeviceInfos(), it should probably be an atomic non-failing operation */
CommitDeviceInfos( &winMmeHostApi->inheritedHostApiRep, hostApiIndex, scanResults, deviceCount );
(*hostApi)->Terminate = Terminate;
(*hostApi)->OpenStream = OpenStream;
(*hostApi)->IsFormatSupported = IsFormatSupported;
(*hostApi)->ScanDeviceInfos = ScanDeviceInfos;
(*hostApi)->CommitDeviceInfos = CommitDeviceInfos;
(*hostApi)->DisposeDeviceInfos = DisposeDeviceInfos;
PaUtil_InitializeStreamInterface( &winMmeHostApi->callbackStreamInterface, CloseStream, StartStream,
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
@ -1323,7 +1397,298 @@ static PaError IsFormatSupported( struct PaUtilHostApiRepresentation *hostApi,
return paFormatIsSupported;
}
/***********************************************************************************/
static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex, void **scanResults, int *newDeviceCount )
{
PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
PaWinMmeDeviceInfo *deviceInfoArray;
//char comWasInitialized = winMmeHostApi->comWasInitialized;
PaError result = paNoError;
PaWinMmeScanDeviceInfosResults *outArgument = 0;
//DSDeviceNamesAndGUIDs deviceNamesAndGUIDs;
int i = 0;
int maximumPossibleDeviceCount = 0;
int inputDeviceCount, outputDeviceCount;
int deviceInfoInitializationSucceeded;
PaTime defaultLowLatency, defaultHighLatency;
DWORD waveInPreferredDevice, waveOutPreferredDevice;
DWORD preferredDeviceStatusFlags;
// Check preconditions
if( /*!comWasInitialized ||*/ scanResults == NULL || newDeviceCount == NULL )
return paInternalError;
/* initialize the out params */
*scanResults = NULL;
*newDeviceCount = 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_PTR)&waveInPreferredDevice, (DWORD_PTR)&preferredDeviceStatusFlags );
preferredDeviceStatusFlags = 0;
waveOutPreferredDevice = -1;
waveOutMessage( (HWAVEOUT)WAVE_MAPPER, DRVM_MAPPER_PREFERRED_GET, (DWORD_PTR)&waveOutPreferredDevice, (DWORD_PTR)&preferredDeviceStatusFlags );
maximumPossibleDeviceCount = 0;
inputDeviceCount = waveInGetNumDevs();
if( inputDeviceCount > 0 )
maximumPossibleDeviceCount += inputDeviceCount + 1; // assume there is a WAVE_MAPPER
outputDeviceCount = waveOutGetNumDevs();
if( outputDeviceCount > 0 )
maximumPossibleDeviceCount += outputDeviceCount + 1;// assume there is a WAVE_MAPPER
if( maximumPossibleDeviceCount > 0 )
{
/* Allocate the out param for all the info we need */
outArgument = (PaWinMmeScanDeviceInfosResults *) PaUtil_GroupAllocateMemory(
winMmeHostApi->allocations, sizeof(PaWinMmeScanDeviceInfosResults) );
if( !outArgument )
{
result = paInsufficientMemory;
goto error;
}
/* allocate array for pointers to PaDeviceInfo structs */
outArgument->deviceInfos = (PaDeviceInfo**)PaUtil_GroupAllocateMemory(
winMmeHostApi->allocations, sizeof(PaDeviceInfo*) * maximumPossibleDeviceCount );
if( !outArgument->deviceInfos )
{
result = paInsufficientMemory;
goto error;
}
// allocate all device info structs in a contiguous block
deviceInfoArray = (PaWinMmeDeviceInfo*)PaUtil_GroupAllocateMemory(
winMmeHostApi->allocations, sizeof(PaWinMmeDeviceInfo) * maximumPossibleDeviceCount );
if( !deviceInfoArray )
{
result = paInsufficientMemory;
goto error;
}
winMmeHostApi->winMmeDeviceIds = (UINT*)PaUtil_GroupAllocateMemory(
winMmeHostApi->allocations, sizeof(int) * maximumPossibleDeviceCount );
if( !winMmeHostApi->winMmeDeviceIds )
{
result = paInsufficientMemory;
goto error;
}
for( i = 0 ; i < maximumPossibleDeviceCount; ++i )
{
PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[i];
PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
deviceInfo->structVersion = 3;
deviceInfo->hostApi = hostApiIndex;
deviceInfo->name = 0;
/*
* The fields maxInputChannels and maxOutputChannels used to be
* initialized in each of the loops bellow to the effect of the
* second loop overwriting/throwing away the results of the first
* loop. In order to resolve the described issue, the initialization
* of the two fields in question has been moved here.
*/
deviceInfo->maxInputChannels = 0;
deviceInfo->maxOutputChannels = 0;
/*
* The fields defaultLowInputLatency, defaultLowOutputLatency,
* defaultHighInputLatency and defaultHighOutputLatency used to be
* initialized in each of the loops bellow. For the sake of clarity,
* their initialization has been moved here.
*/
deviceInfo->defaultLowInputLatency = defaultLowLatency;
deviceInfo->defaultLowOutputLatency = defaultLowLatency;
deviceInfo->defaultHighInputLatency = defaultHighLatency;
deviceInfo->defaultHighOutputLatency = defaultHighLatency;
/*
* The fields deviceInputChannelCountIsKnown and
* deviceOutputChannelCountIsKnown used to be initialized in each of
* the loops bellow to the effect of the second loop
* overwriting/throwing away the results of the first loop. In order
* to resolve the described issue, the initialization of the two
* fields in question has been moved here.
*/
wmmeDeviceInfo->deviceInputChannelCountIsKnown = 0;
wmmeDeviceInfo->deviceOutputChannelCountIsKnown = 0;
outArgument->deviceInfos[ i ] = deviceInfo;
}
outArgument->inputDeviceCount = 0;
outArgument->outputDeviceCount = 0;
GetDefaultLatencies( &defaultLowLatency, &defaultHighLatency );
if( inputDeviceCount > 0 )
{
// -1 is the WAVE_MAPPER
for( i = -1; i < inputDeviceCount; ++i )
{
UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
PaWinMmeDeviceInfo *wmmeDeviceInfo = (PaWinMmeDeviceInfo*)outArgument->deviceInfos[*newDeviceCount];
PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
result = InitializeInputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
winMmeDeviceId, &deviceInfoInitializationSucceeded );
/* ignore error results here and just skip the device */
if( result != paNoError )
continue;
if( deviceInfoInitializationSucceeded )
{
if( outArgument->defaultInputDevice == paNoDevice )
{
// if there is currently no default device, use the first one available
outArgument->defaultInputDevice = *newDeviceCount;
}
else if( winMmeDeviceId == waveInPreferredDevice )
{
// set the default device to the system preferred device
outArgument->defaultInputDevice = *newDeviceCount;
}
winMmeHostApi->winMmeDeviceIds[ *newDeviceCount ] = winMmeDeviceId;
outArgument->inputDeviceCount++;
(*newDeviceCount)++;
}
}
}
if( outputDeviceCount > 0 )
{
// -1 is the WAVE_MAPPER
for( i = -1; i < outputDeviceCount; ++i )
{
UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
PaWinMmeDeviceInfo *wmmeDeviceInfo = (PaWinMmeDeviceInfo*)outArgument->deviceInfos[*newDeviceCount];
PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
result = InitializeOutputDeviceInfo( winMmeHostApi, wmmeDeviceInfo,
winMmeDeviceId, &deviceInfoInitializationSucceeded );
/* ignore error results here and just skip the device */
if( result != paNoError )
continue;
if( deviceInfoInitializationSucceeded )
{
if( outArgument->defaultOutputDevice == paNoDevice )
{
// if there is currently no default device, use the first one available
outArgument->defaultOutputDevice = *newDeviceCount;
}
else if( winMmeDeviceId == waveOutPreferredDevice )
{
// set the default device to the system preferred device
outArgument->defaultOutputDevice = *newDeviceCount;
}
winMmeHostApi->winMmeDeviceIds[ *newDeviceCount ] = winMmeDeviceId;
outArgument->outputDeviceCount++;
(*newDeviceCount)++;
}
}
}
}
InitializeDefaultDeviceIdsFromEnv( winMmeHostApi );
*scanResults = outArgument;
return result;
error:
if( outArgument )
{
if( outArgument->deviceInfos )
{
if( outArgument->deviceInfos[0] )
{
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, outArgument->deviceInfos[0] );
}
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, outArgument->deviceInfos );
}
}
return result;
}
static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex index, void *scanResults, int deviceCount )
{
PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
PaError result = paNoError;
int i = 0;
hostApi->info.deviceCount = 0;
hostApi->info.defaultInputDevice = paNoDevice;
hostApi->info.defaultOutputDevice = paNoDevice;
/* Free any old memory which might be in the device info */
if( hostApi->deviceInfos )
{
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, hostApi->deviceInfos[0] );
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, hostApi->deviceInfos );
hostApi->deviceInfos = NULL;
}
if( scanResults != NULL )
{
PaWinMmeScanDeviceInfosResults *scanDeviceInfosResults = ( PaWinMmeScanDeviceInfosResults * ) 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;
winMmeHostApi->inputDeviceCount = scanDeviceInfosResults->inputDeviceCount;
winMmeHostApi->outputDeviceCount = scanDeviceInfosResults->outputDeviceCount;
}
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, scanDeviceInfosResults );
}
return result;
}
static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, void *scanResults, int deviceCount )
{
PaWinMmeHostApiRepresentation *winMmeHostApi = (PaWinMmeHostApiRepresentation*)hostApi;
if( scanResults != NULL )
{
PaWinMmeScanDeviceInfosResults *scanDeviceInfosResults = ( PaWinMmeScanDeviceInfosResults * ) scanResults;
if( scanDeviceInfosResults->deviceInfos )
{
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, scanDeviceInfosResults->deviceInfos[0] ); // all device info structs are allocated in a block so we can destroy them here
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, scanDeviceInfosResults->deviceInfos );
}
PaUtil_GroupFreeMemory( winMmeHostApi->allocations, scanDeviceInfosResults );
}
return paNoError;
}
/***********************************************************************************/
static void SelectBufferSizeAndCount( unsigned long baseBufferSize,
unsigned long requestedLatency,
@ -3391,7 +3756,7 @@ static PaError StopStream( PaStream *s )
{
PaError result = paNoError;
PaWinMmeStream *stream = (PaWinMmeStream*)s;
int timeout;
DWORD timeout;
DWORD waitResult;
MMRESULT mmresult;
signed int hostOutputBufferIndex;
@ -3412,7 +3777,7 @@ static PaError StopStream( PaStream *s )
stream->stopProcessing = 1;
/* Calculate timeOut longer than longest time it could take to return all buffers. */
timeout = (int)(stream->allBuffersDurationMs * 1.5);
timeout = (DWORD)(stream->allBuffersDurationMs * 1.5);
if( timeout < PA_MME_MIN_TIMEOUT_MSEC_ )
timeout = PA_MME_MIN_TIMEOUT_MSEC_;
@ -3441,6 +3806,12 @@ static PaError StopStream( PaStream *s )
if( PA_IS_OUTPUT_STREAM_(stream) )
{
/* If WaitForSingleObject starts repetitively and consecutively
returning WAIT_TIMEOUT, do eventually give up. Otherwise, the
method may never return.
*/
DWORD totalTimeout;
if( stream->output.framesUsedInCurrentBuffer > 0 )
{
/* there are still unqueued frames in the current buffer, so flush them */
@ -3481,6 +3852,7 @@ static PaError StopStream( PaStream *s )
timeout = PA_MME_MIN_TIMEOUT_MSEC_;
waitCount = 0;
totalTimeout = 0;
while( !NoBuffersAreQueued( &stream->output ) && waitCount <= stream->output.bufferCount )
{
/* wait for MME to signal that a buffer is available */
@ -3491,8 +3863,19 @@ static PaError StopStream( PaStream *s )
}
else if( waitResult == WAIT_TIMEOUT )
{
/* keep waiting */
/* Keep waiting. However, testing has shown that it is
possible to unplug a device and to wait here forever. In
order to prevent such a scenario, do eventually given up.
*/
totalTimeout += timeout;
if( PA_MME_MAX_TIMEOUT_MSEC_ <= totalTimeout)
{
result = paTimedOut;
break;
}
}
else
totalTimeout = 0;
++waitCount;
}
@ -3668,6 +4051,12 @@ static PaError ReadStream( PaStream* s,
if( PA_IS_INPUT_STREAM_(stream) )
{
/* If WaitForSingleObject starts repetitively and consecutively
returning WAIT_TIMEOUT, do eventually give up in order to allow the
caller to handle such cases.
*/
DWORD totalTimeout = 0;
/* make a local copy of the user buffer pointer(s). this is necessary
because PaUtil_CopyInput() advances these pointers every time
it is called.
@ -3688,6 +4077,8 @@ static PaError ReadStream( PaStream* s,
do{
if( CurrentInputBuffersAreDone( stream ) )
{
totalTimeout = 0;
if( NoBuffersAreQueued( &stream->input ) )
{
/** @todo REVIEW: consider what to do if the input overflows.
@ -3741,9 +4132,17 @@ static PaError ReadStream( PaStream* s,
}
else if( waitResult == WAIT_TIMEOUT )
{
/* if a timeout is encountered, continue,
perhaps we should give up eventually
/* If a timeout is encountered, continue. However, testing
has shown that it is possible to unplug a device and to
wait here forever. In order to allow the caller to handle
such cases of repeated timeouts, do eventually given up.
*/
totalTimeout += timeout;
if( PA_MME_MAX_TIMEOUT_MSEC_ <= totalTimeout)
{
result = paTimedOut;
break;
}
}
}
}while( framesRead < frames );
@ -3774,6 +4173,12 @@ static PaError WriteStream( PaStream* s,
if( PA_IS_OUTPUT_STREAM_(stream) )
{
/* If WaitForSingleObject starts repetitively and consecutively
returning WAIT_TIMEOUT, do eventually give up in order to allow the
caller to handle such cases.
*/
DWORD totalTimeout = 0;
/* make a local copy of the user buffer pointer(s). this is necessary
because PaUtil_CopyOutput() advances these pointers every time
it is called.
@ -3794,6 +4199,8 @@ static PaError WriteStream( PaStream* s,
do{
if( CurrentOutputBuffersAreDone( stream ) )
{
totalTimeout = 0;
if( NoBuffersAreQueued( &stream->output ) )
{
/** @todo REVIEW: consider what to do if the output
@ -3849,9 +4256,17 @@ static PaError WriteStream( PaStream* s,
}
else if( waitResult == WAIT_TIMEOUT )
{
/* if a timeout is encountered, continue,
perhaps we should give up eventually
/* If a timeout is encountered, continue. However, testing
has shown that it is possible to unplug a device and to
wait here forever. In order to allow the caller to handle
such cases of repeated timeouts, do eventually given up.
*/
totalTimeout += timeout;
if( PA_MME_MAX_TIMEOUT_MSEC_ <= totalTimeout)
{
result = paTimedOut;
break;
}
}
}
}while( framesWritten < frames );

View file

@ -0,0 +1,76 @@
#include "pa_util.h"
#include "pa_debugprint.h"
#include "pa_allocation.h"
#include <stdio.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
/* 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()
{
}

View file

@ -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 <alsa/asoundlib.h>
#include <stdio.h>
#include <signal.h>
#include <pthread.h>
/* 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);
}

View file

@ -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

318
src/os/win/pa_win_hotplug.c Normal file
View file

@ -0,0 +1,318 @@
#include "pa_util.h"
#include "pa_debugprint.h"
#include "pa_allocation.h"
#include "pa_win_wdmks_utils.h"
#include <windows.h>
#include <dbt.h>
#include <process.h>
#include <assert.h>
#include <ks.h>
#include <ksmedia.h>
#include <setupapi.h>
#include <stdio.h>
#if (defined(WIN32) && (defined(_MSC_VER) && (_MSC_VER >= 1200))) /* MSC version 6 and above */
#pragma comment( lib, "setupapi.lib" )
#endif
/* 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*);
/* use CreateThread for CYGWIN/Windows Mobile, _beginthreadex for all others */
#if !defined(__CYGWIN__) && !defined(_WIN32_WCE)
#define CREATE_THREAD_FUNCTION (HANDLE)_beginthreadex
#define PA_THREAD_FUNC static unsigned WINAPI
#else
#define CREATE_THREAD_FUNCTION CreateThread
#define PA_THREAD_FUNC static DWORD WINAPI
#endif
typedef struct PaHotPlugDeviceInfo
{
wchar_t name[MAX_PATH];
struct PaHotPlugDeviceInfo* next;
} PaHotPlugDeviceInfo;
typedef struct PaHotPlugDeviceEventHandlerInfo
{
HANDLE hWnd;
HANDLE hMsgThread;
HANDLE hNotify;
CRITICAL_SECTION lock;
PaUtilAllocationGroup* cacheAllocGroup;
PaHotPlugDeviceInfo* cache;
} PaHotPlugDeviceEventHandlerInfo;
static BOOL RemoveDeviceFromCache(PaHotPlugDeviceEventHandlerInfo* pInfo, const wchar_t* name)
{
if (pInfo->cache != NULL)
{
PaHotPlugDeviceInfo* lastEntry = 0;
PaHotPlugDeviceInfo* entry = pInfo->cache;
while (entry != NULL)
{
if (_wcsicmp(entry->name, name) == 0)
{
if (lastEntry)
{
lastEntry->next = entry->next;
}
else
{
pInfo->cache = NULL;
}
PaUtil_GroupFreeMemory(pInfo->cacheAllocGroup, entry);
return TRUE;
}
lastEntry = entry;
entry = entry->next;
}
}
return FALSE;
}
static void InsertDeviceIntoCache(PaHotPlugDeviceEventHandlerInfo* pInfo, const wchar_t* name)
{
PaHotPlugDeviceInfo** ppEntry = NULL;
/* Remove it first (if possible) so we don't accidentally get duplicates */
RemoveDeviceFromCache(pInfo, name);
if (pInfo->cache == NULL)
{
ppEntry = &pInfo->cache;
}
else
{
PaHotPlugDeviceInfo* entry = pInfo->cache;
while (entry->next != NULL)
{
entry = entry->next;
}
ppEntry = &entry->next;
}
*ppEntry = (PaHotPlugDeviceInfo*)PaUtil_GroupAllocateMemory(pInfo->cacheAllocGroup, sizeof(PaHotPlugDeviceInfo));
wcsncpy((*ppEntry)->name, name, MAX_PATH-1);
(*ppEntry)->next = NULL;
}
static BOOL IsDeviceAudio(const wchar_t* deviceName)
{
int channelCnt = 0;
channelCnt += PaWin_WDMKS_QueryFilterMaximumChannelCount((void*)deviceName, 1);
channelCnt += PaWin_WDMKS_QueryFilterMaximumChannelCount((void*)deviceName, 0);
return (channelCnt > 0);
}
static void PopulateCacheWithAvailableAudioDevices(PaHotPlugDeviceEventHandlerInfo* pInfo)
{
HDEVINFO handle = NULL;
const int sizeInterface = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W) + (MAX_PATH * sizeof(WCHAR));
SP_DEVICE_INTERFACE_DETAIL_DATA_W* devInterfaceDetails = (SP_DEVICE_INTERFACE_DETAIL_DATA_W*)PaUtil_AllocateMemory(sizeInterface);
if (devInterfaceDetails)
{
devInterfaceDetails->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
/* Open a handle to search for devices (filters) */
handle = SetupDiGetClassDevsW(&KSCATEGORY_AUDIO,NULL,NULL,DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if( handle != NULL )
{
int device;
/* Iterate through the devices */
for( device = 0;;device++ )
{
SP_DEVICE_INTERFACE_DATA interfaceData;
SP_DEVINFO_DATA devInfoData;
int noError;
interfaceData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
interfaceData.Reserved = 0;
devInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
devInfoData.Reserved = 0;
noError = SetupDiEnumDeviceInterfaces(handle,NULL,&KSCATEGORY_AUDIO,device,&interfaceData);
if( !noError )
break; /* No more devices */
noError = SetupDiGetDeviceInterfaceDetailW(handle,&interfaceData,devInterfaceDetails,sizeInterface,NULL,&devInfoData);
if( noError )
{
if (IsDeviceAudio(devInterfaceDetails->DevicePath))
{
PA_DEBUG(("Hotplug cache populated with: '%S'\n", devInterfaceDetails->DevicePath));
InsertDeviceIntoCache(pInfo, devInterfaceDetails->DevicePath);
}
}
}
SetupDiDestroyDeviceInfoList(handle);
}
PaUtil_FreeMemory(devInterfaceDetails);
}
}
static LRESULT CALLBACK PaMsgWinProcW(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
PaHotPlugDeviceEventHandlerInfo* pInfo = (PaHotPlugDeviceEventHandlerInfo*)( GetWindowLongPtr(hWnd, GWLP_USERDATA) );
switch(msg)
{
case WM_DEVICECHANGE:
switch(wParam)
{
case DBT_DEVICEARRIVAL:
{
PDEV_BROADCAST_DEVICEINTERFACE_W ptr = (PDEV_BROADCAST_DEVICEINTERFACE_W)lParam;
if (ptr->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
break;
if (!IsEqualGUID(&ptr->dbcc_classguid, &KSCATEGORY_AUDIO))
break;
if (IsDeviceAudio(ptr->dbcc_name))
{
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);
}
}
break;
case DBT_DEVICEREMOVECOMPLETE:
{
PDEV_BROADCAST_DEVICEINTERFACE_W ptr = (PDEV_BROADCAST_DEVICEINTERFACE_W)lParam;
if (ptr->dbcc_devicetype != DBT_DEVTYP_DEVICEINTERFACE)
break;
if (!IsEqualGUID(&ptr->dbcc_classguid, &KSCATEGORY_AUDIO))
break;
if (RemoveDeviceFromCache(pInfo, ptr->dbcc_name))
{
PA_DEBUG(("Device removed : %S\n", ptr->dbcc_name));
Sleep(2000);
PaUtil_DevicesChanged(2, ptr->dbcc_name);
}
}
break;
default:
break;
}
break;
}
return DefWindowProcW(hWnd, msg, wParam, lParam);
}
PA_THREAD_FUNC PaRunMessageLoop(void* ptr)
{
PaHotPlugDeviceEventHandlerInfo* pInfo = (PaHotPlugDeviceEventHandlerInfo*)ptr;
WNDCLASSW wnd = { 0 };
HMODULE hInstance = GetModuleHandleW(NULL);
wnd.lpfnWndProc = PaMsgWinProcW;
wnd.hInstance = hInstance;
wnd.lpszClassName = L"{1E0D4F5A-B31F-4dcc-AE3C-4F30A47BD521}"; /* Using a GUID as class name */
pInfo->hWnd = CreateWindowW((LPCWSTR)MAKEINTATOM(RegisterClassW(&wnd)), NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, hInstance, NULL);
if (pInfo->hWnd)
{
DEV_BROADCAST_DEVICEINTERFACE_W NotificationFilter = { sizeof(DEV_BROADCAST_DEVICEINTERFACE_W) };
NotificationFilter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
#ifndef DEVICE_NOTIFY_ALL_INTERFACE_CLASSES
#define DEVICE_NOTIFY_ALL_INTERFACE_CLASSES 0x00000004
#endif
pInfo->hNotify = RegisterDeviceNotificationW(
pInfo->hWnd,
&NotificationFilter,
DEVICE_NOTIFY_WINDOW_HANDLE|DEVICE_NOTIFY_ALL_INTERFACE_CLASSES
);
assert(pInfo->hNotify);
SetWindowLongPtr(pInfo->hWnd, GWLP_USERDATA, (LONG_PTR)pInfo);
if (pInfo->hNotify)
{
MSG msg;
BOOL result;
while((result = GetMessageW(&msg, pInfo->hWnd, 0, 0)) != 0)
{
if (result == -1)
{
break;
}
TranslateMessage(&msg);
DispatchMessageW(&msg);
}
UnregisterDeviceNotification(pInfo->hNotify);
pInfo->hNotify = 0;
}
DestroyWindow(pInfo->hWnd);
pInfo->hWnd = 0;
}
return 0;
}
static PaHotPlugDeviceEventHandlerInfo* s_handler = 0;
void PaUtil_InitializeHotPlug()
{
if (s_handler == 0)
{
s_handler = (PaHotPlugDeviceEventHandlerInfo*)PaUtil_AllocateMemory(sizeof(PaHotPlugDeviceEventHandlerInfo));
if (s_handler)
{
s_handler->cacheAllocGroup = PaUtil_CreateAllocationGroup();
InitializeCriticalSection(&s_handler->lock);
PopulateCacheWithAvailableAudioDevices(s_handler);
/* Start message thread */
s_handler->hMsgThread = CREATE_THREAD_FUNCTION(NULL, 0, PaRunMessageLoop, s_handler, 0, NULL);
assert(s_handler->hMsgThread != 0);
}
}
}
void PaUtil_TerminateHotPlug()
{
if (s_handler != 0)
{
if (s_handler->hWnd)
{
PostMessage(s_handler->hWnd, WM_QUIT, 0, 0);
if (WaitForSingleObject(s_handler->hMsgThread, 1000) == WAIT_TIMEOUT)
{
TerminateThread(s_handler->hMsgThread, -1);
}
}
DeleteCriticalSection(&s_handler->lock);
PaUtil_FreeAllAllocations( s_handler->cacheAllocGroup );
PaUtil_DestroyAllocationGroup( s_handler->cacheAllocGroup );
PaUtil_FreeMemory( s_handler );
s_handler = 0;
}
}
void PaUtil_LockHotPlug()
{
EnterCriticalSection(&s_handler->lock);
}
void PaUtil_UnlockHotPlug()
{
LeaveCriticalSection(&s_handler->lock);
}

View file

@ -9,7 +9,7 @@
@author Phil Burk <philburk@softsynth.com>
*/
/*
* $Id$
* $Id: patest_leftright.c 1661 2011-04-28 18:54:46Z rob_bielik $
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com

View file

@ -6,7 +6,7 @@
@author Phil Burk <philburk@softsynth.com>
*/
/*
* $Id$
* $Id: patest_out_underflow.c 1661 2011-04-28 18:54:46Z rob_bielik $
*
* This program uses the PortAudio Portable Audio Library.
* For more information see: http://www.portaudio.com

View file

@ -4,7 +4,7 @@
@author Phil Burk <philburk@softsynth.com>
*/
/*
* $Id$
* $Id: patest_two_rates.c 1661 2011-04-28 18:54:46Z rob_bielik $
*
* Author: Phil Burk http://www.softsynth.com
*

View file

@ -0,0 +1,50 @@
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include "portaudio.h"
void printDevices()
{
int deviceCount = Pa_GetDeviceCount();
int i;
for( i=0; i < deviceCount; ++i ){
const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(i);
const PaHostApiInfo *hostApiInfo = Pa_GetHostApiInfo( deviceInfo->hostApi );
assert( deviceInfo != 0 );
printf( "%d %s (%s)\n", i, deviceInfo->name, hostApiInfo->name );
}
}
static void devicesChangedCallback(void* p)
{
(void)p;
printf( "Portaudio device list have changed!\n" );
}
int main(int argc, char* argv[])
{
Pa_Initialize();
Pa_SetDevicesChangedCallback(NULL, devicesChangedCallback);
for(;;){
printDevices();
printf( "press [enter] to update the device list. or q + [enter] to quit.\n" );
char ch = getchar();
if( ch == 'q' )
break;
Pa_UpdateAvailableDeviceList();
}
Pa_Terminate();
return 0;
}