77import sys
88
99import numpy as np
10- import sounddevice
1110from rtmixer import Mixer , RingBuffer
1211
1312from .._utils import get_config , logger
13+ from ._sounddevice import _find_device
1414
1515_PRIORITY = 100
16- _DEFAULT_NAME = None
1716
1817
1918# only initialize each mixer once and reuse it until this gets garbage
2322class _MixerRegistry (dict ):
2423 def __del__ (self ):
2524 for mixer in self .values ():
26- print (f"Closing { mixer } " )
25+ logger . debug (f"Closing { mixer } " )
2726 mixer .abort ()
2827 mixer .close ()
2928 self .clear ()
@@ -52,56 +51,12 @@ def _get_mixer(self, fs, n_channels, api, name, api_options):
5251
5352
5453def _init_mixer (fs , n_channels , api , name , api_options = None ):
55- devices = sounddevice .query_devices ()
56- if len (devices ) == 0 :
57- raise OSError ("No sound devices found!" )
58- apis = sounddevice .query_hostapis ()
59- valid_apis = []
60- for ai , this_api in enumerate (apis ):
61- if this_api ["name" ] == api :
62- api = this_api
63- break
64- else :
65- valid_apis .append (this_api ["name" ])
66- else :
67- m = 'Could not find host API %s. Valid choices are "%s"'
68- raise RuntimeError (m % (api , ", " .join (valid_apis )))
69- del this_api
70-
71- # Name
72- if name is None :
73- name = get_config ("SOUND_CARD_NAME" , None )
74- if name is None :
75- global _DEFAULT_NAME
76- if _DEFAULT_NAME is None :
77- di = api ["default_output_device" ]
78- _DEFAULT_NAME = devices [di ]["name" ]
79- logger .exp ("Selected default sound device: %r" % (_DEFAULT_NAME ,))
80- name = _DEFAULT_NAME
81- possible = list ()
82- for di , device in enumerate (devices ):
83- if device ["hostapi" ] == ai :
84- possible .append (device ["name" ])
85- if name in device ["name" ]:
86- break
87- else :
88- raise RuntimeError (
89- "Could not find device on API %r with name "
90- "containing %r, found:\n %s" % (api ["name" ], name , "\n " .join (possible ))
91- )
92- param_str = "sound card %r (devices[%d]) via %r" % (device ["name" ], di , api ["name" ])
93- extra_settings = None
94- if api_options is not None :
95- if api ["name" ] == "Windows WASAPI" :
96- # exclusive mode is needed for zero jitter on Windows in testing
97- extra_settings = sounddevice .WasapiSettings (** api_options )
98- else :
99- raise ValueError (
100- 'api_options only supported for "Windows WASAPI" backend, '
101- "using %s backend got api_options=%s" % (api ["name" ], api_options )
102- )
103- param_str += " with options %s" % (api_options ,)
104- param_str += ", %d channels" % (n_channels ,)
54+ device , param_str , extra_settings , all_devices = _find_device (
55+ n_channels ,
56+ api ,
57+ name ,
58+ api_options = api_options ,
59+ )
10560 if fs is not None :
10661 param_str += " @ %d Hz" % (fs ,)
10762 try :
@@ -110,11 +65,11 @@ def _init_mixer(fs, n_channels, api, name, api_options=None):
11065 latency = "low" ,
11166 channels = n_channels ,
11267 dither_off = True ,
113- device = di ,
68+ device = device [ "index" ] ,
11469 extra_settings = extra_settings ,
11570 )
116- except Exception as exp :
117- raise RuntimeError (f"Could not set up { param_str } :\n { exp } " ) from None
71+ except Exception :
72+ raise RuntimeError (f"Could not set up { param_str } :\n \n { all_devices } " )
11873 assert mixer .channels == n_channels
11974 if fs is None :
12075 param_str += " @ %d Hz" % (mixer .samplerate ,)
0 commit comments