27
27
28
28
import atexit as _atexit
29
29
from cffi import FFI as _FFI
30
+ from collections import namedtuple as _namedtuple
30
31
import os as _os
31
32
import platform as _platform
32
33
import sys as _sys
247
248
void PaMacCore_SetupStreamInfo( PaMacCoreStreamInfo *data, unsigned long flags );
248
249
void PaMacCore_SetupChannelMap( PaMacCoreStreamInfo *data, const SInt32 * const channelMap, unsigned long channelMapSize );
249
250
const char *PaMacCore_GetChannelName( int device, int channelIndex, bool input );
251
+ PaError PaMacCore_GetBufferSizeRange( PaDeviceIndex device, long *minBufferSizeFrames, long *maxBufferSizeFrames );
250
252
#define paMacCoreChangeDeviceParameters 0x01
251
253
#define paMacCoreFailIfConversionRequired 0x02
252
254
#define paMacCoreConversionQualityMin 0x0100
265
267
266
268
/* pa_asio.h */
267
269
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
+
268
275
#define paAsioUseChannelSelectors 0x01
269
276
270
277
typedef struct PaAsioStreamInfo
276
283
int *channelSelectors;
277
284
} PaAsioStreamInfo;
278
285
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
+
279
294
/* pa_win_wasapi.h */
280
295
281
296
typedef enum PaWasapiFlags
337
352
PaWasapiStreamCategory streamCategory;
338
353
PaWasapiStreamOption streamOption;
339
354
} PaWasapiStreamInfo;
355
+
356
+ PaError PaWasapi_GetFramesPerHostBuffer( PaStream *pStream, unsigned int *nInput, unsigned int *nOutput );
340
357
""" )
341
358
342
359
try :
@@ -814,6 +831,10 @@ def query_hostapis(index=None):
814
831
overwritten by assigning to `default.device` -- take(s)
815
832
precedence over `default.hostapi` and the information in
816
833
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.
817
838
818
839
See Also
819
840
--------
@@ -827,12 +848,17 @@ def query_hostapis(index=None):
827
848
if not info :
828
849
raise PortAudioError ('Error querying host API {0}' .format (index ))
829
850
assert info .structVersion == 1
851
+ try :
852
+ api = _get_host_api (info .type )
853
+ except KeyError :
854
+ api = None
830
855
return {
831
856
'name' : _ffi .string (info .name ).decode (),
832
857
'devices' : [_lib .Pa_HostApiDeviceIndexToDeviceIndex (index , i )
833
858
for i in range (info .deviceCount )],
834
859
'default_input_device' : info .defaultInputDevice ,
835
860
'default_output_device' : info .defaultOutputDevice ,
861
+ 'api' : api ,
836
862
}
837
863
838
864
@@ -2324,6 +2350,40 @@ class CallbackAbort(Exception):
2324
2350
"""
2325
2351
2326
2352
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
+
2327
2387
class AsioSettings (object ):
2328
2388
2329
2389
def __init__ (self , channel_selectors ):
@@ -2376,6 +2436,109 @@ def __init__(self, channel_selectors):
2376
2436
channelSelectors = self ._selectors ))
2377
2437
2378
2438
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
+
2379
2542
class CoreAudioSettings (object ):
2380
2543
2381
2544
def __init__ (self , channel_map = None , change_device_parameters = False ,
@@ -2468,6 +2631,133 @@ def __init__(self, channel_map=None, change_device_parameters=False,
2468
2631
len (self ._channel_map ))
2469
2632
2470
2633
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
+
2471
2761
class WasapiSettings (object ):
2472
2762
2473
2763
def __init__ (self , exclusive = False ):
@@ -2509,6 +2799,30 @@ def __init__(self, exclusive=False):
2509
2799
))
2510
2800
2511
2801
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
+
2512
2826
class _CallbackContext (object ):
2513
2827
"""Helper class for re-use in play()/rec()/playrec() callbacks."""
2514
2828
0 commit comments