224
224
/* not implemented: Pa_GetStreamHostApiType */
225
225
PaError Pa_GetSampleSize( PaSampleFormat format );
226
226
void Pa_Sleep( long msec );
227
+
228
+ /* pa_asio.h */
229
+
230
+ #define paAsioUseChannelSelectors 0x01
231
+
232
+ typedef struct PaAsioStreamInfo
233
+ {
234
+ unsigned long size;
235
+ PaHostApiTypeId hostApiType;
236
+ unsigned long version;
237
+ unsigned long flags;
238
+ int *channelSelectors;
239
+ } PaAsioStreamInfo;
227
240
""" )
228
241
229
242
try :
@@ -754,7 +767,7 @@ class _StreamBase(object):
754
767
"""Base class for Raw{Input,Output}Stream."""
755
768
756
769
def __init__ (self , kind , samplerate , blocksize , device , channels , dtype ,
757
- latency , callback_wrapper , finished_callback ,
770
+ latency , extra_settings , callback_wrapper , finished_callback ,
758
771
clip_off , dither_off , never_drop_input ,
759
772
prime_output_buffers_using_stream_callback ):
760
773
if blocksize is None :
@@ -784,10 +797,13 @@ def __init__(self, kind, samplerate, blocksize, device, channels, dtype,
784
797
ichannels , ochannels = _split (channels )
785
798
idtype , odtype = _split (dtype )
786
799
ilatency , olatency = _split (latency )
800
+ iextra , oextra = _split (extra_settings )
787
801
iparameters , idtype , isize , isamplerate = _get_stream_parameters (
788
- 'input' , idevice , ichannels , idtype , ilatency , samplerate )
802
+ 'input' , idevice , ichannels , idtype , ilatency , iextra ,
803
+ samplerate )
789
804
oparameters , odtype , osize , osamplerate = _get_stream_parameters (
790
- 'output' , odevice , ochannels , odtype , olatency , samplerate )
805
+ 'output' , odevice , ochannels , odtype , olatency , oextra ,
806
+ samplerate )
791
807
self ._dtype = idtype , odtype
792
808
self ._device = iparameters .device , oparameters .device
793
809
self ._channels = iparameters .channelCount , oparameters .channelCount
@@ -799,8 +815,8 @@ def __init__(self, kind, samplerate, blocksize, device, channels, dtype,
799
815
samplerate = isamplerate
800
816
else :
801
817
parameters , self ._dtype , self ._samplesize , samplerate = \
802
- _get_stream_parameters (
803
- kind , device , channels , dtype , latency , samplerate )
818
+ _get_stream_parameters (kind , device , channels , dtype , latency ,
819
+ extra_settings , samplerate )
804
820
self ._device = parameters .device
805
821
self ._channels = parameters .channelCount
806
822
@@ -1072,7 +1088,7 @@ class RawInputStream(_StreamBase):
1072
1088
1073
1089
def __init__ (self , samplerate = None , blocksize = None ,
1074
1090
device = None , channels = None , dtype = None , latency = None ,
1075
- callback = None , finished_callback = None ,
1091
+ extra_settings = None , callback = None , finished_callback = None ,
1076
1092
clip_off = None , dither_off = None , never_drop_input = None ,
1077
1093
prime_output_buffers_using_stream_callback = None ):
1078
1094
"""Open a "raw" input stream.
@@ -1109,8 +1125,8 @@ def callback_wrapper(iptr, optr, frames, time, status, _):
1109
1125
1110
1126
_StreamBase .__init__ (
1111
1127
self , 'input' , samplerate , blocksize , device , channels , dtype ,
1112
- latency , callback and callback_wrapper , finished_callback ,
1113
- clip_off , dither_off , never_drop_input ,
1128
+ latency , extra_settings , callback and callback_wrapper ,
1129
+ finished_callback , clip_off , dither_off , never_drop_input ,
1114
1130
prime_output_buffers_using_stream_callback )
1115
1131
1116
1132
@property
@@ -1164,7 +1180,7 @@ class RawOutputStream(_StreamBase):
1164
1180
1165
1181
def __init__ (self , samplerate = None , blocksize = None ,
1166
1182
device = None , channels = None , dtype = None , latency = None ,
1167
- callback = None , finished_callback = None ,
1183
+ extra_settings = None , callback = None , finished_callback = None ,
1168
1184
clip_off = None , dither_off = None , never_drop_input = None ,
1169
1185
prime_output_buffers_using_stream_callback = None ):
1170
1186
"""Open a "raw" output stream.
@@ -1201,8 +1217,8 @@ def callback_wrapper(iptr, optr, frames, time, status, _):
1201
1217
1202
1218
_StreamBase .__init__ (
1203
1219
self , 'output' , samplerate , blocksize , device , channels , dtype ,
1204
- latency , callback and callback_wrapper , finished_callback ,
1205
- clip_off , dither_off , never_drop_input ,
1220
+ latency , extra_settings , callback and callback_wrapper ,
1221
+ finished_callback , clip_off , dither_off , never_drop_input ,
1206
1222
prime_output_buffers_using_stream_callback )
1207
1223
1208
1224
@property
@@ -1267,7 +1283,7 @@ class RawStream(RawInputStream, RawOutputStream):
1267
1283
1268
1284
def __init__ (self , samplerate = None , blocksize = None ,
1269
1285
device = None , channels = None , dtype = None , latency = None ,
1270
- callback = None , finished_callback = None ,
1286
+ extra_settings = None , callback = None , finished_callback = None ,
1271
1287
clip_off = None , dither_off = None , never_drop_input = None ,
1272
1288
prime_output_buffers_using_stream_callback = None ):
1273
1289
"""Open a "raw" input/output stream.
@@ -1320,8 +1336,8 @@ def callback_wrapper(iptr, optr, frames, time, status, _):
1320
1336
1321
1337
_StreamBase .__init__ (
1322
1338
self , 'duplex' , samplerate , blocksize , device , channels , dtype ,
1323
- latency , callback and callback_wrapper , finished_callback ,
1324
- clip_off , dither_off , never_drop_input ,
1339
+ latency , extra_settings , callback and callback_wrapper ,
1340
+ finished_callback , clip_off , dither_off , never_drop_input ,
1325
1341
prime_output_buffers_using_stream_callback )
1326
1342
1327
1343
@@ -1330,7 +1346,7 @@ class InputStream(RawInputStream):
1330
1346
1331
1347
def __init__ (self , samplerate = None , blocksize = None ,
1332
1348
device = None , channels = None , dtype = None , latency = None ,
1333
- callback = None , finished_callback = None ,
1349
+ extra_settings = None , callback = None , finished_callback = None ,
1334
1350
clip_off = None , dither_off = None , never_drop_input = None ,
1335
1351
prime_output_buffers_using_stream_callback = None ):
1336
1352
"""Open an input stream.
@@ -1366,8 +1382,8 @@ def callback_wrapper(iptr, optr, frames, time, status, _):
1366
1382
1367
1383
_StreamBase .__init__ (
1368
1384
self , 'input' , samplerate , blocksize , device , channels , dtype ,
1369
- latency , callback and callback_wrapper , finished_callback ,
1370
- clip_off , dither_off , never_drop_input ,
1385
+ latency , extra_settings , callback and callback_wrapper ,
1386
+ finished_callback , clip_off , dither_off , never_drop_input ,
1371
1387
prime_output_buffers_using_stream_callback )
1372
1388
1373
1389
def read (self , frames ):
@@ -1412,7 +1428,7 @@ class OutputStream(RawOutputStream):
1412
1428
1413
1429
def __init__ (self , samplerate = None , blocksize = None ,
1414
1430
device = None , channels = None , dtype = None , latency = None ,
1415
- callback = None , finished_callback = None ,
1431
+ extra_settings = None , callback = None , finished_callback = None ,
1416
1432
clip_off = None , dither_off = None , never_drop_input = None ,
1417
1433
prime_output_buffers_using_stream_callback = None ):
1418
1434
"""Open an output stream.
@@ -1448,8 +1464,8 @@ def callback_wrapper(iptr, optr, frames, time, status, _):
1448
1464
1449
1465
_StreamBase .__init__ (
1450
1466
self , 'output' , samplerate , blocksize , device , channels , dtype ,
1451
- latency , callback and callback_wrapper , finished_callback ,
1452
- clip_off , dither_off , never_drop_input ,
1467
+ latency , extra_settings , callback and callback_wrapper ,
1468
+ finished_callback , clip_off , dither_off , never_drop_input ,
1453
1469
prime_output_buffers_using_stream_callback )
1454
1470
1455
1471
def write (self , data ):
@@ -1503,7 +1519,7 @@ class Stream(InputStream, OutputStream):
1503
1519
1504
1520
def __init__ (self , samplerate = None , blocksize = None ,
1505
1521
device = None , channels = None , dtype = None , latency = None ,
1506
- callback = None , finished_callback = None ,
1522
+ extra_settings = None , callback = None , finished_callback = None ,
1507
1523
clip_off = None , dither_off = None , never_drop_input = None ,
1508
1524
prime_output_buffers_using_stream_callback = None ):
1509
1525
"""Open a stream for input and output.
@@ -1602,6 +1618,9 @@ def __init__(self, samplerate=None, blocksize=None,
1602
1618
next practical value -- i.e. to provide an equal or higher
1603
1619
latency wherever possible. Actual latency values for an
1604
1620
open stream may be retrieved using the `latency` attribute.
1621
+ extra_settings : settings object or pair thereof, optional
1622
+ This can be used for host-API-specific input/output
1623
+ settings. See `default.extra_settings`.
1605
1624
callback : callable, optional
1606
1625
User-supplied function to consume, process or generate audio
1607
1626
data in response to requests from an `active` stream.
@@ -1747,8 +1766,8 @@ def callback_wrapper(iptr, optr, frames, time, status, _):
1747
1766
1748
1767
_StreamBase .__init__ (
1749
1768
self , 'duplex' , samplerate , blocksize , device , channels , dtype ,
1750
- latency , callback and callback_wrapper , finished_callback ,
1751
- clip_off , dither_off , never_drop_input ,
1769
+ latency , extra_settings , callback and callback_wrapper ,
1770
+ finished_callback , clip_off , dither_off , never_drop_input ,
1752
1771
prime_output_buffers_using_stream_callback )
1753
1772
1754
1773
@@ -1929,12 +1948,12 @@ def __repr__(self):
1929
1948
class default (object ):
1930
1949
"""Get/set defaults for the *sounddevice* module.
1931
1950
1932
- The attributes `device`, `channels`, `dtype` and `latency` accept
1933
- single values which specify the given property for both input and
1934
- output. However, if the property differs between input and output,
1935
- pairs of values can be used, where the first value specifies the
1936
- input and the second value specifies the output.
1937
- All other attributes are always single values.
1951
+ The attributes `device`, `channels`, `dtype`, `latency` and
1952
+ `extra_settings` accept single values which specify the given
1953
+ property for both input and output. However, if the property
1954
+ differs between input and output, pairs of values can be used, where
1955
+ the first value specifies the input and the second value specifies
1956
+ the output. All other attributes are always single values.
1938
1957
1939
1958
Examples
1940
1959
--------
@@ -2025,6 +2044,15 @@ class default(object):
2025
2044
2026
2045
"""
2027
2046
2047
+ extra_settings = _default_extra_settings = None , None
2048
+ """Host-API-specific input/output settings.
2049
+
2050
+ See Also
2051
+ --------
2052
+ AsioSettings
2053
+
2054
+ """
2055
+
2028
2056
samplerate = None
2029
2057
"""Sampling frequency in Hertz (= frames per second).
2030
2058
@@ -2076,6 +2104,8 @@ def __init__(self):
2076
2104
vars (self )['channels' ] = _InputOutputPair (self , '_default_channels' )
2077
2105
vars (self )['dtype' ] = _InputOutputPair (self , '_default_dtype' )
2078
2106
vars (self )['latency' ] = _InputOutputPair (self , '_default_latency' )
2107
+ vars (self )['extra_settings' ] = _InputOutputPair (self ,
2108
+ '_default_extra_settings' )
2079
2109
2080
2110
def __setattr__ (self , name , value ):
2081
2111
"""Only allow setting existing attributes."""
@@ -2137,6 +2167,58 @@ class CallbackAbort(Exception):
2137
2167
"""
2138
2168
2139
2169
2170
+ class AsioSettings (object ):
2171
+
2172
+ def __init__ (self , channel_selectors ):
2173
+ """ASIO-specific input/output settings.
2174
+
2175
+ Objects of this class can be used as *extra_settings* argument
2176
+ to `Stream()` (and variants) or as `default.extra_settings`.
2177
+
2178
+ Parameters
2179
+ ----------
2180
+ channel_selectors : list of int
2181
+ Support for opening only specific channels of an ASIO
2182
+ device. *channel_selectors* is a list of integers
2183
+ specifying the (zero-based) channel numbers to use.
2184
+ The length of *channel_selectors* must match the
2185
+ corresponding *channels* parameter of `Stream()` (or
2186
+ variants), otherwise a crash may result.
2187
+ The values in the selectors array must specify channels
2188
+ within the range of supported channels.
2189
+
2190
+ Examples
2191
+ --------
2192
+ Setting output channels when calling `play()`:
2193
+
2194
+ >>> import sounddevice as sd
2195
+ >>> asio_out = sd.AsioSettings(channel_selectors=[12, 13])
2196
+ >>> sd.play(..., extra_settings=asio_out)
2197
+
2198
+ Setting default output channels:
2199
+
2200
+ >>> sd.default.extra_settings = asio_out
2201
+ >>> sd.play(...)
2202
+
2203
+ Setting input channels as well:
2204
+
2205
+ >>> asio_in = sd.AsioSettings(channel_selectors=[8])
2206
+ >>> sd.default.extra_settings = asio_in, asio_out
2207
+ >>> sd.playrec(..., channels=1, ...)
2208
+
2209
+ """
2210
+ if isinstance (channel_selectors , int ):
2211
+ raise TypeError ('channel_selectors must be a list or tuple' )
2212
+ # int array must be kept alive!
2213
+ self ._selectors = _ffi .new ('int[]' , channel_selectors )
2214
+ self ._streaminfo = _ffi .new ('PaAsioStreamInfo*' , dict (
2215
+ size = _ffi .sizeof ('PaAsioStreamInfo' ),
2216
+ hostApiType = _lib .paASIO ,
2217
+ version = 1 ,
2218
+ flags = _lib .paAsioUseChannelSelectors ,
2219
+ channelSelectors = self ._selectors ))
2220
+
2221
+
2140
2222
class _CallbackContext (object ):
2141
2223
"""Helper class for re-use in play()/rec()/playrec() callbacks."""
2142
2224
@@ -2317,7 +2399,8 @@ def _check_dtype(dtype):
2317
2399
return dtype
2318
2400
2319
2401
2320
- def _get_stream_parameters (kind , device , channels , dtype , latency , samplerate ):
2402
+ def _get_stream_parameters (kind , device , channels , dtype , latency ,
2403
+ extra_settings , samplerate ):
2321
2404
"""Get parameters for one direction (input or output) of a stream."""
2322
2405
if device is None :
2323
2406
device = default .device [kind ]
@@ -2327,6 +2410,8 @@ def _get_stream_parameters(kind, device, channels, dtype, latency, samplerate):
2327
2410
dtype = default .dtype [kind ]
2328
2411
if latency is None :
2329
2412
latency = default .latency [kind ]
2413
+ if extra_settings is None :
2414
+ extra_settings = default .extra_settings [kind ]
2330
2415
if samplerate is None :
2331
2416
samplerate = default .samplerate
2332
2417
@@ -2348,9 +2433,9 @@ def _get_stream_parameters(kind, device, channels, dtype, latency, samplerate):
2348
2433
latency = info ['default_' + latency + '_' + kind + '_latency' ]
2349
2434
if samplerate is None :
2350
2435
samplerate = info ['default_samplerate' ]
2351
- parameters = _ffi .new (
2352
- 'PaStreamParameters*' ,
2353
- ( device , channels , sampleformat , latency , _ffi .NULL ))
2436
+ parameters = _ffi .new ('PaStreamParameters*' , (
2437
+ device , channels , sampleformat , latency ,
2438
+ extra_settings . _streaminfo if extra_settings else _ffi .NULL ))
2354
2439
return parameters , dtype , samplesize , samplerate
2355
2440
2356
2441
0 commit comments