2727
2828import atexit as _atexit
2929from cffi import FFI as _FFI
30+ from collections import namedtuple as _namedtuple
3031import os as _os
3132import platform as _platform
3233import sys as _sys
247248void PaMacCore_SetupStreamInfo( PaMacCoreStreamInfo *data, unsigned long flags );
248249void PaMacCore_SetupChannelMap( PaMacCoreStreamInfo *data, const SInt32 * const channelMap, unsigned long channelMapSize );
249250const char *PaMacCore_GetChannelName( int device, int channelIndex, bool input );
251+ PaError PaMacCore_GetBufferSizeRange( PaDeviceIndex device, long *minBufferSizeFrames, long *maxBufferSizeFrames );
250252#define paMacCoreChangeDeviceParameters 0x01
251253#define paMacCoreFailIfConversionRequired 0x02
252254#define paMacCoreConversionQualityMin 0x0100
265267
266268/* pa_asio.h */
267269
270+ PaError PaAsio_GetAvailableBufferSizes( PaDeviceIndex device, long *minBufferSizeFrames, long *maxBufferSizeFrames, long *preferredBufferSizeFrames, long *granularity );
271+ PaError PaAsio_GetInputChannelName( PaDeviceIndex device, int channelIndex, const char** channelName );
272+ PaError PaAsio_GetOutputChannelName( PaDeviceIndex device, int channelIndex, const char** channelName );
273+ PaError PaAsio_SetStreamSampleRate( PaStream* stream, double sampleRate );
274+
268275#define paAsioUseChannelSelectors 0x01
269276
270277typedef struct PaAsioStreamInfo
276283 int *channelSelectors;
277284} PaAsioStreamInfo;
278285
286+ /* pa_linux_alsa.h */
287+
288+ void PaAlsa_EnableRealtimeScheduling( PaStream *s, int enable );
289+ PaError PaAlsa_GetStreamInputCard( PaStream *s, int *card );
290+ PaError PaAlsa_GetStreamOutputCard( PaStream *s, int *card );
291+ PaError PaAlsa_SetNumPeriods( int numPeriods );
292+ PaError PaAlsa_SetRetriesBusy( int retries );
293+
279294/* pa_win_wasapi.h */
280295
281296typedef enum PaWasapiFlags
337352 PaWasapiStreamCategory streamCategory;
338353 PaWasapiStreamOption streamOption;
339354} PaWasapiStreamInfo;
355+
356+ PaError PaWasapi_GetFramesPerHostBuffer( PaStream *pStream, unsigned int *nInput, unsigned int *nOutput );
340357""" )
341358
342359try :
@@ -814,6 +831,10 @@ def query_hostapis(index=None):
814831 overwritten by assigning to `default.device` -- take(s)
815832 precedence over `default.hostapi` and the information in
816833 the abovementioned dictionaries.
834+ ``'api'``
835+ A namedtuple containing the platform-specific API from
836+ PortAudio. If a platform-specific API is unavailable, this
837+ is None.
817838
818839 See Also
819840 --------
@@ -827,12 +848,17 @@ def query_hostapis(index=None):
827848 if not info :
828849 raise PortAudioError ('Error querying host API {0}' .format (index ))
829850 assert info .structVersion == 1
851+ try :
852+ api = _get_host_api (info .type )
853+ except KeyError :
854+ api = None
830855 return {
831856 'name' : _ffi .string (info .name ).decode (),
832857 'devices' : [_lib .Pa_HostApiDeviceIndexToDeviceIndex (index , i )
833858 for i in range (info .deviceCount )],
834859 'default_input_device' : info .defaultInputDevice ,
835860 'default_output_device' : info .defaultOutputDevice ,
861+ 'api' : api ,
836862 }
837863
838864
@@ -2324,6 +2350,40 @@ class CallbackAbort(Exception):
23242350 """
23252351
23262352
2353+ # Host-API:
2354+
2355+
2356+ _api_dicts = {}
2357+ def _get_host_api (hostapi_typeid ):
2358+ """Lookup hostapi_typeid and return the results as a namedtuple.
2359+
2360+ Parameters
2361+ ----------
2362+ hostapi_typeid : int
2363+ *hostapi_typeid* is a value from enum PaHostApiTypeId, such as
2364+ _lib.paASIO
2365+
2366+ Example
2367+ -------
2368+ api = _get_host_api(_lib.paASIO)
2369+ extra_settings = api.Settings(channel_selectors=[12, 13])
2370+ available_buffer_sizes = api.get_available_buffer_sizes(device)
2371+
2372+ Implementation Notes
2373+ --------------------
2374+ The fields in the returned namedtuple are formed from a dict, and thus,
2375+ index and iteration order is not guaranteed.
2376+
2377+ """
2378+ api_dict = _api_dicts [hostapi_typeid ]
2379+ API = _namedtuple ('_API_' + str (hostapi_typeid ), api_dict .keys ())
2380+ api = API (** api_dict )
2381+ return api
2382+
2383+
2384+ # Host-API: ASIO
2385+
2386+
23272387class AsioSettings (object ):
23282388
23292389 def __init__ (self , channel_selectors ):
@@ -2376,6 +2436,109 @@ def __init__(self, channel_selectors):
23762436 channelSelectors = self ._selectors ))
23772437
23782438
2439+ _api_asio_buf_sz = _namedtuple ('_api_asio_buf_sz' , ('min' , 'max' , 'preferred' ,
2440+ 'granularity' ))
2441+ def _api_asio_get_available_buffer_sizes (device ):
2442+ """Retrieve legal native buffer sizes for the specificed device, in
2443+ sample frames.
2444+
2445+ Parameters
2446+ ----------
2447+ device : int
2448+ Device ID. (aka The global index of the PortAudio device.)
2449+
2450+ Returns
2451+ -------
2452+ namedtuple containing:
2453+ min : int
2454+ the minimum buffer size value.
2455+ max : int
2456+ the maximum buffer size value.
2457+ preferred : int
2458+ the preferred buffer size value.
2459+ granularity : int
2460+ the step size used to compute the legal values between
2461+ minBufferSizeFrames and maxBufferSizeFrames. If granularity is
2462+ -1 then available buffer size values are powers of two.
2463+
2464+ @see ASIOGetBufferSize in the ASIO SDK.
2465+
2466+ """
2467+ min = _ffi .new ('long[1]' )
2468+ max = _ffi .new ('long[1]' )
2469+ pref = _ffi .new ('long[1]' )
2470+ gran = _ffi .new ('long[1]' )
2471+ _check (_lib .PaAsio_GetAvailableBufferSizes (device , min , max , pref , gran ))
2472+ # Let's be friendly and return a namedtuple...
2473+ return _api_asio_buf_sz (min = min [0 ], max = max [0 ], preferred = pref [0 ],
2474+ granularity = gran [0 ])
2475+
2476+
2477+ def _api_asio_get_input_channel_name (device , channel ):
2478+ """Retrieve the name of the specified output channel.
2479+
2480+ Parameters
2481+ ----------
2482+ device : int
2483+ Device ID. (aka The global index of the PortAudio device.)
2484+ channel : int
2485+ Channel number from 0 to max_*_channels-1.
2486+
2487+ Returns
2488+ -------
2489+ The channel's name : str
2490+
2491+ """
2492+ channel_name = _ffi .new ('char*[1]' )
2493+ _check (_lib .PaAsio_GetInputChannelName (device , channel , channel_name ))
2494+ return _ffi .string (channel_name [0 ]).decode ()
2495+
2496+
2497+ def _api_asio_get_output_channel_name (device , channel ):
2498+ """Retrieve the name of the specified output channel.
2499+
2500+ Parameters
2501+ ----------
2502+ device : int
2503+ Device ID. (aka The global index of the PortAudio device.)
2504+ channel : int
2505+ Channel number from 0 to max_*_channels-1.
2506+
2507+ Returns
2508+ -------
2509+ The channel's name : str
2510+
2511+ """
2512+ channel_name = _ffi .new ('char*[1]' )
2513+ _check (_lib .PaAsio_GetOutputChannelName (device , channel , channel_name ))
2514+ return _ffi .string (channel_name [0 ]).decode ()
2515+
2516+
2517+ def _api_asio_set_stream_sample_rate (stream , sample_rate ):
2518+ """Set stream sample rate.
2519+
2520+ Parameters
2521+ ----------
2522+ stream : an open stream
2523+ Device ID. (aka The global index of the PortAudio device.)
2524+ sample_rate : float
2525+
2526+ """
2527+ _check (_lib .PaAsio_SetStreamSampleRate (stream ._ptr , sample_rate ))
2528+
2529+
2530+ _api_dicts [_lib .paASIO ] = dict (
2531+ Settings = AsioSettings ,
2532+ get_available_buffer_sizes = _api_asio_get_available_buffer_sizes ,
2533+ get_input_channel_name = _api_asio_get_input_channel_name ,
2534+ get_output_channel_name = _api_asio_get_output_channel_name ,
2535+ set_stream_sample_rate = _api_asio_set_stream_sample_rate ,
2536+ )
2537+
2538+
2539+ # Host-API: Core Audio
2540+
2541+
23792542class CoreAudioSettings (object ):
23802543
23812544 def __init__ (self , channel_map = None , change_device_parameters = False ,
@@ -2468,6 +2631,133 @@ def __init__(self, channel_map=None, change_device_parameters=False,
24682631 len (self ._channel_map ))
24692632
24702633
2634+ def _api_coreaudio_get_input_channel_name (device , channel ):
2635+ """Retrieve the name of the specified input channel.
2636+
2637+ Parameters
2638+ ----------
2639+ device : int
2640+ Device ID. (aka The global index of the PortAudio device.)
2641+ channel : int
2642+ Channel number from 0 to max_*_channels-1.
2643+
2644+ """
2645+ return _ffi .string (_lib .PaMacCore_GetChannelName (device , channel , True )
2646+ ).decode ()
2647+
2648+
2649+ def _api_coreaudio_get_output_channel_name (device , channel ):
2650+ """Retrieve the name of the specified output channel.
2651+
2652+ Parameters
2653+ ----------
2654+ device : int
2655+ Device ID. (aka The global index of the PortAudio device.)
2656+ channel : int
2657+ Channel number from 0 to max_*_channels-1.
2658+
2659+ """
2660+ return _ffi .string (_lib .PaMacCore_GetChannelName (device , channel , False )
2661+ ).decode ()
2662+
2663+
2664+ _api_coreaudio_buf_sz = _namedtuple ('_api_coreaudio_buf_sz' , ('min' , 'max' ))
2665+ def _api_coreaudio_get_buffer_size_range (device ):
2666+ """Retrieve the range of legal native buffer sizes for the
2667+ specificed device, in sample frames.
2668+
2669+ Parameters
2670+ ----------
2671+ device : int
2672+ Device ID. (aka The global index of the PortAudio device.)
2673+
2674+ Returns
2675+ -------
2676+ namedtuple containing:
2677+ min : int
2678+ the minimum buffer size value.
2679+ max : int
2680+ the maximum buffer size value.
2681+
2682+ See Also
2683+ --------
2684+ kAudioDevicePropertyBufferFrameSizeRange in the CoreAudio SDK.
2685+
2686+ """
2687+ min = _ffi .new ('long[1]' )
2688+ max = _ffi .new ('long[1]' )
2689+ _check (_lib .PaMacCore_GetBufferSizeRange (device , min , max ))
2690+ return _api_coreaudio_buf_sz (min = min [0 ], max = max [0 ])
2691+
2692+
2693+ _api_dicts [_lib .paCoreAudio ] = dict (
2694+ Settings = CoreAudioSettings ,
2695+ get_input_channel_name = _api_coreaudio_get_input_channel_name ,
2696+ get_output_channel_name = _api_coreaudio_get_output_channel_name ,
2697+ get_buffer_size_range = _api_coreaudio_get_buffer_size_range ,
2698+ )
2699+
2700+
2701+ # Host-API: ALSA
2702+
2703+
2704+ def _api_alsa_enable_realtime_scheduling (stream , enable ):
2705+ """ Instruct whether to enable real-time priority when starting the
2706+ audio thread.
2707+
2708+ If this is turned on by the stream is started, the audio callback
2709+ thread will be created with the FIFO scheduling policy, which is
2710+ suitable for realtime operation.
2711+
2712+ """
2713+ _lib .PaAlsa_EnableRealtimeScheduling (stream ._ptr , enable )
2714+
2715+
2716+ def _api_alsa_get_stream_input_card (stream ):
2717+ """Get the ALSA-lib card index of this stream's input device."""
2718+ card = _ffi .new ('int[1]' )
2719+ _check (_lib .PaAlsa_GetStreamInputCard (stream ._ptr , card ))
2720+ return card [0 ]
2721+
2722+
2723+ def _api_alsa_get_stream_output_card (stream ):
2724+ """Get the ALSA-lib card index of this stream's output device."""
2725+ card = _ffi .new ('int[1]' )
2726+ _check (_lib .PaAlsa_GetStreamOutputCard (stream ._ptr , card ))
2727+ return card [0 ]
2728+
2729+
2730+ def _api_alsa_set_num_periods (num_periods ):
2731+ """Set the number of periods (buffer fragments) to configure devices
2732+ with.
2733+
2734+ By default the number of periods is 4, this is the lowest number of
2735+ periods that works well on the author's soundcard.
2736+
2737+ """
2738+ _check (_lib .PaAlsa_SetNumPeriods (num_periods ))
2739+
2740+
2741+ def _api_alsa_set_retries_busy (retries ):
2742+ """Set the maximum number of times to retry opening busy device
2743+ (sleeping for a short interval inbetween).
2744+
2745+ """
2746+ _check (_lib .PaAlsa_SetRetriesBusy (retries ))
2747+
2748+
2749+ _api_dicts [_lib .paALSA ] = dict (
2750+ enable_realtime_scheduling = _api_alsa_enable_realtime_scheduling ,
2751+ get_stream_input_card = _api_alsa_get_stream_input_card ,
2752+ get_stream_output_card = _api_alsa_get_stream_output_card ,
2753+ set_num_periods = _api_alsa_set_num_periods ,
2754+ set_retries_busy = _api_alsa_set_retries_busy ,
2755+ )
2756+
2757+
2758+ # Host-API: WASAPI
2759+
2760+
24712761class WasapiSettings (object ):
24722762
24732763 def __init__ (self , exclusive = False ):
@@ -2509,6 +2799,30 @@ def __init__(self, exclusive=False):
25092799 ))
25102800
25112801
2802+ _api_wasapi_buf_sz = _namedtuple ('_api_wasapi_buf_sz' , ('max_in' , 'max_out' ))
2803+ def _api_wasapi_get_frames_per_host_buffer (stream ):
2804+ """Get number of frames per host buffer.
2805+
2806+ Returns
2807+ -------
2808+ This returns the maximal value of frames of WASAPI buffer which can
2809+ be locked for operations. Use this method as helper to findout
2810+ maximal values of inputFrames / outputFrames of
2811+ PaWasapiHostProcessorCallback.
2812+
2813+ """
2814+ max_in = _ffi .new ('unsigned int[1]' )
2815+ max_out = _ffi .new ('unsigned int[1]' )
2816+ _check (_lib .PaWasapi_GetFramesPerHostBuffer (stream ._ptr , max_in , max_out ))
2817+ return _api_wasapi_buf_sz (max_in = max_in [0 ], max_out = max_out [0 ])
2818+
2819+
2820+ _api_dicts [_lib .paWASAPI ] = dict (
2821+ Settings = WasapiSettings ,
2822+ get_frames_per_host_buffer = _api_wasapi_get_frames_per_host_buffer ,
2823+ )
2824+
2825+
25122826class _CallbackContext (object ):
25132827 """Helper class for re-use in play()/rec()/playrec() callbacks."""
25142828
0 commit comments