224224/* not implemented: Pa_GetStreamHostApiType */
225225PaError Pa_GetSampleSize( PaSampleFormat format );
226226void 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;
227240""" )
228241
229242try :
@@ -754,7 +767,7 @@ class _StreamBase(object):
754767 """Base class for Raw{Input,Output}Stream."""
755768
756769 def __init__ (self , kind , samplerate , blocksize , device , channels , dtype ,
757- latency , callback_wrapper , finished_callback ,
770+ latency , extra_settings , callback_wrapper , finished_callback ,
758771 clip_off , dither_off , never_drop_input ,
759772 prime_output_buffers_using_stream_callback ):
760773 if blocksize is None :
@@ -784,10 +797,13 @@ def __init__(self, kind, samplerate, blocksize, device, channels, dtype,
784797 ichannels , ochannels = _split (channels )
785798 idtype , odtype = _split (dtype )
786799 ilatency , olatency = _split (latency )
800+ iextra , oextra = _split (extra_settings )
787801 iparameters , idtype , isize , isamplerate = _get_stream_parameters (
788- 'input' , idevice , ichannels , idtype , ilatency , samplerate )
802+ 'input' , idevice , ichannels , idtype , ilatency , iextra ,
803+ samplerate )
789804 oparameters , odtype , osize , osamplerate = _get_stream_parameters (
790- 'output' , odevice , ochannels , odtype , olatency , samplerate )
805+ 'output' , odevice , ochannels , odtype , olatency , oextra ,
806+ samplerate )
791807 self ._dtype = idtype , odtype
792808 self ._device = iparameters .device , oparameters .device
793809 self ._channels = iparameters .channelCount , oparameters .channelCount
@@ -799,8 +815,8 @@ def __init__(self, kind, samplerate, blocksize, device, channels, dtype,
799815 samplerate = isamplerate
800816 else :
801817 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 )
804820 self ._device = parameters .device
805821 self ._channels = parameters .channelCount
806822
@@ -1072,7 +1088,7 @@ class RawInputStream(_StreamBase):
10721088
10731089 def __init__ (self , samplerate = None , blocksize = None ,
10741090 device = None , channels = None , dtype = None , latency = None ,
1075- callback = None , finished_callback = None ,
1091+ extra_settings = None , callback = None , finished_callback = None ,
10761092 clip_off = None , dither_off = None , never_drop_input = None ,
10771093 prime_output_buffers_using_stream_callback = None ):
10781094 """Open a "raw" input stream.
@@ -1109,8 +1125,8 @@ def callback_wrapper(iptr, optr, frames, time, status, _):
11091125
11101126 _StreamBase .__init__ (
11111127 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 ,
11141130 prime_output_buffers_using_stream_callback )
11151131
11161132 @property
@@ -1164,7 +1180,7 @@ class RawOutputStream(_StreamBase):
11641180
11651181 def __init__ (self , samplerate = None , blocksize = None ,
11661182 device = None , channels = None , dtype = None , latency = None ,
1167- callback = None , finished_callback = None ,
1183+ extra_settings = None , callback = None , finished_callback = None ,
11681184 clip_off = None , dither_off = None , never_drop_input = None ,
11691185 prime_output_buffers_using_stream_callback = None ):
11701186 """Open a "raw" output stream.
@@ -1201,8 +1217,8 @@ def callback_wrapper(iptr, optr, frames, time, status, _):
12011217
12021218 _StreamBase .__init__ (
12031219 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 ,
12061222 prime_output_buffers_using_stream_callback )
12071223
12081224 @property
@@ -1267,7 +1283,7 @@ class RawStream(RawInputStream, RawOutputStream):
12671283
12681284 def __init__ (self , samplerate = None , blocksize = None ,
12691285 device = None , channels = None , dtype = None , latency = None ,
1270- callback = None , finished_callback = None ,
1286+ extra_settings = None , callback = None , finished_callback = None ,
12711287 clip_off = None , dither_off = None , never_drop_input = None ,
12721288 prime_output_buffers_using_stream_callback = None ):
12731289 """Open a "raw" input/output stream.
@@ -1320,8 +1336,8 @@ def callback_wrapper(iptr, optr, frames, time, status, _):
13201336
13211337 _StreamBase .__init__ (
13221338 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 ,
13251341 prime_output_buffers_using_stream_callback )
13261342
13271343
@@ -1330,7 +1346,7 @@ class InputStream(RawInputStream):
13301346
13311347 def __init__ (self , samplerate = None , blocksize = None ,
13321348 device = None , channels = None , dtype = None , latency = None ,
1333- callback = None , finished_callback = None ,
1349+ extra_settings = None , callback = None , finished_callback = None ,
13341350 clip_off = None , dither_off = None , never_drop_input = None ,
13351351 prime_output_buffers_using_stream_callback = None ):
13361352 """Open an input stream.
@@ -1366,8 +1382,8 @@ def callback_wrapper(iptr, optr, frames, time, status, _):
13661382
13671383 _StreamBase .__init__ (
13681384 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 ,
13711387 prime_output_buffers_using_stream_callback )
13721388
13731389 def read (self , frames ):
@@ -1412,7 +1428,7 @@ class OutputStream(RawOutputStream):
14121428
14131429 def __init__ (self , samplerate = None , blocksize = None ,
14141430 device = None , channels = None , dtype = None , latency = None ,
1415- callback = None , finished_callback = None ,
1431+ extra_settings = None , callback = None , finished_callback = None ,
14161432 clip_off = None , dither_off = None , never_drop_input = None ,
14171433 prime_output_buffers_using_stream_callback = None ):
14181434 """Open an output stream.
@@ -1448,8 +1464,8 @@ def callback_wrapper(iptr, optr, frames, time, status, _):
14481464
14491465 _StreamBase .__init__ (
14501466 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 ,
14531469 prime_output_buffers_using_stream_callback )
14541470
14551471 def write (self , data ):
@@ -1503,7 +1519,7 @@ class Stream(InputStream, OutputStream):
15031519
15041520 def __init__ (self , samplerate = None , blocksize = None ,
15051521 device = None , channels = None , dtype = None , latency = None ,
1506- callback = None , finished_callback = None ,
1522+ extra_settings = None , callback = None , finished_callback = None ,
15071523 clip_off = None , dither_off = None , never_drop_input = None ,
15081524 prime_output_buffers_using_stream_callback = None ):
15091525 """Open a stream for input and output.
@@ -1602,6 +1618,9 @@ def __init__(self, samplerate=None, blocksize=None,
16021618 next practical value -- i.e. to provide an equal or higher
16031619 latency wherever possible. Actual latency values for an
16041620 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`.
16051624 callback : callable, optional
16061625 User-supplied function to consume, process or generate audio
16071626 data in response to requests from an `active` stream.
@@ -1747,8 +1766,8 @@ def callback_wrapper(iptr, optr, frames, time, status, _):
17471766
17481767 _StreamBase .__init__ (
17491768 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 ,
17521771 prime_output_buffers_using_stream_callback )
17531772
17541773
@@ -1929,12 +1948,12 @@ def __repr__(self):
19291948class default (object ):
19301949 """Get/set defaults for the *sounddevice* module.
19311950
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.
19381957
19391958 Examples
19401959 --------
@@ -2025,6 +2044,15 @@ class default(object):
20252044
20262045 """
20272046
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+
20282056 samplerate = None
20292057 """Sampling frequency in Hertz (= frames per second).
20302058
@@ -2076,6 +2104,8 @@ def __init__(self):
20762104 vars (self )['channels' ] = _InputOutputPair (self , '_default_channels' )
20772105 vars (self )['dtype' ] = _InputOutputPair (self , '_default_dtype' )
20782106 vars (self )['latency' ] = _InputOutputPair (self , '_default_latency' )
2107+ vars (self )['extra_settings' ] = _InputOutputPair (self ,
2108+ '_default_extra_settings' )
20792109
20802110 def __setattr__ (self , name , value ):
20812111 """Only allow setting existing attributes."""
@@ -2137,6 +2167,58 @@ class CallbackAbort(Exception):
21372167 """
21382168
21392169
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+
21402222class _CallbackContext (object ):
21412223 """Helper class for re-use in play()/rec()/playrec() callbacks."""
21422224
@@ -2317,7 +2399,8 @@ def _check_dtype(dtype):
23172399 return dtype
23182400
23192401
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 ):
23212404 """Get parameters for one direction (input or output) of a stream."""
23222405 if device is None :
23232406 device = default .device [kind ]
@@ -2327,6 +2410,8 @@ def _get_stream_parameters(kind, device, channels, dtype, latency, samplerate):
23272410 dtype = default .dtype [kind ]
23282411 if latency is None :
23292412 latency = default .latency [kind ]
2413+ if extra_settings is None :
2414+ extra_settings = default .extra_settings [kind ]
23302415 if samplerate is None :
23312416 samplerate = default .samplerate
23322417
@@ -2348,9 +2433,9 @@ def _get_stream_parameters(kind, device, channels, dtype, latency, samplerate):
23482433 latency = info ['default_' + latency + '_' + kind + '_latency' ]
23492434 if samplerate is None :
23502435 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 ))
23542439 return parameters , dtype , samplesize , samplerate
23552440
23562441
0 commit comments