Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66bda13f84 | ||
|
|
ab8b64023c | ||
|
|
c534056a7b | ||
|
|
6c6269425f | ||
|
|
15d77417bf | ||
|
|
0a7b80cfce | ||
|
|
e7a274942b | ||
|
|
b77a1f828c | ||
|
|
d13fcd8ea9 |
39 changed files with 3567 additions and 1724 deletions
|
|
@ -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
BIN
bindings/cpp/configure
vendored
Binary file not shown.
|
|
@ -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
BIN
configure
vendored
Binary file not shown.
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@
|
|||
#include "portaudio.h"
|
||||
|
||||
#include <AudioUnit/AudioUnit.h>
|
||||
//#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
BIN
install-sh
BIN
install-sh
Binary file not shown.
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 ) );
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 );
|
||||
|
|
|
|||
76
src/os/mac_osx/pa_osx_hotplug.c
Normal file
76
src/os/mac_osx/pa_osx_hotplug.c
Normal 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()
|
||||
{
|
||||
}
|
||||
|
||||
147
src/os/unix/pa_linux_hotplug.c
Normal file
147
src/os/unix/pa_linux_hotplug.c
Normal 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);
|
||||
}
|
||||
|
||||
|
|
@ -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
318
src/os/win/pa_win_hotplug.c
Normal 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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*
|
||||
|
|
|
|||
50
test/patest_update_available_device_list.c
Normal file
50
test/patest_update_available_device_list.c
Normal 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;
|
||||
}
|
||||
|
||||
Loading…
Reference in a new issue