Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ca24be18da | ||
|
|
2048a0a62f | ||
|
|
d485a4b36d | ||
|
|
5549bca961 | ||
|
|
82bdb95a6d | ||
|
|
c2175e14f1 | ||
|
|
b95f3bd6a2 | ||
|
|
231ecf77c7 | ||
|
|
0a290773de | ||
|
|
46f97c7c20 | ||
|
|
8e9e507e12 | ||
|
|
8d285faa5e | ||
|
|
6840cda9ad | ||
|
|
5c5d4fb711 | ||
|
|
9562fb8074 | ||
|
|
ec5b0687a8 | ||
|
|
b45ec9ed0a | ||
|
|
d41ba780b2 | ||
|
|
abfcd708b4 | ||
|
|
3f7c315f22 | ||
|
|
9761960aa5 | ||
|
|
23b4bdd49a | ||
|
|
89f8f75a27 |
19 changed files with 1484 additions and 368 deletions
|
|
@ -3,7 +3,7 @@ EXPORTS
|
|||
;
|
||||
Pa_GetVersion @1
|
||||
Pa_GetVersionText @2
|
||||
Pa_GetErrorText @3
|
||||
Pa_GetErrorText @3
|
||||
Pa_Initialize @4
|
||||
Pa_Terminate @5
|
||||
Pa_GetHostApiCount @6
|
||||
|
|
@ -35,6 +35,8 @@ Pa_GetStreamReadAvailable @31
|
|||
Pa_GetStreamWriteAvailable @32
|
||||
Pa_GetSampleSize @33
|
||||
Pa_Sleep @34
|
||||
Pa_RefreshDeviceList @35
|
||||
Pa_SetDevicesChangedCallback @36
|
||||
PaAsio_GetAvailableBufferSizes @50
|
||||
PaAsio_ShowControlPanel @51
|
||||
PaUtil_InitializeX86PlainConverters @52
|
||||
|
|
|
|||
|
|
@ -1827,6 +1827,10 @@
|
|||
RelativePath="..\..\src\os\win\pa_win_wdmks_utils.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\os\win\pa_win_hotplug.c"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath="..\..\src\os\win\pa_x86_plain_converters.c"
|
||||
>
|
||||
|
|
|
|||
|
|
@ -155,9 +155,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, default devices,
|
||||
and the default host API are all frozen when Pa_Initialize() is called. The
|
||||
device list is not automatically updated when hardware devices are connected or
|
||||
disconnected. To refresh the list of devices, and default devices, either call
|
||||
Pa_RefreshDeviceList() or uninitialize PortAudio using Pa_Terminate(),
|
||||
and then reinitialize it by calling Pa_Initialize(). To register a callback
|
||||
when native devices or defaults change, call Pa_SetDevicesChangedCallback().
|
||||
|
||||
@return paNoError if successful, otherwise an error code indicating the cause
|
||||
of failure.
|
||||
|
||||
|
|
@ -432,6 +440,77 @@ PaDeviceIndex Pa_GetDefaultInputDevice( void );
|
|||
PaDeviceIndex Pa_GetDefaultOutputDevice( void );
|
||||
|
||||
|
||||
/** Refresh PortAudio's list of devices to reflect currently available
|
||||
native audio devices. Also refreshes default devices and default host API.
|
||||
|
||||
PortAudio's list of devices is usually frozen when Pa_Initialize() is
|
||||
called. Call Pa_RefreshDeviceList() to refresh PortAudio's device list
|
||||
at any time while PortAudio is initialized.
|
||||
|
||||
@return an error code indicating whether the refresh was successful.
|
||||
|
||||
If the function succeeds, future calls to Pa_GetDeviceCount() may return
|
||||
a different count than before, all device indexes may refer to
|
||||
different devices, and any previously returned PaDeviceInfo pointers are
|
||||
invalidated. Default devices reflected by PaHostApiInfo.defaultInputDevice,
|
||||
PaHostApiInfo.defaultOutputDevice and global defaults returned by
|
||||
Pa_GetDefaultHostApi(), Pa_GetDefaultInputDevice() and
|
||||
Pa_GetDefaultOutputDevice() may changed.
|
||||
|
||||
If the function fails, the device list and defaults are unchanged.
|
||||
|
||||
@note Open streams will not be affected by calls to this function.
|
||||
*/
|
||||
PaError Pa_RefreshDeviceList( void );
|
||||
|
||||
|
||||
/** Functions of type PaDevicesChangedCallback are implemented by PortAudio
|
||||
clients. They can be registered with PortAudio using the Pa_SetDevicesChangedCallback
|
||||
function. Once registered, a callback is invoked whenever available native audio
|
||||
devices may have changed. For example, when a USB audio device is connected or
|
||||
disconnected. The callback is also invoked whenever the native default device
|
||||
selection changes.
|
||||
|
||||
When the callback is invoked, there is no change to the devices listed by
|
||||
PortAudio. To update PortAudio's view of the device list, the client
|
||||
must call Pa_RefreshDeviceList(). This must not be done from within
|
||||
the callback.
|
||||
|
||||
The callback may occur on any thread. The client should not call other PortAudio
|
||||
functions from within a PaDevicesChangedCallback. In particular, the client
|
||||
should not directly call Pa_RefreshDeviceList() from within the notification
|
||||
callback. Instead, the client should set an atomic flag, or queue a message to
|
||||
notify a controller thread that would then call Pa_RefreshDeviceList().
|
||||
|
||||
@param userData The userData parameter supplied to Pa_SetDevicesChangedCallback()
|
||||
|
||||
@see Pa_SetDevicesChangedCallback
|
||||
*/
|
||||
typedef void PaDevicesChangedCallback( void *userData );
|
||||
|
||||
|
||||
/** Register a callback function that will be called when native audio devices
|
||||
become available or unavailable, and when native default devices change.
|
||||
See the description of PaDevicesChangedCallback for further details about
|
||||
when the callback will be called.
|
||||
|
||||
@param userData A client supplied pointer that is passed to the devices changed
|
||||
callback.
|
||||
|
||||
@param devicesChangedCallback a pointer to a callback function with the same
|
||||
signature as PaDevicesChangedCallback. Passing NULL for this parameter will
|
||||
un-register a previously registered callback function.
|
||||
|
||||
@return on success returns paNoError, otherwise an error code indicating the
|
||||
cause of the error.
|
||||
|
||||
@note Only one function pointer may be registered at a time.
|
||||
|
||||
@see PaDevicesChangedCallback
|
||||
*/
|
||||
PaError Pa_SetDevicesChangedCallback( void *userData, PaDevicesChangedCallback* devicesChangedCallback );
|
||||
|
||||
|
||||
/** The type used to represent monotonic time in seconds. PaTime is
|
||||
used for the fields of the PaStreamCallbackTimeInfo argument to the
|
||||
PaStreamCallback and as the result of Pa_GetStreamTime().
|
||||
|
|
@ -477,12 +556,30 @@ typedef unsigned long PaSampleFormat;
|
|||
|
||||
#define paNonInterleaved ((PaSampleFormat) 0x80000000) /**< @see PaSampleFormat */
|
||||
|
||||
|
||||
/** An integer id that uniquely identifies a device, so long as the device is
|
||||
plugged in and PortAudio is initialized. Its purpose is to identify (correlate)
|
||||
devices across calls to Pa_RefreshDeviceList().
|
||||
|
||||
Each device reported by Pa_GetDeviceInfo() has a distinct connectionId.
|
||||
|
||||
PortAudio makes a best-effort to ensure that a persistent underlying device
|
||||
has the same connectionId before and after a single call to Pa_RefreshDeviceList().
|
||||
|
||||
If a device is disconnected and later reconnected it may be assigned a new
|
||||
connectionId.
|
||||
|
||||
@see Pa_RefreshDeviceList()
|
||||
*/
|
||||
typedef unsigned long PaDeviceConnectionId;
|
||||
|
||||
|
||||
/** A structure providing information and capabilities of PortAudio devices.
|
||||
Devices may support input, output or both input and output.
|
||||
*/
|
||||
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*/
|
||||
|
||||
|
|
@ -497,6 +594,8 @@ typedef struct PaDeviceInfo
|
|||
PaTime defaultHighOutputLatency;
|
||||
|
||||
double defaultSampleRate;
|
||||
|
||||
PaDeviceConnectionId connectionId; /**< @see PaDeviceConnectionId */
|
||||
} PaDeviceInfo;
|
||||
|
||||
|
||||
|
|
@ -1201,7 +1300,6 @@ PaError Pa_GetSampleSize( PaSampleFormat format );
|
|||
void Pa_Sleep( long msec );
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
|
|
|
|||
|
|
@ -76,11 +76,13 @@
|
|||
#include "pa_stream.h"
|
||||
#include "pa_trace.h" /* still usefull?*/
|
||||
#include "pa_debugprint.h"
|
||||
#include "pa_hotplug.h"
|
||||
|
||||
#ifndef PA_SVN_REVISION
|
||||
#include "pa_svnrevision.h"
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* This is incremented if we make incompatible API changes.
|
||||
* This version scheme is based loosely on http://semver.org/
|
||||
|
|
@ -161,6 +163,8 @@ static int deviceCount_ = 0;
|
|||
|
||||
PaUtilStreamRepresentation *firstOpenStream_ = NULL;
|
||||
|
||||
PaDevicesChangedCallback* devicesChangedCallback_ = NULL;
|
||||
void* devicesChangedCallbackUserData_ = NULL;
|
||||
|
||||
#define PA_IS_INITIALISED_ (initializationCount_ != 0)
|
||||
|
||||
|
|
@ -371,6 +375,9 @@ 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_;
|
||||
|
|
@ -396,6 +403,8 @@ PaError Pa_Terminate( void )
|
|||
|
||||
TerminateHostApis();
|
||||
|
||||
PaUtil_TerminateHotPlug();
|
||||
|
||||
PaUtil_DumpTraceMessages();
|
||||
}
|
||||
result = paNoError;
|
||||
|
|
@ -744,6 +753,166 @@ PaDeviceIndex Pa_GetDefaultOutputDevice( void )
|
|||
}
|
||||
|
||||
|
||||
PaError Pa_RefreshDeviceList( void )
|
||||
{
|
||||
PaError result = paNoError;
|
||||
void **scanResults = NULL;
|
||||
int *deviceCounts = NULL;
|
||||
int i;
|
||||
int baseDeviceIndex;
|
||||
|
||||
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*) * hostApisCount_ );
|
||||
if( !scanResults )
|
||||
{
|
||||
result = paInsufficientMemory;
|
||||
goto done;
|
||||
}
|
||||
|
||||
deviceCounts = ( int * ) PaUtil_AllocateMemory( sizeof( int ) * hostApisCount_ );
|
||||
if( !deviceCounts )
|
||||
{
|
||||
result = paInsufficientMemory;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Phase 1: Perform a scan of new devices */
|
||||
for( i = 0 ; i < hostApisCount_ ; ++i )
|
||||
{
|
||||
PaUtilHostApiRepresentation *hostApi = hostApis_[i];
|
||||
if( hostApi->ScanDeviceInfos == NULL )
|
||||
continue;
|
||||
|
||||
PA_DEBUG(( "Scanning new device list for host api %d.\n",i));
|
||||
result = hostApi->ScanDeviceInfos( hostApi, i, &scanResults[ i ], &deviceCounts[ i ] );
|
||||
if( result != paNoError )
|
||||
{
|
||||
/* On error, dispose scan results already obtained, then return. */
|
||||
int j = 0;
|
||||
for( j = 0 ; j < i ; ++j )
|
||||
{
|
||||
PaUtilHostApiRepresentation *hostApi = 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 ] );
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------- Can't fail from here on -------------- */
|
||||
|
||||
/* Phase 2: Commit the scan changes to each back-end */
|
||||
|
||||
defaultHostApiIndex_ = -1; /* indicates that we haven't determined the default host API yet */
|
||||
baseDeviceIndex = 0;
|
||||
deviceCount_ = 0;
|
||||
for( i = 0 ; i < hostApisCount_ ; ++i )
|
||||
{
|
||||
PaUtilHostApiRepresentation *hostApi = hostApis_[i];
|
||||
if( hostApi->CommitDeviceInfos == NULL )
|
||||
{
|
||||
/* Not yet implemented for this backend. Just
|
||||
assume that the baseDeviceIndex and the deviceCount_ are
|
||||
incremented according to the values in the info */
|
||||
baseDeviceIndex += hostApi->info.deviceCount;
|
||||
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 );
|
||||
|
||||
/* the first successfully initialized host API with a default input *or*
|
||||
output device is used as the default host API.
|
||||
*/
|
||||
if( (defaultHostApiIndex_ == -1) &&
|
||||
( hostApi->info.defaultInputDevice != paNoDevice
|
||||
|| hostApi->info.defaultOutputDevice != paNoDevice ) )
|
||||
{
|
||||
defaultHostApiIndex_ = i;
|
||||
}
|
||||
|
||||
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;
|
||||
deviceCount_ += hostApi->info.deviceCount;
|
||||
}
|
||||
|
||||
/* if no host APIs have devices, the default host API is the first initialized host API */
|
||||
if( defaultHostApiIndex_ == -1 )
|
||||
defaultHostApiIndex_ = 0;
|
||||
|
||||
done:
|
||||
|
||||
if( scanResults )
|
||||
PaUtil_FreeMemory( scanResults );
|
||||
|
||||
if( deviceCounts )
|
||||
PaUtil_FreeMemory( deviceCounts );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
PaError Pa_SetDevicesChangedCallback( void *userData, PaStreamFinishedCallback* devicesChangedCallback )
|
||||
{
|
||||
PaUtil_LockHotPlug();
|
||||
devicesChangedCallback_ = devicesChangedCallback;
|
||||
devicesChangedCallbackUserData_ = userData;
|
||||
PaUtil_UnlockHotPlug();
|
||||
return paNoError;
|
||||
}
|
||||
|
||||
/* Called by platform hotplug implementation whenever a OS audio device change has been detected */
|
||||
void PaUtil_DevicesChanged(unsigned state, void* pData)
|
||||
{
|
||||
(void)state;
|
||||
(void)pData;
|
||||
PaUtil_LockHotPlug();
|
||||
if (devicesChangedCallback_)
|
||||
{
|
||||
(devicesChangedCallback_)(devicesChangedCallbackUserData_);
|
||||
}
|
||||
PaUtil_UnlockHotPlug();
|
||||
}
|
||||
|
||||
static PaDeviceConnectionId nextDeviceConnectionId_ = 1000;
|
||||
|
||||
PaDeviceConnectionId PaUtil_MakeDeviceConnectionId( void )
|
||||
{
|
||||
/* FIXME: if the counter wraps around we may want to ensure that
|
||||
we are not issuing an ID that belongs to a current device.
|
||||
we need to honor the invariant "no two devices have the same connection id"
|
||||
*/
|
||||
return nextDeviceConnectionId_++;
|
||||
}
|
||||
|
||||
|
||||
const PaDeviceInfo* Pa_GetDeviceInfo( PaDeviceIndex device )
|
||||
{
|
||||
int hostSpecificDeviceIndex;
|
||||
|
|
@ -1806,4 +1975,3 @@ PaError Pa_GetSampleSize( PaSampleFormat format )
|
|||
|
||||
return (PaError) result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -171,6 +171,9 @@ typedef struct PaUtilPrivatePaFrontHostApiInfo {
|
|||
}PaUtilPrivatePaFrontHostApiInfo;
|
||||
|
||||
|
||||
PaDeviceConnectionId PaUtil_MakeDeviceConnectionId( void );
|
||||
|
||||
|
||||
/** The common header for all data structures whose pointers are passed through
|
||||
the hostApiSpecificStreamInfo field of the PaStreamParameters structure.
|
||||
Note that in order to keep the public PortAudio interface clean, this structure
|
||||
|
|
@ -322,6 +325,119 @@ typedef struct PaUtilHostApiRepresentation {
|
|||
const PaStreamParameters *inputParameters,
|
||||
const PaStreamParameters *outputParameters,
|
||||
double sampleRate );
|
||||
|
||||
/** ScanDeviceInfos, CommitDeviceInfos, DisposeDeviceInfos are used to refresh the device list.
|
||||
|
||||
You can think of the caller doing something like:
|
||||
|
||||
void *scanResults = 0;
|
||||
int newDeviceCount = 0;
|
||||
if( hostApi->ScanDeviceInfos(hostApi, hostApiIndex, &scanResults, &newDeviceCount) == paNoError )
|
||||
{
|
||||
... do other stuff ...
|
||||
|
||||
if( ok to commit )
|
||||
{
|
||||
hostApi->CommitDeviceInfos(hostApi, hostApiIndex, scanResults, newDeviceCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
hostApi->DisposeDeviceInfos(hostApi, hostApiIndex, scanResults, newDeviceCount);
|
||||
}
|
||||
}
|
||||
|
||||
NB: ScanDeviceInfos may be called on all host APIs first, prior to pa_front
|
||||
deciding whether to commit or cleanup.
|
||||
*/
|
||||
|
||||
/**
|
||||
Scan for current native device information and compute a new device count.
|
||||
Preparation step of a two-stage transaction that updates the active device list.
|
||||
|
||||
@param hostApi The target host API.
|
||||
|
||||
@param index The host API index of the target host API.
|
||||
|
||||
@param scanResults (OUT) Result parameter. Upon success, set to point to an
|
||||
opaque structure containing collected device information. Ignored on failure.
|
||||
|
||||
@param newDeviceCount (Out) Result parameter. Upon success, set to the
|
||||
number of discovered devices. Ignored on failure.
|
||||
|
||||
@return on success returns paNoError, otherwise an error code indicating
|
||||
the cause of the error.
|
||||
|
||||
This function should not make any visible changes to the host API's internal
|
||||
state. In particular, it should not update the active device list, device
|
||||
information or device count.
|
||||
|
||||
If the function returns successfully, 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.
|
||||
|
||||
@note CommitDeviceInfos and DisposeDeviceInfos should not fail, therefore
|
||||
ScanDeviceInfos should perform all necessary preparations,
|
||||
such as memory allocation, so that CommitDeviceInfos can always execute
|
||||
without failure.
|
||||
|
||||
@see CommitDeviceInfos, DisposeDeviceInfos
|
||||
*/
|
||||
PaError (*ScanDeviceInfos)( struct PaUtilHostApiRepresentation *hostApi,
|
||||
PaHostApiIndex index,
|
||||
void **scanResults,
|
||||
int *newDeviceCount );
|
||||
|
||||
/**
|
||||
Update the active device list with new device information that was
|
||||
returned by ScanDeviceInfos(). This is the final commit step of a two-stage
|
||||
transaction that updates the active device list.
|
||||
|
||||
As this function is not permitted to fail, it should be very simple:
|
||||
swap in new device information and free the old information.
|
||||
|
||||
@param hostApi The target host API.
|
||||
|
||||
@param index The host API index of the target host API.
|
||||
|
||||
@param scanResults An opaque structure containing collected device
|
||||
information previously returned by ScanDeviceInfos. This is the information
|
||||
that will be installed into the active device list.
|
||||
|
||||
@param newDeviceCount The number of discovered devices previously returned
|
||||
by ScanDeviceInfos. This is the new number of devices in the active device
|
||||
list.
|
||||
|
||||
@note This function is not permitted to fail. It should always return paNoError.
|
||||
|
||||
@see ScanDeviceInfos, DisposeDeviceInfos
|
||||
*/
|
||||
PaError (*CommitDeviceInfos)( struct PaUtilHostApiRepresentation *hostApi,
|
||||
PaHostApiIndex index,
|
||||
void *scanResults,
|
||||
int deviceCount );
|
||||
|
||||
/**
|
||||
Free device information that was returned by ScanDeviceInfos. This is
|
||||
used in the cleanup process for a failed two-stage transaction.
|
||||
|
||||
@param hostApi The target host API.
|
||||
|
||||
@param index The host API index of the target host API.
|
||||
|
||||
@param scanResults An opaque structure containing collected device
|
||||
information previously returned by ScanDeviceInfos. scanResults will be
|
||||
freed by this function.
|
||||
|
||||
@param newDeviceCount The number of discovered devices previously returned
|
||||
by ScanDeviceInfos.
|
||||
|
||||
@note This function is not permitted to fail. It should always return paNoError.
|
||||
|
||||
@see CommitDeviceInfos, ScanDeviceInfos
|
||||
*/
|
||||
PaError (*DisposeDeviceInfos)( struct PaUtilHostApiRepresentation *hostApi, void *scanResults, int deviceCount );
|
||||
|
||||
} PaUtilHostApiRepresentation;
|
||||
|
||||
|
||||
|
|
|
|||
98
src/common/pa_hotplug.h
Normal file
98
src/common/pa_hotplug.h
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
#ifndef PA_HOTPLUG_H
|
||||
#define PA_HOTPLUG_H
|
||||
/*
|
||||
* $Id$
|
||||
* Portable Audio I/O Library
|
||||
* hotplug interface and utilities
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2016 Ross Bencina, Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* Any person wishing to distribute modifications to the Software is
|
||||
* requested to send the modifications to the original developer so that
|
||||
* they can be incorporated into the canonical version. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
/** @file
|
||||
@ingroup common_src
|
||||
|
||||
@brief Utilities for implementing hotplug support.
|
||||
*/
|
||||
|
||||
#include "portaudio.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif /* __cplusplus */
|
||||
|
||||
/** Init/terminate hotplug notification engine.
|
||||
|
||||
At the moment there is on hotplug implementation per platform.
|
||||
It is responsible for posting devices changed notifications
|
||||
by calling PaUtil_DevicesChanged.
|
||||
|
||||
Once we support multiple notification mechanisms we'll probably
|
||||
have the host APIs init and terminate their own notification
|
||||
engines (using reference counting?) e.g. wasapi will have its own,
|
||||
but other windows APIs will use the global windows notifier.
|
||||
|
||||
Implemented in pa_win_hotplug.c
|
||||
*/
|
||||
void PaUtil_InitializeHotPlug();
|
||||
void PaUtil_TerminateHotPlug();
|
||||
|
||||
|
||||
/** Invoke the client's registered devices changed notification.
|
||||
|
||||
@param first 0 = unknown, 1 = insertion, 2 = removal
|
||||
@param second Host specific device change info (in windows it is the (unicode) device path)
|
||||
|
||||
Parameters are currently ignored. TODO REVIEW
|
||||
|
||||
Implemented in pa_front.c
|
||||
*/
|
||||
void PaUtil_DevicesChanged(unsigned, void*);
|
||||
|
||||
|
||||
/** Lock/unlock a mutex used to protect the devices changed callback.
|
||||
Used by pa_front.c to synchronise notification callbacks and client
|
||||
requests to set/clear the device callback.
|
||||
|
||||
Coded like this because we don't have a cross-platform mutex.
|
||||
|
||||
Implemented in pa_win_hotplug.c
|
||||
*/
|
||||
void PaUtil_LockHotPlug();
|
||||
void PaUtil_UnlockHotPlug();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif /* __cplusplus */
|
||||
#endif /* PA_HOTPLUG_H */
|
||||
|
|
@ -757,6 +757,9 @@ PaError PaAlsa_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
|
|||
(*hostApi)->Terminate = Terminate;
|
||||
(*hostApi)->OpenStream = OpenStream;
|
||||
(*hostApi)->IsFormatSupported = IsFormatSupported;
|
||||
(*hostApi)->ScanDeviceInfos = 0;
|
||||
(*hostApi)->CommitDeviceInfos = 0;
|
||||
(*hostApi)->DisposeDeviceInfos = 0;
|
||||
|
||||
/** If AlsaErrorHandler is to be used, do not forget to unregister callback pointer in
|
||||
Terminate function.
|
||||
|
|
@ -1186,9 +1189,11 @@ static PaError FillInDevInfo( PaAlsaHostApiRepresentation *alsaApi, HwDevInfo* d
|
|||
}
|
||||
}
|
||||
|
||||
baseDeviceInfo->structVersion = 2;
|
||||
baseDeviceInfo->structVersion = 3;
|
||||
baseDeviceInfo->hostApi = alsaApi->hostApiIndex;
|
||||
baseDeviceInfo->name = deviceHwInfo->name;
|
||||
deviceInfo->connectionId = PaUtil_MakeDeviceConnectionId();
|
||||
|
||||
devInfo->alsaName = deviceHwInfo->alsaName;
|
||||
devInfo->isPlug = deviceHwInfo->isPlug;
|
||||
|
||||
|
|
|
|||
|
|
@ -636,7 +636,7 @@ static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostA
|
|||
hpiDevice->streamIndex = j;
|
||||
hpiDevice->streamIsOutput = 0;
|
||||
/* Set common PortAudio device stats */
|
||||
baseDeviceInfo->structVersion = 2;
|
||||
baseDeviceInfo->structVersion = 3;
|
||||
/* Make sure name string is owned by API info structure */
|
||||
sprintf( srcName,
|
||||
"Adapter %d (%4X) - Input Stream %d", i+1, type, j+1 );
|
||||
|
|
@ -656,6 +656,7 @@ static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostA
|
|||
/* HPI interface can actually handle any sampling rate to 1 Hz accuracy,
|
||||
* so this default is as good as any */
|
||||
baseDeviceInfo->defaultSampleRate = 44100;
|
||||
baseDeviceInfo->connectionId = PaUtil_MakeDeviceConnectionId();
|
||||
|
||||
/* Store device in global PortAudio list */
|
||||
hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice;
|
||||
|
|
@ -678,7 +679,7 @@ static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostA
|
|||
hpiDevice->streamIndex = j;
|
||||
hpiDevice->streamIsOutput = 1;
|
||||
/* Set common PortAudio device stats */
|
||||
baseDeviceInfo->structVersion = 2;
|
||||
baseDeviceInfo->structVersion = 3;
|
||||
/* Make sure name string is owned by API info structure */
|
||||
sprintf( srcName,
|
||||
"Adapter %d (%4X) - Output Stream %d", i+1, type, j+1 );
|
||||
|
|
@ -698,6 +699,7 @@ static PaError PaAsiHpi_BuildDeviceList( PaAsiHpiHostApiRepresentation *hpiHostA
|
|||
/* HPI interface can actually handle any sampling rate to 1 Hz accuracy,
|
||||
* so this default is as good as any */
|
||||
baseDeviceInfo->defaultSampleRate = 44100;
|
||||
baseDeviceInfo->connectionId = PaUtil_MakeDeviceConnectionId();
|
||||
|
||||
/* Store device in global PortAudio list */
|
||||
hostApi->deviceInfos[deviceIndex++] = (PaDeviceInfo *) hpiDevice;
|
||||
|
|
@ -771,6 +773,9 @@ PaError PaAsiHpi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
|
|||
(*hostApi)->Terminate = Terminate;
|
||||
(*hostApi)->OpenStream = OpenStream;
|
||||
(*hostApi)->IsFormatSupported = IsFormatSupported;
|
||||
(*hostApi)->ScanDeviceInfos = 0;
|
||||
(*hostApi)->CommitDeviceInfos = 0;
|
||||
(*hostApi)->DisposeDeviceInfos = 0;
|
||||
|
||||
PaUtil_InitializeStreamInterface( &hpiHostApi->callbackStreamInterface, CloseStream, StartStream,
|
||||
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
|
||||
|
|
|
|||
|
|
@ -1322,10 +1322,11 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
|
|||
PaAsioDeviceInfo *asioDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
|
||||
PaDeviceInfo *deviceInfo = &asioDeviceInfo->commonDeviceInfo;
|
||||
|
||||
deviceInfo->structVersion = 2;
|
||||
deviceInfo->structVersion = 3;
|
||||
deviceInfo->hostApi = hostApiIndex;
|
||||
|
||||
deviceInfo->name = names[i];
|
||||
deviceInfo->connectionId = PaUtil_MakeDeviceConnectionId();
|
||||
|
||||
if( InitPaDeviceInfoFromAsioDriver( asioHostApi, names[i], i, deviceInfo, asioDeviceInfo ) == paNoError )
|
||||
{
|
||||
|
|
@ -1356,6 +1357,9 @@ PaError PaAsio_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
|
|||
(*hostApi)->Terminate = Terminate;
|
||||
(*hostApi)->OpenStream = OpenStream;
|
||||
(*hostApi)->IsFormatSupported = IsFormatSupported;
|
||||
(*hostApi)->ScanDeviceInfos = 0;
|
||||
(*hostApi)->CommitDeviceInfos = 0;
|
||||
(*hostApi)->DisposeDeviceInfos = 0;
|
||||
|
||||
PaUtil_InitializeStreamInterface( &asioHostApi->callbackStreamInterface, CloseStream, StartStream,
|
||||
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
|
||||
|
|
|
|||
|
|
@ -659,7 +659,7 @@ static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi,
|
|||
|
||||
memset(deviceInfo, 0, sizeof(PaDeviceInfo));
|
||||
|
||||
deviceInfo->structVersion = 2;
|
||||
deviceInfo->structVersion = 3;
|
||||
deviceInfo->hostApi = hostApiIndex;
|
||||
|
||||
/* Get the device name using CFString */
|
||||
|
|
@ -693,6 +693,7 @@ static PaError InitializeDeviceInfo( PaMacAUHAL *auhalHostApi,
|
|||
CFRelease(nameRef);
|
||||
}
|
||||
deviceInfo->name = name;
|
||||
deviceInfo->connectionId = PaUtil_MakeDeviceConnectionId();
|
||||
|
||||
/* Try to get the default sample rate. Don't fail if we can't get this. */
|
||||
propSize = sizeof(Float64);
|
||||
|
|
@ -825,6 +826,9 @@ PaError PaMacCore_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIn
|
|||
(*hostApi)->Terminate = Terminate;
|
||||
(*hostApi)->OpenStream = OpenStream;
|
||||
(*hostApi)->IsFormatSupported = IsFormatSupported;
|
||||
(*hostApi)->ScanDeviceInfos = 0;
|
||||
(*hostApi)->CommitDeviceInfos = 0;
|
||||
(*hostApi)->DisposeDeviceInfos = 0;
|
||||
|
||||
PaUtil_InitializeStreamInterface( &auhalHostApi->callbackStreamInterface,
|
||||
CloseStream, StartStream,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -573,7 +573,7 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi )
|
|||
strlen(client_names[client_index]) + 1 ), paInsufficientMemory );
|
||||
strcpy( (char *)curDevInfo->name, client_names[client_index] );
|
||||
|
||||
curDevInfo->structVersion = 2;
|
||||
curDevInfo->structVersion = 3;
|
||||
curDevInfo->hostApi = jackApi->hostApiIndex;
|
||||
|
||||
/* JACK is very inflexible: there is one sample rate the whole
|
||||
|
|
@ -634,6 +634,8 @@ static PaError BuildDeviceList( PaJackHostApiRepresentation *jackApi )
|
|||
commonApi->info.defaultInputDevice = client_index;
|
||||
if( commonApi->info.defaultOutputDevice == paNoDevice && curDevInfo->maxOutputChannels > 0 )
|
||||
commonApi->info.defaultOutputDevice = client_index;
|
||||
|
||||
deviceInfo->connectionId = PaUtil_MakeDeviceConnectionId();
|
||||
}
|
||||
|
||||
error:
|
||||
|
|
@ -756,6 +758,9 @@ PaError PaJack_Initialize( PaUtilHostApiRepresentation **hostApi,
|
|||
(*hostApi)->Terminate = Terminate;
|
||||
(*hostApi)->OpenStream = OpenStream;
|
||||
(*hostApi)->IsFormatSupported = IsFormatSupported;
|
||||
(*hostApi)->ScanDeviceInfos = 0;
|
||||
(*hostApi)->CommitDeviceInfos = 0;
|
||||
(*hostApi)->DisposeDeviceInfos = 0;
|
||||
|
||||
PaUtil_InitializeStreamInterface( &jackHostApi->callbackStreamInterface,
|
||||
CloseStream, StartStream,
|
||||
|
|
|
|||
|
|
@ -256,6 +256,9 @@ PaError PaOSS_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiIndex
|
|||
(*hostApi)->Terminate = Terminate;
|
||||
(*hostApi)->OpenStream = OpenStream;
|
||||
(*hostApi)->IsFormatSupported = IsFormatSupported;
|
||||
(*hostApi)->ScanDeviceInfos = 0;
|
||||
(*hostApi)->CommitDeviceInfos = 0;
|
||||
(*hostApi)->DisposeDeviceInfos = 0;
|
||||
|
||||
PA_ENSURE( BuildDeviceList( ossHostApi ) );
|
||||
|
||||
|
|
@ -295,7 +298,7 @@ PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name,
|
|||
{
|
||||
PaError result = paNoError;
|
||||
|
||||
deviceInfo->structVersion = 2;
|
||||
deviceInfo->structVersion = 3;
|
||||
if( allocations )
|
||||
{
|
||||
size_t len = strlen( name ) + 1;
|
||||
|
|
@ -313,6 +316,7 @@ PaError PaUtil_InitializeDeviceInfo( PaDeviceInfo *deviceInfo, const char *name,
|
|||
deviceInfo->defaultHighInputLatency = defaultHighInputLatency;
|
||||
deviceInfo->defaultHighOutputLatency = defaultHighOutputLatency;
|
||||
deviceInfo->defaultSampleRate = defaultSampleRate;
|
||||
deviceInfo->connectionId = PaUtil_MakeDeviceConnectionId();
|
||||
|
||||
error:
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -209,6 +209,9 @@ PaError PaSkeleton_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiI
|
|||
(*hostApi)->Terminate = Terminate;
|
||||
(*hostApi)->OpenStream = OpenStream;
|
||||
(*hostApi)->IsFormatSupported = IsFormatSupported;
|
||||
(*hostApi)->ScanDeviceInfos = 0;
|
||||
(*hostApi)->CommitDeviceInfos = 0;
|
||||
(*hostApi)->DisposeDeviceInfos = 0;
|
||||
|
||||
PaUtil_InitializeStreamInterface( &skeletonHostApi->callbackStreamInterface, CloseStream, StartStream,
|
||||
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
|
||||
|
|
|
|||
|
|
@ -1563,7 +1563,7 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
|
|||
for (i = 0; i < paWasapi->deviceCount; ++i)
|
||||
{
|
||||
PaDeviceInfo *deviceInfo = &deviceInfoArray[i];
|
||||
deviceInfo->structVersion = 2;
|
||||
deviceInfo->structVersion = 3;
|
||||
deviceInfo->hostApi = hostApiIndex;
|
||||
|
||||
PA_DEBUG(("WASAPI: device idx: %02d\n", i));
|
||||
|
|
@ -1796,6 +1796,8 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
|
|||
break;
|
||||
}
|
||||
|
||||
deviceInfo->connectionId = PaUtil_MakeDeviceConnectionId();
|
||||
|
||||
(*hostApi)->deviceInfos[i] = deviceInfo;
|
||||
++(*hostApi)->info.deviceCount;
|
||||
}
|
||||
|
|
@ -1804,6 +1806,9 @@ PaError PaWasapi_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
|
|||
(*hostApi)->Terminate = Terminate;
|
||||
(*hostApi)->OpenStream = OpenStream;
|
||||
(*hostApi)->IsFormatSupported = IsFormatSupported;
|
||||
(*hostApi)->ScanDeviceInfos = 0;
|
||||
(*hostApi)->CommitDeviceInfos = 0;
|
||||
(*hostApi)->DisposeDeviceInfos = 0;
|
||||
|
||||
PaUtil_InitializeStreamInterface( &paWasapi->callbackStreamInterface, CloseStream, StartStream,
|
||||
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
|
||||
|
|
|
|||
|
|
@ -3442,6 +3442,22 @@ static unsigned GetNameIndex(PaNameHashObject* obj, const wchar_t* name, const B
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void FreeHostApiDeviceInfos( PaUtilAllocationGroup *allocations, PaDeviceInfo **deviceInfos, int deviceCount )
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < deviceCount; ++i)
|
||||
{
|
||||
PaWinWdmDeviceInfo* pDevice = (PaWinWdmDeviceInfo*)deviceInfos[i];
|
||||
if (pDevice->filter != 0)
|
||||
{
|
||||
FilterFree(pDevice->filter);
|
||||
}
|
||||
}
|
||||
|
||||
PaUtil_GroupFreeMemory( allocations, deviceInfos[0] ); /* all device info structs are allocated in a block so we can destroy them here */
|
||||
PaUtil_GroupFreeMemory( allocations, deviceInfos );
|
||||
}
|
||||
|
||||
static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaHostApiIndex hostApiIndex, void **scanResults, int *newDeviceCount )
|
||||
{
|
||||
PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
|
||||
|
|
@ -3514,7 +3530,7 @@ static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaH
|
|||
for( i = 0 ; i < totalDeviceCount; ++i )
|
||||
{
|
||||
PaDeviceInfo *deviceInfo = &deviceInfoArray[i].inheritedDeviceInfo;
|
||||
deviceInfo->structVersion = 2;
|
||||
deviceInfo->structVersion = 3;
|
||||
deviceInfo->hostApi = hostApiIndex;
|
||||
deviceInfo->name = 0;
|
||||
outArgument->deviceInfos[ i ] = deviceInfo;
|
||||
|
|
@ -3557,11 +3573,13 @@ static PaError ScanDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, PaH
|
|||
|
||||
wdmDeviceInfo->filter = pFilter;
|
||||
|
||||
deviceInfo->structVersion = 2;
|
||||
deviceInfo->structVersion = 3;
|
||||
deviceInfo->hostApi = hostApiIndex;
|
||||
deviceInfo->name = wdmDeviceInfo->compositeName;
|
||||
/* deviceInfo->hostApiSpecificDeviceInfo = &pFilter->devInfo; */
|
||||
|
||||
deviceInfo->connectionId = PaUtil_MakeDeviceConnectionId();
|
||||
|
||||
wdmDeviceInfo->pin = pin->pinId;
|
||||
|
||||
/* Get the name of the "device" */
|
||||
|
|
@ -3706,22 +3724,17 @@ static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, P
|
|||
{
|
||||
PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
|
||||
|
||||
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 )
|
||||
{
|
||||
PaWinWDMScanDeviceInfosResults* localScanResults = (PaWinWDMScanDeviceInfosResults*)PaUtil_GroupAllocateMemory(
|
||||
wdmHostApi->allocations, sizeof(PaWinWDMScanDeviceInfosResults));
|
||||
localScanResults->deviceInfos = hostApi->deviceInfos;
|
||||
|
||||
DisposeDeviceInfos(hostApi, &localScanResults, hostApi->info.deviceCount);
|
||||
|
||||
FreeHostApiDeviceInfos( wdmHostApi->allocations, hostApi->deviceInfos, hostApi->info.deviceCount );
|
||||
hostApi->deviceInfos = NULL;
|
||||
}
|
||||
|
||||
hostApi->info.deviceCount = 0;
|
||||
hostApi->info.defaultInputDevice = paNoDevice;
|
||||
hostApi->info.defaultOutputDevice = paNoDevice;
|
||||
|
||||
if( scanResults != NULL )
|
||||
{
|
||||
PaWinWDMScanDeviceInfosResults *scanDeviceInfosResults = ( PaWinWDMScanDeviceInfosResults * ) scanResults;
|
||||
|
|
@ -3746,7 +3759,7 @@ static PaError CommitDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, P
|
|||
|
||||
static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi, void *scanResults, int deviceCount )
|
||||
{
|
||||
PaWinWdmHostApiRepresentation *winDsHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
|
||||
PaWinWdmHostApiRepresentation *wdmHostApi = (PaWinWdmHostApiRepresentation*)hostApi;
|
||||
|
||||
if( scanResults != NULL )
|
||||
{
|
||||
|
|
@ -3754,21 +3767,10 @@ static PaError DisposeDeviceInfos( struct PaUtilHostApiRepresentation *hostApi,
|
|||
|
||||
if( scanDeviceInfosResults->deviceInfos )
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < deviceCount; ++i)
|
||||
{
|
||||
PaWinWdmDeviceInfo* pDevice = (PaWinWdmDeviceInfo*)scanDeviceInfosResults->deviceInfos[i];
|
||||
if (pDevice->filter != 0)
|
||||
{
|
||||
FilterFree(pDevice->filter);
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
FreeHostApiDeviceInfos( wdmHostApi->allocations, scanDeviceInfosResults->deviceInfos, hostApi->info.deviceCount );
|
||||
}
|
||||
|
||||
PaUtil_GroupFreeMemory( winDsHostApi->allocations, scanDeviceInfosResults );
|
||||
PaUtil_GroupFreeMemory( wdmHostApi->allocations, scanDeviceInfosResults );
|
||||
}
|
||||
|
||||
return paNoError;
|
||||
|
|
@ -3853,11 +3855,10 @@ PaError PaWinWdm_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
|
|||
(*hostApi)->Terminate = Terminate;
|
||||
(*hostApi)->OpenStream = OpenStream;
|
||||
(*hostApi)->IsFormatSupported = IsFormatSupported;
|
||||
/* In preparation for hotplug
|
||||
(*hostApi)->ScanDeviceInfos = ScanDeviceInfos;
|
||||
(*hostApi)->CommitDeviceInfos = CommitDeviceInfos;
|
||||
(*hostApi)->DisposeDeviceInfos = DisposeDeviceInfos;
|
||||
*/
|
||||
|
||||
PaUtil_InitializeStreamInterface( &wdmHostApi->callbackStreamInterface, CloseStream, StartStream,
|
||||
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
|
||||
GetStreamTime, GetStreamCpuLoad,
|
||||
|
|
@ -3900,10 +3901,11 @@ static void Terminate( struct PaUtilHostApiRepresentation *hostApi )
|
|||
|
||||
if( wdmHostApi)
|
||||
{
|
||||
PaWinWDMScanDeviceInfosResults* localScanResults = (PaWinWDMScanDeviceInfosResults*)PaUtil_GroupAllocateMemory(
|
||||
wdmHostApi->allocations, sizeof(PaWinWDMScanDeviceInfosResults));
|
||||
localScanResults->deviceInfos = hostApi->deviceInfos;
|
||||
DisposeDeviceInfos(hostApi, localScanResults, hostApi->info.deviceCount);
|
||||
if( hostApi->deviceInfos )
|
||||
{
|
||||
FreeHostApiDeviceInfos( wdmHostApi->allocations, hostApi->deviceInfos, hostApi->info.deviceCount );
|
||||
hostApi->deviceInfos = NULL;
|
||||
}
|
||||
|
||||
if( wdmHostApi->allocations )
|
||||
{
|
||||
|
|
|
|||
|
|
@ -789,6 +789,8 @@ static PaError InitializeInputDeviceInfo( PaWinMmeHostApiRepresentation *winMmeH
|
|||
DetectDefaultSampleRate( winMmeDeviceInfo, winMmeInputDeviceId,
|
||||
QueryInputWaveFormatEx, deviceInfo->maxInputChannels );
|
||||
|
||||
deviceInfo->connectionId = PaUtil_MakeDeviceConnectionId();
|
||||
|
||||
*success = 1;
|
||||
|
||||
error:
|
||||
|
|
@ -924,6 +926,8 @@ static PaError InitializeOutputDeviceInfo( PaWinMmeHostApiRepresentation *winMme
|
|||
DetectDefaultSampleRate( winMmeDeviceInfo, winMmeOutputDeviceId,
|
||||
QueryOutputWaveFormatEx, deviceInfo->maxOutputChannels );
|
||||
|
||||
deviceInfo->connectionId = PaUtil_MakeDeviceConnectionId();
|
||||
|
||||
*success = 1;
|
||||
|
||||
error:
|
||||
|
|
@ -1069,7 +1073,7 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
|
|||
UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
|
||||
PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
|
||||
PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
|
||||
deviceInfo->structVersion = 2;
|
||||
deviceInfo->structVersion = 3;
|
||||
deviceInfo->hostApi = hostApiIndex;
|
||||
|
||||
deviceInfo->maxInputChannels = 0;
|
||||
|
|
@ -1112,7 +1116,7 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
|
|||
UINT winMmeDeviceId = (UINT)((i==-1) ? WAVE_MAPPER : i);
|
||||
PaWinMmeDeviceInfo *wmmeDeviceInfo = &deviceInfoArray[ (*hostApi)->info.deviceCount ];
|
||||
PaDeviceInfo *deviceInfo = &wmmeDeviceInfo->inheritedDeviceInfo;
|
||||
deviceInfo->structVersion = 2;
|
||||
deviceInfo->structVersion = 3;
|
||||
deviceInfo->hostApi = hostApiIndex;
|
||||
|
||||
deviceInfo->maxInputChannels = 0;
|
||||
|
|
@ -1155,6 +1159,9 @@ PaError PaWinMme_Initialize( PaUtilHostApiRepresentation **hostApi, PaHostApiInd
|
|||
(*hostApi)->Terminate = Terminate;
|
||||
(*hostApi)->OpenStream = OpenStream;
|
||||
(*hostApi)->IsFormatSupported = IsFormatSupported;
|
||||
(*hostApi)->ScanDeviceInfos = 0;
|
||||
(*hostApi)->CommitDeviceInfos = 0;
|
||||
(*hostApi)->DisposeDeviceInfos = 0;
|
||||
|
||||
PaUtil_InitializeStreamInterface( &winMmeHostApi->callbackStreamInterface, CloseStream, StartStream,
|
||||
StopStream, AbortStream, IsStreamStopped, IsStreamActive,
|
||||
|
|
|
|||
348
src/os/win/pa_win_hotplug.c
Normal file
348
src/os/win/pa_win_hotplug.c
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* $Id$
|
||||
* Portable Audio I/O Library
|
||||
* Hotplug interface and utilities
|
||||
* Copyright (c) 2011-2016 Robert Bielik
|
||||
*
|
||||
* Based on the Open Source API proposed by Ross Bencina
|
||||
* Copyright (c) 1999-2016 Ross Bencina, Phil Burk
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files
|
||||
* (the "Software"), to deal in the Software without restriction,
|
||||
* including without limitation the rights to use, copy, modify, merge,
|
||||
* publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so,
|
||||
* subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||||
* ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
|
||||
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The text above constitutes the entire PortAudio license; however,
|
||||
* the PortAudio community also makes the following non-binding requests:
|
||||
*
|
||||
* Any person wishing to distribute modifications to the Software is
|
||||
* requested to send the modifications to the original developer so that
|
||||
* they can be incorporated into the canonical version. It is also
|
||||
* requested that these non-binding requests be included along with the
|
||||
* license above.
|
||||
*/
|
||||
|
||||
#include "pa_hotplug.h"
|
||||
#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
|
||||
|
||||
/* 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);
|
||||
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));
|
||||
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, (DWORD)-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);
|
||||
}
|
||||
50
test/patest_refresh_device_list.c
Normal file
50
test/patest_refresh_device_list.c
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#include <stdio.h>
|
||||
#include <assert.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 );
|
||||
assert( deviceInfo->structVersion >= 3 ); /* should be the case if all APIs have implemented connectionId */
|
||||
|
||||
printf( "%d (conn id: %lu) %s (%s)\n", i, deviceInfo->connectionId, deviceInfo->name, hostApiInfo->name );
|
||||
}
|
||||
}
|
||||
|
||||
static void devicesChangedCallback(void* p)
|
||||
{
|
||||
(void)p;
|
||||
|
||||
printf( "Portaudio device list have changed!\n" );
|
||||
}
|
||||
|
||||
int main(void);
|
||||
int main(void)
|
||||
{
|
||||
Pa_Initialize();
|
||||
|
||||
Pa_SetDevicesChangedCallback(NULL, devicesChangedCallback);
|
||||
|
||||
for(;;){
|
||||
printDevices();
|
||||
|
||||
printf( "press [enter] to update the device list. or q + [enter] to quit.\n" );
|
||||
if( getchar() == 'q' )
|
||||
break;
|
||||
|
||||
Pa_RefreshDeviceList();
|
||||
}
|
||||
|
||||
Pa_Terminate();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
Loading…
Reference in a new issue