diff --git a/src-python/model.py b/src-python/model.py index b0363f25..7c5ebb16 100644 --- a/src-python/model.py +++ b/src-python/model.py @@ -69,6 +69,7 @@ class Model: def init(self): self.logger = None + self.th_check_device = None self.mic_print_transcript = None self.mic_audio_recorder = None self.mic_energy_recorder = None @@ -422,6 +423,33 @@ class Model: def getListOutputDevice(): return [device["name"] for device in getOutputDevices()] + def startAutomaticDeviceSelection(self, fnc_mic, fnc_speaker): + def checkDevice(fnc_mic, fnc_speaker): + if config.ENABLE_MIC_AUTOMATIC_SELECTION is True: + default_device = getDefaultInputDevice() + mic_host_name = default_device["host"]["name"] + mic_device_name = default_device["device"]["name"] + if mic_host_name != config.CHOICE_MIC_HOST or mic_device_name != config.CHOICE_MIC_DEVICE: + fnc_mic(mic_host_name, mic_device_name) + + if config.ENABLE_SPEAKER_AUTOMATIC_SELECTION is True: + default_device = getDefaultOutputDevice() + speaker_device_name = default_device["device"]["name"] + if speaker_device_name != config.CHOICE_SPEAKER_DEVICE: + fnc_speaker(speaker_device_name) + sleep(1) + + if isinstance(self.th_check_device, threadFnc) is False: + self.th_check_device = threadFnc(checkDevice, args=(fnc_mic, fnc_speaker,)) + self.th_check_device.daemon = True + self.th_check_device.start() + + def stopAutomaticDeviceSelection(self): + if config.ENABLE_MIC_AUTOMATIC_SELECTION is False and config.ENABLE_SPEAKER_AUTOMATIC_SELECTION is False: + if isinstance(self.th_check_device, threadFnc): + self.th_check_device.stop() + self.th_check_device = None + def startMicTranscript(self, fnc): if config.ENABLE_MIC_AUTOMATIC_SELECTION is True: default_device = getDefaultInputDevice() @@ -553,7 +581,10 @@ class Model: # self.mic_get_energy.stop() # self.mic_get_energy = None - def startCheckMicEnergy(self, fnc): + def startCheckMicEnergy(self, fnc:Callable[[float], None]=None) -> None: + if isinstance(fnc, Callable): + self.check_mic_energy_fnc = fnc + if config.ENABLE_MIC_AUTOMATIC_SELECTION is True: default_device = getDefaultInputDevice() mic_host_name = default_device["host"]["name"] @@ -572,7 +603,7 @@ class Model: if mic_energy_queue.empty() is False: energy = mic_energy_queue.get() try: - fnc(energy) + self.check_mic_energy_fnc(energy) except Exception: pass sleep(0.01) @@ -684,7 +715,10 @@ class Model: # self.speaker_get_energy.stop() # self.speaker_get_energy = None - def startCheckSpeakerEnergy(self, fnc): + def startCheckSpeakerEnergy(self, fnc:Callable[[float], None]=None) -> None: + if isinstance(fnc, Callable): + self.check_speaker_energy_fnc = fnc + if config.ENABLE_SPEAKER_AUTOMATIC_SELECTION is True: default_device = getDefaultOutputDevice() speaker_device_name = default_device["device"]["name"] @@ -701,7 +735,7 @@ class Model: if speaker_energy_queue.empty() is False: energy = speaker_energy_queue.get() try: - fnc(energy) + self.check_speaker_energy_fnc(energy) except Exception: pass sleep(0.01) diff --git a/src-python/webui_controller.py b/src-python/webui_controller.py index f7c94c69..9d766593 100644 --- a/src-python/webui_controller.py +++ b/src-python/webui_controller.py @@ -5,7 +5,7 @@ from threading import Thread import re from config import config from model import model -from utils import isUniqueStrings, printLog, printResponse +from utils import isUniqueStrings, printLog # Common class DownloadSoftwareProgressBar: @@ -778,22 +778,60 @@ def callbackClearDeeplAuthKey(*args, **kwargs) -> dict: # Transcription Tab # Transcription (Mic) +class UpdateSelectedDevice: + def __init__(self, action): + self.action = action -def callbackEnableMicAutomaticSelection(*args, **kwargs) -> dict: + def set_mic(self, host, device) -> None: + config.CHOICE_MIC_HOST = host + config.CHOICE_MIC_DEVICE = device + printLog("Update Host/Mic Device", f"{host}/{device}") + self.action("mic", { + "status":200, + "result":{"host":host, "device":device} + }) + + def set_speaker(self, device) -> None: + config.CHOICE_SPEAKER_DEVICE = device + printLog("Update Speaker Device", device) + self.action("speaker", { + "status":200, + "result":device + }) + +def callbackEnableMicAutomaticSelection(data, action, *args, **kwargs) -> dict: printLog("Enable Mic Automatic Selection") + update_device = UpdateSelectedDevice(action) + model.startAutomaticDeviceSelection(update_device.set_mic, update_device.set_speaker) config.ENABLE_MIC_AUTOMATIC_SELECTION = True return {"status":200, "result":config.ENABLE_MIC_AUTOMATIC_SELECTION} def callbackDisableMicAutomaticSelection(*args, **kwargs) -> dict: printLog("Disable Mic Automatic Selection") + model.stopAutomaticDeviceSelection() config.ENABLE_MIC_AUTOMATIC_SELECTION = False return {"status":200, "result":config.ENABLE_MIC_AUTOMATIC_SELECTION} +def callbackEnableSpeakerAutomaticSelection(data, action, *args, **kwargs) -> dict: + printLog("Enable Speaker Automatic Selection") + update_device = UpdateSelectedDevice(action) + model.startAutomaticDeviceSelection(update_device.set_mic, update_device.set_speaker) + config.ENABLE_SPEAKER_AUTOMATIC_SELECTION = True + return {"status":200, "result":config.ENABLE_SPEAKER_AUTOMATIC_SELECTION} + +def callbackDisableSpeakerAutomaticSelection(*args, **kwargs) -> dict: + printLog("Disable Speaker Automatic Selection") + model.stopAutomaticDeviceSelection() + config.ENABLE_SPEAKER_AUTOMATIC_SELECTION = False + return {"status":200, "result":config.ENABLE_SPEAKER_AUTOMATIC_SELECTION} + def callbackSetMicHost(data, *args, **kwargs) -> dict: printLog("Set Mic Host", data) config.CHOICE_MIC_HOST = data config.CHOICE_MIC_DEVICE = model.getInputDefaultDevice() - model.stopCheckMicEnergy() + if config.ENABLE_CHECK_ENERGY_SEND is True: + model.stopCheckMicEnergy() + model.startCheckMicEnergy() return {"status":200, "result":{ "host":config.CHOICE_MIC_HOST, @@ -804,8 +842,10 @@ def callbackSetMicHost(data, *args, **kwargs) -> dict: def callbackSetMicDevice(data, *args, **kwargs) -> dict: printLog("Set Mic Device", data) config.CHOICE_MIC_DEVICE = data - model.stopCheckMicEnergy() - return {"status":200, "result":config.CHOICE_MIC_DEVICE} + if config.ENABLE_CHECK_ENERGY_SEND is True: + model.stopCheckMicEnergy() + model.startCheckMicEnergy() + return {"status":200, "result":{"host":config.CHOICE_MIC_HOST, "device":config.CHOICE_MIC_DEVICE}} def callbackSetMicEnergyThreshold(data, *args, **kwargs) -> dict: printLog("Set Mic Energy Threshold", data) @@ -926,21 +966,12 @@ def callbackDeleteMicWordFilter(data, *args, **kwargs) -> dict: return {"status":200, "result":config.INPUT_MIC_WORD_FILTER} # Transcription (Speaker) - -def callbackEnableSpeakerAutomaticSelection(*args, **kwargs) -> dict: - printLog("Enable Speaker Automatic Selection") - config.ENABLE_SPEAKER_AUTOMATIC_SELECTION = True - return {"status":200, "result":config.ENABLE_SPEAKER_AUTOMATIC_SELECTION} - -def callbackDisableSpeakerAutomaticSelection(*args, **kwargs) -> dict: - printLog("Disable Speaker Automatic Selection") - config.ENABLE_SPEAKER_AUTOMATIC_SELECTION = False - return {"status":200, "result":config.ENABLE_SPEAKER_AUTOMATIC_SELECTION} - def callbackSetSpeakerDevice(data, *args, **kwargs) -> dict: printLog("Set Speaker Device", data) config.CHOICE_SPEAKER_DEVICE = data - model.stopCheckSpeakerEnergy() + if config.ENABLE_CHECK_ENERGY_RECEIVE is True: + model.stopCheckSpeakerEnergy() + model.startCheckSpeakerEnergy() return {"status":200, "result":config.CHOICE_SPEAKER_DEVICE} def callbackSetSpeakerEnergyThreshold(data, *args, **kwargs) -> dict: @@ -1325,7 +1356,7 @@ def getListInputDevice(*args, **kwargs) -> dict: def getListOutputDevice(*args, **kwargs) -> dict: return {"status":200, "result": model.getListOutputDevice()} -def init(endpoints:dict, *args, **kwargs) -> None: +def init(actions:dict, *args, **kwargs) -> None: printLog("Start Initialization") printLog("Start check DeepL API Key") @@ -1343,9 +1374,8 @@ def init(endpoints:dict, *args, **kwargs) -> None: # check Downloaded CTranslate2 Model Weight printLog("Check Downloaded CTranslate2 Model Weight") if config.USE_TRANSLATION_FEATURE is True and model.checkCTranslatorCTranslate2ModelWeight() is False: - def callback(progress): - printResponse(200, endpoints["ctranslate2"], {"progress":progress}) - startThreadingDownloadCtranslate2Weight(callback) + download = DownloadCTranslate2ProgressBar(actions["download_ctranslate2"]) + startThreadingDownloadCtranslate2Weight(download.set) # set Transcription Engine printLog("Set Transcription Engine") @@ -1357,9 +1387,8 @@ def init(endpoints:dict, *args, **kwargs) -> None: # check Downloaded Whisper Model Weight printLog("Check Downloaded Whisper Model Weight") if config.USE_WHISPER_FEATURE is True and model.checkTranscriptionWhisperModelWeight() is False: - def callback(progress): - printResponse(200, endpoints["whisper"], {"progress":progress}) - startThreadingDownloadWhisperWeight(callback) + download = DownloadWhisperProgressBar(actions["download_whisper"]) + startThreadingDownloadWhisperWeight(download.set) # set word filter printLog("Set Word Filter") @@ -1380,4 +1409,10 @@ def init(endpoints:dict, *args, **kwargs) -> None: model.startReceiveOSC() if config.ENABLE_VRC_MIC_MUTE_SYNC is True: model.startCheckMuteSelfStatus() - printLog("End Initialization") \ No newline at end of file + printLog("End Initialization") + + # init Auto device selection + printLog("Init Auto Device Selection") + if config.ENABLE_MIC_AUTOMATIC_SELECTION is True or config.ENABLE_SPEAKER_AUTOMATIC_SELECTION is True: + update_device = UpdateSelectedDevice(actions["update_selected_device"]) + model.startAutomaticDeviceSelection(update_device.set_mic, update_device.set_speaker) \ No newline at end of file diff --git a/src-python/webui_mainloop.py b/src-python/webui_mainloop.py index 1e96b257..f4c4a4cd 100644 --- a/src-python/webui_mainloop.py +++ b/src-python/webui_mainloop.py @@ -240,6 +240,14 @@ action_mapping = { "/controller/callback_download_whisper_weight": { "download":"/action/download_whisper_weight" }, + "/controller/callback_enable_mic_automatic_selection": { + "mic":"/controller/callback_set_mic_host", + "speaker":"/controller/callback_set_speaker_device", + }, + "/controller/callback_enable_speaker_automatic_selection": { + "mic":"/controller/callback_set_mic_host", + "speaker":"/controller/callback_set_speaker_device", + } } def handleConfigRequest(endpoint): @@ -259,12 +267,16 @@ def handleControllerRequest(endpoint, data=None): status = 404 else: action_endpoint = action_mapping.get(endpoint, None) - if action_endpoint is not None: - response = handler(data, Action(action_endpoint).transmit) - else: - response = handler(data) - status = response.get("status", None) - result = response.get("result", None) + try: + if action_endpoint is not None: + response = handler(data, Action(action_endpoint).transmit) + else: + response = handler(data) + status = response.get("status", None) + result = response.get("result", None) + except Exception as e: + result = str(e) + status = 500 return result, status class Action: @@ -272,9 +284,12 @@ class Action: self.endpoints = endpoints def transmit(self, key:str, data:dict) -> None: - status = data.get("status", None) - result = data.get("result", None) - printResponse(status, self.endpoints[key], result) + if key not in self.endpoints: + printLog("Invalid endpoint", key) + else: + status = data.get("status", None) + result = data.get("result", None) + printResponse(status, self.endpoints[key], result) def main(): received_data = sys.stdin.readline().strip() @@ -284,7 +299,7 @@ def main(): endpoint = received_data.get("endpoint", None) data = received_data.get("data", None) data = encodeBase64(data) if data is not None else None - printLog(endpoint, data) + printLog(endpoint, {"receive_data":data}) try: match endpoint.split("/")[1]: @@ -297,12 +312,14 @@ def main(): except Exception as e: result = str(e) status = 500 + printLog(endpoint, {"send_data":result}) printResponse(status, endpoint, result) if __name__ == "__main__": controller.init({ - "ctranslate2": action_mapping["/controller/callback_download_ctranslate2_weight"]["download"], - "whisper": action_mapping["/controller/callback_download_whisper_weight"]["download"], + "download_ctranslate2": Action(action_mapping["/controller/callback_download_ctranslate2_weight"]).transmit, + "download_whisper": Action(action_mapping["/controller/callback_download_whisper_weight"]).transmit, + "update_selected_device": Action(action_mapping["/controller/callback_enable_mic_automatic_selection"]).transmit, }) process = "main" @@ -317,12 +334,11 @@ if __name__ == "__main__": traceback.print_exc(file=f) case "test": - endpoint = "/controller/callback_download_ctranslate2_weight" - result, status = handleControllerRequest(endpoint) - printResponse(status, endpoint, result) - endpoint = "/controller/callback_download_whisper_weight" - result, status = handleControllerRequest(endpoint) - printResponse(status, endpoint, result) + for _ in range(100): + time.sleep(0.5) + endpoint = "/controller/list_mic_host" + result, status = handleControllerRequest(endpoint) + printResponse(status, endpoint, result) case "test_all": import time