Compare commits

...
Sign in to create a new pull request.
Author SHA1 Message Date
Ross Bencina
ca24be18da hotplug: update docs for Pa_Initialize() 2016-09-19 01:08:14 +10:00
Ross Bencina
2048a0a62f add comment about dsound default devices 2016-09-19 00:52:56 +10:00
Ross Bencina
d485a4b36d hotplug: Update doc comments for Pa_RefreshDeviceList() and PaDevicesChangedCallback to indicate that they reflect changes to default devices and default host API. 2016-09-19 00:46:19 +10:00
Ross Bencina
5549bca961 hotplug: Update defaultHostApiIndex_ in Pa_RefreshDeviceList() to reflect the first host API with an input or output device (previously this was left unchanged, which could result in a default host API with zero devices, and therefore no global default devices.) 2016-09-19 00:43:40 +10:00
Ross Bencina
82bdb95a6d hotplug: Clean up structure of Pa_RefreshDeviceList(). Fix to report error result for failed host api ScanDeviceInfos(). Previously it would return paNoError if it failed. 2016-09-19 00:39:26 +10:00
Ross Bencina
c2175e14f1 patest_refresh_device_list: Fix unused args error in main() (use int main(void) as elsewhere). Use %lu to printf unsigned long connectionId (should fix compilation error on Mac). 2016-09-18 19:20:58 +10:00
Ross Bencina
b95f3bd6a2 hotplug dsound: zero new device count on error 2016-09-12 02:46:40 +10:00
Ross Bencina
231ecf77c7 hotplug: fix signed/unsigned parameter warning in pa_win_hotplug.c 2016-09-10 23:47:21 +10:00
Ross Bencina
0a290773de fix missing new line at end of pa_front.c 2016-09-09 23:48:53 +10:00
Ross Bencina
46f97c7c20 Merge branch 'master' into hotplug 2016-09-03 14:19:23 +10:00
Ross Bencina
8e9e507e12 hotplug dsound: initial cut of properly working connectionIds. it works. it pulls device information from each device every time, and then reuses the old connectionId if the GUID is the same. This has the advantage of always pulling updated channel count information. it has the disadvantage of re-polling every device, not just newly inserted/removed ones. the alternative is to reuse the existing device information by copying it over, which could be done. 2016-09-03 01:57:29 +10:00
Ross Bencina
8d285faa5e hotplug dsound: remove resolved fixme comment 2016-09-03 00:30:18 +10:00
Ross Bencina
6840cda9ad hotplug: extended doc comments for ScanDeviceInfos, CommitDeviceInfos, DisposeDeviceInfos. Added requirement that commit and dispose should be implemented such that they can't fail (this is currently the case with DirectSound and WDM/KS implementations). 2016-09-03 00:11:56 +10:00
Ross Bencina
5c5d4fb711 hotplug directsound: factor out freeing of device info array to FreeDeviceInfos. Delete some unused variables. 2016-09-02 23:29:20 +10:00
Ross Bencina
9562fb8074 hotplug: further refinement of API doc comments 2016-09-02 21:47:43 +10:00
Ross Bencina
ec5b0687a8 fix .def file: rename Pa_RefreshDeviceList --> Pa_UpdateAvailableDeviceList 2016-09-02 21:12:10 +10:00
Ross Bencina
b45ec9ed0a Moved hotplug function prototypes from random locations to new header file src/common/pa_hotplug.h. Added draft documentation and licence headers. In pa_front.c moved devices changed code next to refresh device list code. 2016-09-02 20:52:42 +10:00
Ross Bencina
d41ba780b2 hotplug: added connectionId parameter to PaDeviceInfo and bumped struct version number. added PaUtil_MakeDeviceConnectionId() function and used it to initialize connectionId in all host APIs. updated patest_refresh_device_list to display connection id. NOTE: persistent connection ids have not been implemented in dsound and wdmks, so refreshing the device list will change connection ids. 2016-09-02 20:27:39 +10:00
Ross Bencina
abfcd708b4 hotplug: first cut doc comments for PaDevicesChangedCallback and Pa_SetDevicesChangedCallback 2016-09-02 19:43:27 +10:00
Ross Bencina
3f7c315f22 hotplug: rename Pa_UpdateAvailableDeviceList() to Pa_RefreshDeviceList(). Clean up doc comment for Pa_RefreshDeviceList(). 2016-09-02 19:27:47 +10:00
Ross Bencina
9761960aa5 wdmks hotplug: Make CommitDeviceInfos fail-safe by removing allocation of temporary. Factored out device info array freeing function from DisposeDeviceInfos into separate FreeHostApiDeviceInfos. 2016-09-02 19:08:39 +10:00
Ross Bencina
23b4bdd49a wdm/ks hotplug: fixed two bugs in CommitDeviceInfos() Firstly, hostApi->info.deviceCount was being zeroed before it was used to specify how many devices to free, so zero devices/filters would be freed. Secondly, a ptr ptr (**) to temporary scan results localScanResults was being pased to DisposeDeviceInfos() instead of a ptr (*). 2016-09-02 18:52:22 +10:00
Ross Bencina
89f8f75a27 Merge of old SVN hotplug branch into current git HEAD. Includes hotplug support for DirectSound, WDM/KS. Both are working running in debugger but WDM/KS seems to be crashing in stand-alone build. I removed the use of struct _PaInternalInfo in hotplug/pa_front.c to reduce the size of the change. pa_win_dsound.c involved the most intricate changes due to other changes that had been made in the mean time. I've also initialized function ptrs ScanDeviceInfos, CommitDeviceInfos, DisposeDeviceInfos so this should build on all platforms. 2016-08-27 17:51:40 +10:00
19 changed files with 1484 additions and 368 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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
View 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 */

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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;
}