@@ -1561,9 +1561,10 @@ def __init__(self, samplerate=None, blocksize=None,
15611561 non-zero `blocksize` value only be used when your
15621562 algorithm requires a fixed number of frames per stream
15631563 callback.
1564- device : int or pair of int, optional
1565- Device index specifying the device to be used. The default
1566- value(s) can be changed with :attr:`default.device`.
1564+ device : int or str or pair thereof, optional
1565+ Device index(es) or query string(s) specifying the device(s)
1566+ to be used. The default value(s) can be changed with
1567+ :attr:`default.device`.
15671568 channels : int or pair of int, optional
15681569 The number of channels of sound to be delivered to the
15691570 stream callback or accessed by :meth:`read` or
@@ -1934,10 +1935,15 @@ class default(object):
19341935 # provided here for static analysis tools and for the docs.
19351936 # They're overwritten in __init__().
19361937 device = None , None
1937- """Index of default input/output device.
1938+ """Index or query string of default input/output device.
19381939
19391940 If not overwritten, this is queried from PortAudio.
19401941
1942+ If a string is given, the device is selected which contains all
1943+ space-separated parts in the right order. Each device string
1944+ contains the name of the corresponding host API in the end.
1945+ The string comparison is case-insensitive.
1946+
19411947 See Also
19421948 --------
19431949 :func:`query_devices`
@@ -2285,6 +2291,8 @@ def _get_stream_parameters(kind, device, channels, dtype, latency, samplerate):
22852291 if samplerate is None :
22862292 samplerate = default .samplerate
22872293
2294+ if not isinstance (device , int ):
2295+ device = _find_device_id (kind , device )
22882296 info = query_devices (device )
22892297 if channels is None :
22902298 channels = info ['max_' + kind + '_channels' ]
@@ -2362,6 +2370,38 @@ def _check(err, msg=""):
23622370 return err
23632371
23642372
2373+ def _find_device_id (kind , device ):
2374+ """Return device ID given space-separated substrings."""
2375+ device_list = []
2376+ for i , info in enumerate (query_devices ()):
2377+ if info ['max_' + kind + '_channels' ] > 0 :
2378+ hostapi_info = query_hostapis (info ['hostapi' ])
2379+ name = info ['name' ] + ', ' + hostapi_info ['name' ]
2380+ device_list .append ((i , name ))
2381+
2382+ substrings = device .lower ().split ()
2383+ matches = []
2384+ for i , device_string in device_list :
2385+ lowercase_device_string = device_string .lower ()
2386+ pos = 0
2387+ for substring in substrings :
2388+ pos = lowercase_device_string .find (substring , pos )
2389+ if pos < 0 :
2390+ break
2391+ pos += len (substring )
2392+ else :
2393+ matches .append ((i , device_string ))
2394+
2395+ if not matches :
2396+ raise ValueError (
2397+ "No " + kind + " device matching " + repr (device ))
2398+ if len (matches ) > 1 :
2399+ raise ValueError (
2400+ "Multiple " + kind + " devices found for " + repr (device ) + ": " +
2401+ '; ' .join ('[{0}] {1}' .format (id , name ) for id , name in matches ))
2402+ return matches [0 ][0 ]
2403+
2404+
23652405def _initialize ():
23662406 """Initialize PortAudio."""
23672407 _check (_lib .Pa_Initialize (), "Error initializing PortAudio" )
0 commit comments