-
-
Notifications
You must be signed in to change notification settings - Fork 15
/
Copy pathdevice_manager.py
327 lines (267 loc) · 13.5 KB
/
device_manager.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
from typing import Callable
from time import sleep
from threading import Thread
import comtypes
from pyaudiowpatch import PyAudio, paWASAPI
from pycaw.callbacks import MMNotificationClient
from pycaw.utils import AudioUtilities
from utils import errorLogging
class Client(MMNotificationClient):
def __init__(self):
super().__init__()
self.loop = True
def on_default_device_changed(self, flow, flow_id, role, role_id, default_device_id):
self.loop = False
def on_device_added(self, added_device_id):
self.loop = False
def on_device_removed(self, removed_device_id):
self.loop = False
def on_device_state_changed(self, device_id, state):
self.loop = False
# def on_property_value_changed(self, device_id, key):
# self.loop = False
class DeviceManager:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(DeviceManager, cls).__new__(cls)
cls._instance.init()
return cls._instance
def init(self):
self.mic_devices = {"NoHost": [{"index": -1, "name": "NoDevice"}]}
self.default_mic_device = {"host": {"index": -1, "name": "NoHost"}, "device": {"index": -1, "name": "NoDevice"}}
self.speaker_devices = [{"index": -1, "name": "NoDevice"}]
self.default_speaker_device = {"device": {"index": -1, "name": "NoDevice"}}
self.update()
self.prev_mic_host = [host for host in self.mic_devices]
self.prev_mic_devices = self.mic_devices
self.prev_default_mic_device = self.default_mic_device
self.prev_speaker_devices = self.speaker_devices
self.prev_default_speaker_device = self.default_speaker_device
self.update_flag_default_mic_device = False
self.update_flag_default_speaker_device = False
self.update_flag_host_list = False
self.update_flag_mic_device_list = False
self.update_flag_speaker_device_list = False
self.callback_default_mic_device = None
self.callback_default_speaker_device = None
self.callback_host_list = None
self.callback_mic_device_list = None
self.callback_speaker_device_list = None
self.callback_process_before_update_devices = None
self.callback_process_after_update_devices = None
self.monitoring_flag = False
self.startMonitoring()
def update(self):
buffer_mic_devices = {}
buffer_default_mic_device = {"host": {"index": -1, "name": "NoHost"}, "device": {"index": -1, "name": "NoDevice"}}
buffer_speaker_devices = []
buffer_default_speaker_device = {"device": {"index": -1, "name": "NoDevice"}}
with PyAudio() as p:
for host_index in range(p.get_host_api_count()):
host = p.get_host_api_info_by_index(host_index)
device_count = host.get('deviceCount', 0)
for device_index in range(device_count):
device = p.get_device_info_by_host_api_device_index(host_index, device_index)
if device.get("maxInputChannels", 0) > 0 and not device.get("isLoopbackDevice", True):
buffer_mic_devices.setdefault(host["name"], []).append(device)
if not buffer_mic_devices:
buffer_mic_devices = {"NoHost": [{"index": -1, "name": "NoDevice"}]}
api_info = p.get_default_host_api_info()
default_mic_device = api_info["defaultInputDevice"]
for host_index in range(p.get_host_api_count()):
host = p.get_host_api_info_by_index(host_index)
device_count = host.get('deviceCount', 0)
for device_index in range(device_count):
device = p.get_device_info_by_host_api_device_index(host_index, device_index)
if device["index"] == default_mic_device:
buffer_default_mic_device = {"host": host, "device": device}
break
else:
continue
break
speaker_devices = []
wasapi_info = p.get_host_api_info_by_type(paWASAPI)
wasapi_name = wasapi_info["name"]
for host_index in range(p.get_host_api_count()):
host = p.get_host_api_info_by_index(host_index)
if host["name"] == wasapi_name:
device_count = host.get('deviceCount', 0)
for device_index in range(device_count):
device = p.get_device_info_by_host_api_device_index(host_index, device_index)
if not device.get("isLoopbackDevice", True):
for loopback in p.get_loopback_device_info_generator():
if device["name"] in loopback["name"]:
speaker_devices.append(loopback)
speaker_devices = [dict(t) for t in {tuple(d.items()) for d in speaker_devices}] or [{"index": -1, "name": "NoDevice"}]
buffer_speaker_devices = sorted(speaker_devices, key=lambda d: d['index'])
wasapi_info = p.get_host_api_info_by_type(paWASAPI)
default_speaker_device_index = wasapi_info["defaultOutputDevice"]
for host_index in range(p.get_host_api_count()):
host_info = p.get_host_api_info_by_index(host_index)
device_count = host_info.get('deviceCount', 0)
for device_index in range(0, device_count):
device = p.get_device_info_by_host_api_device_index(host_index, device_index)
if device["index"] == default_speaker_device_index:
default_speakers = device
if not default_speakers.get("isLoopbackDevice", True):
for loopback in p.get_loopback_device_info_generator():
if default_speakers["name"] in loopback["name"]:
buffer_default_speaker_device = {"device": loopback}
break
break
if buffer_default_speaker_device["device"]["name"] != "NoDevice":
break
self.mic_devices = buffer_mic_devices
self.default_mic_device = buffer_default_mic_device
self.speaker_devices = buffer_speaker_devices
self.default_speaker_device = buffer_default_speaker_device
def checkUpdate(self):
if self.prev_default_mic_device["device"]["name"] != self.default_mic_device["device"]["name"]:
self.update_flag_default_mic_device = True
self.prev_default_mic_device = self.default_mic_device
if self.prev_default_speaker_device["device"]["name"] != self.default_speaker_device["device"]["name"]:
self.update_flag_default_speaker_device = True
self.prev_default_speaker_device = self.default_speaker_device
if self.prev_mic_host != [host for host in self.mic_devices]:
self.update_flag_host_list = True
self.prev_mic_host = [host for host in self.mic_devices]
if {key: [device['name'] for device in devices] for key, devices in self.prev_mic_devices.items()} != {key: [device['name'] for device in devices] for key, devices in self.mic_devices.items()}:
self.update_flag_mic_device_list = True
self.prev_mic_devices = self.mic_devices
if [device['name'] for device in self.prev_speaker_devices] != [device['name'] for device in self.speaker_devices]:
self.update_flag_speaker_device_list = True
self.prev_speaker_devices = self.speaker_devices
update_flag = (
self.update_flag_default_mic_device or
self.update_flag_default_speaker_device or
self.update_flag_host_list or
self.update_flag_mic_device_list or
self.update_flag_speaker_device_list
)
return update_flag
def monitoring(self):
try:
while self.monitoring_flag is True:
try:
comtypes.CoInitialize()
cb = Client()
enumerator = AudioUtilities.GetDeviceEnumerator()
enumerator.RegisterEndpointNotificationCallback(cb)
while cb.loop is True:
sleep(1)
enumerator.UnregisterEndpointNotificationCallback(cb)
comtypes.CoUninitialize()
self.runProcessBeforeUpdateDevices()
sleep(2)
for _ in range(10):
self.update()
if self.checkUpdate():
break
sleep(2)
self.noticeUpdateDevices()
self.runProcessAfterUpdateDevices()
except Exception:
errorLogging()
finally:
pass
except Exception:
errorLogging()
def startMonitoring(self):
self.monitoring_flag = True
self.th_monitoring = Thread(target=self.monitoring)
self.th_monitoring.daemon = True
self.th_monitoring.start()
def stopMonitoring(self):
self.monitoring_flag = False
self.th_monitoring.join()
def setCallbackDefaultMicDevice(self, callback):
self.callback_default_mic_device = callback
def clearCallbackDefaultMicDevice(self):
self.callback_default_mic_device = None
def setCallbackDefaultSpeakerDevice(self, callback):
self.callback_default_speaker_device = callback
def clearCallbackDefaultSpeakerDevice(self):
self.callback_default_speaker_device = None
def setCallbackHostList(self, callback):
self.callback_host_list = callback
def clearCallbackHostList(self):
self.callback_host_list = None
def setCallbackMicDeviceList(self, callback):
self.callback_mic_device_list = callback
def clearCallbackMicDeviceList(self):
self.callback_mic_device_list = None
def setCallbackSpeakerDeviceList(self, callback):
self.callback_speaker_device_list = callback
def clearCallbackSpeakerDeviceList(self):
self.callback_speaker_device_list = None
def setCallbackProcessBeforeUpdateDevices(self, callback):
self.callback_process_before_update_devices = callback
def clearCallbackProcessBeforeUpdateDevices(self):
self.callback_process_before_update_devices = None
def runProcessBeforeUpdateDevices(self):
if isinstance(self.callback_process_before_update_devices, Callable):
self.callback_process_before_update_devices()
def setCallbackProcessAfterUpdateDevices(self, callback):
self.callback_process_after_update_devices = callback
def clearCallbackProcessAfterUpdateDevices(self):
self.callback_process_after_update_devices = None
def runProcessAfterUpdateDevices(self):
if isinstance(self.callback_process_after_update_devices, Callable):
self.callback_process_after_update_devices()
def noticeUpdateDevices(self):
if self.update_flag_default_mic_device is True:
self.setMicDefaultDevice()
if self.update_flag_default_speaker_device is True:
self.setSpeakerDefaultDevice()
if self.update_flag_host_list is True:
self.setMicHostList()
if self.update_flag_mic_device_list is True:
self.setMicDeviceList()
if self.update_flag_speaker_device_list is True:
self.setSpeakerDeviceList()
self.update_flag_default_mic_device = False
self.update_flag_default_speaker_device = False
self.update_flag_host_list = False
self.update_flag_mic_device_list = False
self.update_flag_speaker_device_list = False
def setMicDefaultDevice(self):
if isinstance(self.callback_default_mic_device, Callable):
self.callback_default_mic_device(self.default_mic_device["host"]["name"], self.default_mic_device["device"]["name"])
def setSpeakerDefaultDevice(self):
if isinstance(self.callback_default_speaker_device, Callable):
self.callback_default_speaker_device(self.default_speaker_device["device"]["name"])
def setMicHostList(self):
if isinstance(self.callback_host_list, Callable):
self.callback_host_list()
def setMicDeviceList(self):
if isinstance(self.callback_mic_device_list, Callable):
self.callback_mic_device_list()
def setSpeakerDeviceList(self):
if isinstance(self.callback_speaker_device_list, Callable):
self.callback_speaker_device_list()
def getMicDevices(self):
return self.mic_devices
def getDefaultMicDevice(self):
return self.default_mic_device
def getSpeakerDevices(self):
return self.speaker_devices
def getDefaultSpeakerDevice(self):
return self.default_speaker_device
def forceUpdateAndSetMicDevices(self):
self.update()
self.setMicHostList()
self.setMicDeviceList()
self.setMicDefaultDevice()
def forceUpdateAndSetSpeakerDevices(self):
self.update()
self.setSpeakerDeviceList()
self.setSpeakerDefaultDevice()
device_manager = DeviceManager()
if __name__ == "__main__":
# print("getMicDevices()", device_manager.getMicDevices())
# print("getDefaultMicDevice()", device_manager.getDefaultMicDevice())
# print("getSpeakerDevices()", device_manager.getSpeakerDevices())
# print("getDefaultSpeakerDevice()", device_manager.getDefaultSpeakerDevice())
while True:
sleep(1)