[Update] 要素機能をmodel.pyを通して処理するように変更

This commit is contained in:
misygauziya
2023-08-18 15:39:57 +09:00
parent 19b29b4955
commit 027e9397d5
4 changed files with 445 additions and 299 deletions

240
VRCT.py
View File

@@ -1,35 +1,21 @@
from time import sleep
from os import path as os_path
from requests import get as requests_get
from queue import Queue
import customtkinter
from customtkinter import CTk, CTkFrame, CTkCheckBox, CTkFont, CTkButton, CTkImage, CTkTabview, CTkTextbox, CTkEntry
from PIL.Image import open as Image_open
from flashtext import KeywordProcessor
from threading import Thread
from utils import print_textbox, thread_fnc, get_localized_text, widget_main_window_label_setter
from osc_tools import send_typing, send_message, send_test_action, receive_osc_parameters
from utils import print_textbox, get_localized_text, widget_main_window_label_setter
from window_config import ToplevelWindowConfig
from window_information import ToplevelWindowInformation
from languages import transcription_lang
from audio_utils import get_input_device_list, get_output_device_list
from audio_recorder import SelectedMicRecorder, SelectedSpeakerRecorder
from audio_transcriber import AudioTranscriber
from translation import Translator
from config import config
from notification import notification_xsoverlay_for_vrct
__version__ = "1.3.2"
from model import model
class App(CTk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# init instance
self.translator = Translator()
self.keyword_processor = KeywordProcessor()
## set UI theme
customtkinter.set_appearance_mode(config.APPEARANCE_THEME)
customtkinter.set_default_color_theme("blue")
@@ -68,28 +54,16 @@ class App(CTk):
def init_process(self):
# set translator
if self.translator.authentication(config.CHOICE_TRANSLATOR, config.AUTH_KEYS[config.CHOICE_TRANSLATOR]) is False:
if model.authenticationTranslator() is False:
# error update Auth key
print_textbox(self.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR")
print_textbox(self.textbox_message_system_log, "Auth Key or language setting is incorrect", "ERROR")
# set word filter
for f in config.INPUT_MIC_WORD_FILTER:
self.keyword_processor.add_keyword(f)
model.addKeywords()
# start receive osc
th_receive_osc_parameters = Thread(target=receive_osc_parameters, args=(self.check_osc_receive,))
th_receive_osc_parameters.daemon = True
th_receive_osc_parameters.start()
# check osc started
send_test_action()
# check update
response = requests_get(config.GITHUB_URL)
tag_name = response.json()["tag_name"]
if tag_name != __version__:
config.UPDATE_FLAG = True
# check OSC started
model.oscCheck()
def button_config_callback(self):
self.foreground_stop()
@@ -117,7 +91,8 @@ class App(CTk):
self.information_window.focus()
def checkbox_translation_callback(self):
if self.checkbox_translation.get() is True:
config.ENABLE_TRANSLATION = self.checkbox_translation.get()
if config.ENABLE_TRANSLATION is True:
print_textbox(self.textbox_message_log, "Start translation", "INFO")
print_textbox(self.textbox_message_system_log, "Start translation", "INFO")
else:
@@ -125,61 +100,7 @@ class App(CTk):
print_textbox(self.textbox_message_system_log, "Stop translation", "INFO")
def transcription_send_start(self):
self.mic_audio_queue = Queue()
mic_device = [device for device in get_input_device_list()[config.CHOICE_MIC_HOST] if device["name"] == config.CHOICE_MIC_DEVICE][0]
self.mic_audio_recorder = SelectedMicRecorder(
mic_device,
config.INPUT_MIC_ENERGY_THRESHOLD,
config.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD,
config.INPUT_MIC_RECORD_TIMEOUT,
)
self.mic_audio_recorder.record_into_queue(self.mic_audio_queue)
self.mic_transcriber = AudioTranscriber(
speaker=False,
source=self.mic_audio_recorder.source,
phrase_timeout=config.INPUT_MIC_PHRASE_TIMEOUT,
max_phrases=config.INPUT_MIC_MAX_PHRASES,
)
def mic_transcript_to_chatbox():
self.mic_transcriber.transcribe_audio_queue(self.mic_audio_queue, transcription_lang[config.INPUT_MIC_VOICE_LANGUAGE])
message = self.mic_transcriber.get_transcript()
if len(message) > 0:
# word filter
if len(self.keyword_processor.extract_keywords(message)) != 0:
print_textbox(self.textbox_message_log, f"Detect WordFilter :{message}", "INFO")
print_textbox(self.textbox_message_system_log, f"Detect WordFilter :{message}", "INFO")
return
# translate
if self.checkbox_translation.get() is False:
voice_message = f"{message}"
elif self.translator.translator_status[config.CHOICE_TRANSLATOR] is False:
print_textbox(self.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR")
print_textbox(self.textbox_message_system_log, "Auth Key or language setting is incorrect", "ERROR")
voice_message = f"{message}"
else:
result = self.translator.translate(
translator_name=config.CHOICE_TRANSLATOR,
source_language=config.INPUT_SOURCE_LANG,
target_language=config.INPUT_TARGET_LANG,
message=message
)
voice_message = config.MESSAGE_FORMAT.replace("[message]", message).replace("[translation]", result)
if self.checkbox_transcription_send.get() is True:
if config.ENABLE_OSC is True:
# send OSC message
send_message(voice_message, config.OSC_IP_ADDRESS, config.OSC_PORT)
else:
print_textbox(self.textbox_message_log, "OSC is not enabled, please enable OSC and rejoin.", "ERROR")
print_textbox(self.textbox_message_system_log, "OSC is not enabled, please enable OSC and rejoin.", "ERROR")
# update textbox message log
print_textbox(self.textbox_message_log, f"{voice_message}", "SEND")
print_textbox(self.textbox_message_send_log, f"{voice_message}", "SEND")
self.mic_print_transcript = thread_fnc(mic_transcript_to_chatbox)
self.mic_print_transcript.daemon = True
self.mic_print_transcript.start()
model.startMicTranscript(self.textbox_message_log, self.textbox_message_send_log, self.textbox_message_system_log)
print_textbox(self.textbox_message_log, "Start voice2chatbox", "INFO")
print_textbox(self.textbox_message_system_log, "Start voice2chatbox", "INFO")
self.checkbox_transcription_send.configure(state="normal")
@@ -187,12 +108,7 @@ class App(CTk):
self.button_config.configure(state="normal", fg_color=["#3B8ED0", "#1F6AA5"])
def transcription_send_stop(self):
if isinstance(self.mic_print_transcript, thread_fnc):
self.mic_print_transcript.stop()
if self.mic_audio_recorder.stop != None:
self.mic_audio_recorder.stop()
self.mic_audio_recorder.stop = None
model.stopMicTranscript()
print_textbox(self.textbox_message_log, "Stop voice2chatbox", "INFO")
print_textbox(self.textbox_message_system_log, "Stop voice2chatbox", "INFO")
self.checkbox_transcription_send.configure(state="normal")
@@ -200,20 +116,16 @@ class App(CTk):
self.button_config.configure(state="normal", fg_color=["#3B8ED0", "#1F6AA5"])
def transcription_send_stop_for_config(self):
if isinstance(self.mic_print_transcript, thread_fnc):
self.mic_print_transcript.stop()
if self.mic_audio_recorder.stop != None:
self.mic_audio_recorder.stop()
self.mic_audio_recorder.stop = None
model.stopMicTranscript()
print_textbox(self.textbox_message_log, "Stop voice2chatbox", "INFO")
print_textbox(self.textbox_message_system_log, "Stop voice2chatbox", "INFO")
def checkbox_transcription_send_callback(self):
config.ENABLE_TRANSCRIPTION_SEND = self.checkbox_transcription_send.get()
self.checkbox_transcription_send.configure(state="disabled")
self.checkbox_transcription_receive.configure(state="disabled")
self.button_config.configure(state="disabled", fg_color=["gray92", "gray14"])
if self.checkbox_transcription_send.get() is True:
if config.ENABLE_TRANSCRIPTION_SEND is True:
th_transcription_send_start = Thread(target=self.transcription_send_start)
th_transcription_send_start.daemon = True
th_transcription_send_start.start()
@@ -223,54 +135,7 @@ class App(CTk):
th_transcription_send_stop.start()
def transcription_receive_start(self):
self.spk_audio_queue = Queue()
spk_device = [device for device in get_output_device_list() if device["name"] == config.CHOICE_SPEAKER_DEVICE][0]
self.spk_audio_recorder = SelectedSpeakerRecorder(
spk_device,
config.INPUT_SPEAKER_ENERGY_THRESHOLD,
config.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD,
config.INPUT_SPEAKER_RECORD_TIMEOUT,
)
self.spk_audio_recorder.record_into_queue(self.spk_audio_queue)
self.spk_transcriber = AudioTranscriber(
speaker=True,
source=self.spk_audio_recorder.source,
phrase_timeout=config.INPUT_SPEAKER_PHRASE_TIMEOUT,
max_phrases=config.INPUT_SPEAKER_MAX_PHRASES,
)
def spk_transcript_to_textbox():
self.spk_transcriber.transcribe_audio_queue(self.spk_audio_queue, transcription_lang[config.INPUT_SPEAKER_VOICE_LANGUAGE])
message = self.spk_transcriber.get_transcript()
if len(message) > 0:
# translate
if self.checkbox_translation.get() is False:
voice_message = f"{message}"
elif self.translator.translator_status[config.CHOICE_TRANSLATOR] is False:
print_textbox(self.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR")
print_textbox(self.textbox_message_system_log, "Auth Key or language setting is incorrect", "ERROR")
voice_message = f"{message}"
else:
result = self.translator.translate(
translator_name=config.CHOICE_TRANSLATOR,
source_language=config.OUTPUT_SOURCE_LANG,
target_language=config.OUTPUT_TARGET_LANG,
message=message
)
voice_message = config.MESSAGE_FORMAT.replace("[message]", message).replace("[translation]", result)
# send OSC message
# send_message(voice_message, config.OSC_IP_ADDRESS, self.OSC_PORT)
if self.checkbox_transcription_receive.get() is True:
# update textbox message receive log
print_textbox(self.textbox_message_log, f"{voice_message}", "RECEIVE")
print_textbox(self.textbox_message_receive_log, f"{voice_message}", "RECEIVE")
if config.ENABLE_NOTICE_XSOVERLAY is True:
notification_xsoverlay_for_vrct(content=f"{voice_message}")
self.spk_print_transcript = thread_fnc(spk_transcript_to_textbox)
self.spk_print_transcript.daemon = True
self.spk_print_transcript.start()
model.startSpeakerTranscript(self.textbox_message_log, self.textbox_message_receive_log, self.textbox_message_system_log)
print_textbox(self.textbox_message_log, "Start speaker2log", "INFO")
print_textbox(self.textbox_message_system_log, "Start speaker2log", "INFO")
self.checkbox_transcription_send.configure(state="normal")
@@ -278,12 +143,7 @@ class App(CTk):
self.button_config.configure(state="normal", fg_color=["#3B8ED0", "#1F6AA5"])
def transcription_receive_stop(self):
if isinstance(self.spk_print_transcript, thread_fnc):
self.spk_print_transcript.stop()
if self.spk_audio_recorder.stop != None:
self.spk_audio_recorder.stop()
self.spk_audio_recorder.stop = None
model.stopSpeakerTranscript()
print_textbox(self.textbox_message_log, "Stop speaker2log", "INFO")
print_textbox(self.textbox_message_system_log, "Stop speaker2log", "INFO")
self.checkbox_transcription_send.configure(state="normal")
@@ -291,20 +151,16 @@ class App(CTk):
self.button_config.configure(state="normal", fg_color=["#3B8ED0", "#1F6AA5"])
def transcription_receive_stop_for_config(self):
if isinstance(self.spk_print_transcript, thread_fnc):
self.spk_print_transcript.stop()
if self.spk_audio_recorder.stop != None:
self.spk_audio_recorder.stop()
self.spk_audio_recorder.stop = None
model.stopSpeakerTranscript()
print_textbox(self.textbox_message_log, "Stop speaker2log", "INFO")
print_textbox(self.textbox_message_system_log, "Stop speaker2log", "INFO")
def checkbox_transcription_receive_callback(self):
config.ENABLE_TRANSCRIPTION_RECEIVE = self.checkbox_transcription_receive.get()
self.checkbox_transcription_send.configure(state="disabled")
self.checkbox_transcription_receive.configure(state="disabled")
self.button_config.configure(state="disabled", fg_color=["gray92", "gray14"])
if self.checkbox_transcription_receive.get() is True:
if config.ENABLE_TRANSCRIPTION_RECEIVE is True:
th_transcription_receive_start = Thread(target=self.transcription_receive_start)
th_transcription_receive_start.daemon = True
th_transcription_receive_start.start()
@@ -314,28 +170,29 @@ class App(CTk):
th_transcription_receive_stop.start()
def transcription_start(self):
if self.checkbox_transcription_send.get() is True:
if config.ENABLE_TRANSCRIPTION_SEND is True:
th_transcription_send_start = Thread(target=self.transcription_send_start)
th_transcription_send_start.daemon = True
th_transcription_send_start.start()
sleep(2)
if self.checkbox_transcription_receive.get() is True:
if config.ENABLE_TRANSCRIPTION_RECEIVE is True:
th_transcription_receive_start = Thread(target=self.transcription_receive_start)
th_transcription_receive_start.daemon = True
th_transcription_receive_start.start()
def transcription_stop(self):
if self.checkbox_transcription_send.get() is True:
if config.ENABLE_TRANSCRIPTION_SEND is True:
th_transcription_send_stop = Thread(target=self.transcription_send_stop_for_config)
th_transcription_send_stop.daemon = True
th_transcription_send_stop.start()
if self.checkbox_transcription_receive.get() is True:
if config.ENABLE_TRANSCRIPTION_RECEIVE is True:
th_transcription_receive_stop = Thread(target=self.transcription_receive_stop_for_config)
th_transcription_receive_stop.daemon = True
th_transcription_receive_stop.start()
def checkbox_foreground_callback(self):
if self.checkbox_foreground.get():
config.ENABLE_FOREGROUND = self.checkbox_foreground.get()
if config.ENABLE_FOREGROUND:
self.attributes("-topmost", True)
print_textbox(self.textbox_message_log, "Start foreground", "INFO")
print_textbox(self.textbox_message_system_log, "Start foreground", "INFO")
@@ -345,45 +202,39 @@ class App(CTk):
print_textbox(self.textbox_message_system_log, "Stop foreground", "INFO")
def foreground_start(self):
if self.checkbox_foreground.get():
if config.ENABLE_FOREGROUND:
self.attributes("-topmost", True)
print_textbox(self.textbox_message_log, "Start foreground", "INFO")
print_textbox(self.textbox_message_system_log, "Start foreground", "INFO")
def foreground_stop(self):
if self.checkbox_foreground.get():
if config.ENABLE_FOREGROUND:
self.attributes("-topmost", False)
print_textbox(self.textbox_message_log, "Stop foreground", "INFO")
print_textbox(self.textbox_message_system_log, "Stop foreground", "INFO")
def entry_message_box_press_key_enter(self, event):
# send OSC typing
send_typing(False, config.OSC_IP_ADDRESS, config.OSC_PORT)
# osc stop send typing
model.oscStopSendTyping()
if self.checkbox_foreground.get():
if config.ENABLE_FOREGROUND:
self.attributes("-topmost", True)
message = self.entry_message_box.get()
if len(message) > 0:
# translate
if self.checkbox_translation.get() is False:
if config.ENABLE_TRANSLATION is False:
chat_message = f"{message}"
elif self.translator.translator_status[config.CHOICE_TRANSLATOR] is False:
elif model.getTranslatorStatus() is False:
print_textbox(self.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR")
print_textbox(self.textbox_message_system_log, "Auth Key or language setting is incorrect", "ERROR")
chat_message = f"{message}"
else:
result = self.translator.translate(
translator_name=config.CHOICE_TRANSLATOR,
source_language=config.INPUT_SOURCE_LANG,
target_language=config.INPUT_TARGET_LANG,
message=message
)
chat_message = config.MESSAGE_FORMAT.replace("[message]", message).replace("[translation]", result)
chat_message = model.getInputTranslate(message)
# send OSC message
if config.ENABLE_OSC is True:
send_message(chat_message, config.OSC_IP_ADDRESS, config.OSC_PORT)
model.oscSendMessage(chat_message)
else:
print_textbox(self.textbox_message_log, "OSC is not enabled, please enable OSC and rejoin.", "ERROR")
print_textbox(self.textbox_message_system_log, "OSC is not enabled, please enable OSC and rejoin.", "ERROR")
@@ -396,25 +247,21 @@ class App(CTk):
if config.ENABLE_AUTO_CLEAR_CHATBOX is True:
self.entry_message_box.delete(0, customtkinter.END)
BREAK_KEYSYM_LIST = [
"Delete", "Select", "Up", "Down", "Next", "End", "Print",
"Prior","Insert","Home", "Left", "Clear", "Right", "Linefeed"
]
def entry_message_box_press_key_any(self, event):
# send OSC typing
send_typing(True, config.OSC_IP_ADDRESS, config.OSC_PORT)
if self.checkbox_foreground.get():
# osc start send typing
model.oscStartSendTyping()
if config.ENABLE_FOREGROUND:
self.attributes("-topmost", False)
if event.keysym != "??":
if len(event.char) != 0 and event.keysym in self.BREAK_KEYSYM_LIST:
if len(event.char) != 0 and event.keysym in config.BREAK_KEYSYM_LIST:
self.entry_message_box.insert("end", event.char)
return "break"
def entry_message_box_leave(self, event):
# send OSC typing
send_typing(False, config.OSC_IP_ADDRESS, config.OSC_PORT)
if self.checkbox_foreground.get():
# osc stop send typing
model.oscStopSendTyping()
if config.ENABLE_FOREGROUND:
self.attributes("-topmost", True)
def delete_window(self):
@@ -558,11 +405,6 @@ class App(CTk):
widget_main_window_label_setter(self, language_yaml_data)
def check_osc_receive(self, address, osc_arguments):
print(address, osc_arguments)
if config.ENABLE_OSC is False:
config.ENABLE_OSC = True
if __name__ == "__main__":
try:
app = App()

View File

@@ -18,10 +18,50 @@ class Config:
cls._instance.load_config()
return cls._instance
@property
def VERSION(self):
return self._VERSION
@property
def PATH_CONFIG(self):
return self._PATH_CONFIG
@property
def ENABLE_TRANSLATION(self):
return self._ENABLE_TRANSLATION
@ENABLE_TRANSLATION.setter
def ENABLE_TRANSLATION(self, value):
if type(value) is bool:
self._ENABLE_TRANSLATION = value
@property
def ENABLE_TRANSCRIPTION_SEND(self):
return self._ENABLE_TRANSCRIPTION_SEND
@ENABLE_TRANSCRIPTION_SEND.setter
def ENABLE_TRANSCRIPTION_SEND(self, value):
if type(value) is bool:
self._ENABLE_TRANSCRIPTION_SEND = value
@property
def ENABLE_TRANSCRIPTION_RECEIVE(self):
return self._ENABLE_TRANSCRIPTION_RECEIVE
@ENABLE_TRANSCRIPTION_RECEIVE.setter
def ENABLE_TRANSCRIPTION_RECEIVE(self, value):
if type(value) is bool:
self._ENABLE_TRANSCRIPTION_RECEIVE = value
@property
def ENABLE_FOREGROUND(self):
return self._ENABLE_FOREGROUND
@ENABLE_FOREGROUND.setter
def ENABLE_FOREGROUND(self, value):
if type(value) is bool:
self._ENABLE_FOREGROUND = value
@property
def TRANSPARENCY(self):
return self._TRANSPARENCY
@@ -371,8 +411,25 @@ class Config:
def GITHUB_URL(self):
return self._GITHUB_URL
@property
def BREAK_KEYSYM_LIST(self):
return self._BREAK_KEYSYM_LIST
@property
def MAX_MIC_ENERGY_THRESHOLD(self):
return self._MAX_MIC_ENERGY_THRESHOLD
@property
def MAX_SPEAKER_ENERGY_THRESHOLD(self):
return self._MAX_SPEAKER_ENERGY_THRESHOLD
def init_config(self):
self._VERSION = "1.3.2"
self._PATH_CONFIG = "./config.json"
self._ENABLE_TRANSLATION = False
self._ENABLE_TRANSCRIPTION_SEND = False
self._ENABLE_TRANSCRIPTION_RECEIVE = False
self._ENABLE_FOREGROUND = False
self._TRANSPARENCY = 100
self._APPEARANCE_THEME = "System"
self._UI_SCALING = "100%"
@@ -413,6 +470,12 @@ class Config:
self._ENABLE_OSC = False
self._UPDATE_FLAG = False
self._GITHUB_URL = "https://api.github.com/repos/misyaguziya/VRCT/releases/latest"
self._BREAK_KEYSYM_LIST = [
"Delete", "Select", "Up", "Down", "Next", "End", "Print",
"Prior","Insert","Home", "Left", "Clear", "Right", "Linefeed"
]
self._MAX_MIC_ENERGY_THRESHOLD = 2000
self._MAX_SPEAKER_ENERGY_THRESHOLD = 4000
def load_config(self):
if os_path.isfile(self.PATH_CONFIG) is not False:

304
model.py Normal file
View File

@@ -0,0 +1,304 @@
from time import sleep
from queue import Queue
from threading import Thread
from requests import get as requests_get
from translation import Translator
from flashtext import KeywordProcessor
from osc_tools import send_typing, send_message, send_test_action, receive_osc_parameters
from languages import transcription_lang
from audio_utils import get_input_device_list, get_output_device_list, get_default_output_device
from audio_recorder import SelectedMicRecorder, SelectedSpeakerRecorder
from audio_recorder import SelectedMicEnergyRecorder, SelectedSpeakeEnergyRecorder
from audio_transcriber import AudioTranscriber
from utils import print_textbox, thread_fnc
from config import config
from notification import notification_xsoverlay_for_vrct
class Model:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super(Model, cls).__new__(cls)
cls._instance.init()
return cls._instance
def init(self):
self.mic_energy_recorder = None
self.mic_energy_plot_progressbar = None
self.speaker_energy_get_progressbar = None
self.speaker_energy_plot_progressbar = None
self.translator = Translator()
self.keyword_processor = KeywordProcessor()
def resetTranslator(self):
del self.translator
self.translator = Translator()
def resetKeywordProcessor(self):
del self.translator
self.keyword_processor = KeywordProcessor()
def authenticationTranslator(self, choice_translator=None, auth_key=None):
if choice_translator == None:
choice_translator = config.CHOICE_TRANSLATOR
if auth_key == None:
auth_key = config.AUTH_KEYS[choice_translator]
result = self.translator.authentication(choice_translator, auth_key)
if result:
auth_keys = config.AUTH_KEYS
auth_keys[choice_translator] = auth_key
config.AUTH_KEYS = auth_keys
return result
def getTranslatorStatus(self):
return self.translator.translator_status[config.CHOICE_TRANSLATOR]
def getListTranslatorName(self):
return list(self.translator.translator_status.keys())
def getInputTranslate(self, message):
translation = self.translator.translate(
translator_name=config.CHOICE_TRANSLATOR,
source_language=config.INPUT_SOURCE_LANG,
target_language=config.INPUT_TARGET_LANG,
message=message
)
message = config.MESSAGE_FORMAT.replace("[message]", message).replace("[translation]", translation)
return message
def getOutputTranslate(self, message):
translation = self.translator.translate(
translator_name=config.CHOICE_TRANSLATOR,
source_language=config.OUTPUT_SOURCE_LANG,
target_language=config.OUTPUT_TARGET_LANG,
message=message
)
message = config.MESSAGE_FORMAT.replace("[message]", message).replace("[translation]", translation)
return message
def addKeywords(self):
for f in config.INPUT_MIC_WORD_FILTER:
self.keyword_processor.add_keyword(f)
def checkKeywords(self, message):
return len(self.keyword_processor.extract_keywords(message)) != 0
@staticmethod
def oscStartSendTyping():
send_typing(True, config.OSC_IP_ADDRESS, config.OSC_PORT)
@staticmethod
def oscStopSendTyping():
send_typing(False, config.OSC_IP_ADDRESS, config.OSC_PORT)
@staticmethod
def oscSendMessage(message):
send_message(message, config.OSC_IP_ADDRESS, config.OSC_PORT)
@staticmethod
def oscCheck():
def check_osc_receive(address, osc_arguments):
if config.ENABLE_OSC is False:
config.ENABLE_OSC = True
# start receive osc
th_receive_osc_parameters = Thread(target=receive_osc_parameters, args=(check_osc_receive,))
th_receive_osc_parameters.daemon = True
th_receive_osc_parameters.start()
# check osc started
send_test_action()
# check update
response = requests_get(config.GITHUB_URL)
tag_name = response.json()["tag_name"]
if tag_name != config.VERSION:
config.UPDATE_FLAG = True
@staticmethod
def getListInputHost():
return [host for host in get_input_device_list().keys()]
@staticmethod
def getListInputDevice():
return [device["name"] for device in get_input_device_list()[config.CHOICE_MIC_HOST]]
@staticmethod
def getInputDefaultDevice():
return [device["name"] for device in get_input_device_list()[config.CHOICE_MIC_HOST]][0]
@staticmethod
def getListOutputDevice():
return [device["name"] for device in get_output_device_list()]
@staticmethod
def checkSpeakerStatus(choice=config.CHOICE_SPEAKER_DEVICE):
speaker_device = [device for device in get_output_device_list() if device["name"] == choice][0]
if get_default_output_device()["index"] == speaker_device["index"]:
return True
return False
def startMicTranscript(self, log, send_log, system_log):
mic_audio_queue = Queue()
self.mic_audio_recorder = SelectedMicRecorder(
[device for device in get_input_device_list()[config.CHOICE_MIC_HOST] if device["name"] == config.CHOICE_MIC_DEVICE][0],
config.INPUT_MIC_ENERGY_THRESHOLD,
config.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD,
config.INPUT_MIC_RECORD_TIMEOUT,
)
self.mic_audio_recorder.record_into_queue(mic_audio_queue)
mic_transcriber = AudioTranscriber(
speaker=False,
source=self.mic_audio_recorder.source,
phrase_timeout=config.INPUT_MIC_PHRASE_TIMEOUT,
max_phrases=config.INPUT_MIC_MAX_PHRASES,
)
def mic_transcript_to_chatbox():
mic_transcriber.transcribe_audio_queue(mic_audio_queue, transcription_lang[config.INPUT_MIC_VOICE_LANGUAGE])
message = mic_transcriber.get_transcript()
if len(message) > 0:
print(message)
# word filter
if self.checkKeywords(message):
print_textbox(log, f"Detect WordFilter :{message}", "INFO")
print_textbox(system_log, f"Detect WordFilter :{message}", "INFO")
return
# translate
if config.ENABLE_TRANSLATION is False:
voice_message = f"{message}"
elif self.getTranslatorStatus() is False:
print_textbox(log, "Auth Key or language setting is incorrect", "ERROR")
print_textbox(system_log, "Auth Key or language setting is incorrect", "ERROR")
voice_message = f"{message}"
else:
voice_message = self.getInputTranslate(message)
if config.ENABLE_TRANSCRIPTION_SEND is True:
if config.ENABLE_OSC is True:
# osc send message
model.oscSendMessage(voice_message)
else:
print_textbox(log, "OSC is not enabled, please enable OSC and rejoin.", "ERROR")
print_textbox(system_log, "OSC is not enabled, please enable OSC and rejoin.", "ERROR")
# update textbox message log
print_textbox(log, f"{voice_message}", "SEND")
print_textbox(send_log, f"{voice_message}", "SEND")
self.mic_print_transcript = thread_fnc(mic_transcript_to_chatbox)
self.mic_print_transcript.daemon = True
self.mic_print_transcript.start()
def stopMicTranscript(self):
if isinstance(self.mic_print_transcript, thread_fnc):
self.mic_print_transcript.stop()
if self.mic_audio_recorder.stop != None:
self.mic_audio_recorder.stop()
self.mic_audio_recorder.stop = None
def startCheckMicEnergy(self, progressBar):
def progressBarInputMicEnergyPlot():
if mic_energy_queue.empty() is False:
energy = mic_energy_queue.get()
try:
progressBar.set(energy/config.MAX_MIC_ENERGY_THRESHOLD)
except:
pass
sleep(0.01)
mic_energy_queue = Queue()
mic_device = [device for device in get_input_device_list()[config.CHOICE_MIC_HOST] if device["name"] == config.CHOICE_MIC_DEVICE][0]
self.mic_energy_recorder = SelectedMicEnergyRecorder(mic_device)
self.mic_energy_recorder.record_into_queue(mic_energy_queue)
self.mic_energy_plot_progressbar = thread_fnc(progressBarInputMicEnergyPlot)
self.mic_energy_plot_progressbar.daemon = True
self.mic_energy_plot_progressbar.start()
def stopCheckMicEnergy(self):
if self.mic_energy_recorder != None:
self.mic_energy_recorder.stop()
if self.mic_energy_plot_progressbar != None:
self.mic_energy_plot_progressbar.stop()
def startSpeakerTranscript(self, log, receive_log, system_log):
spk_audio_queue = Queue()
spk_device = [device for device in get_output_device_list() if device["name"] == config.CHOICE_SPEAKER_DEVICE][0]
self.spk_audio_recorder = SelectedSpeakerRecorder(
spk_device,
config.INPUT_SPEAKER_ENERGY_THRESHOLD,
config.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD,
config.INPUT_SPEAKER_RECORD_TIMEOUT,
)
self.spk_audio_recorder.record_into_queue(spk_audio_queue)
spk_transcriber = AudioTranscriber(
speaker=True,
source=self.spk_audio_recorder.source,
phrase_timeout=config.INPUT_SPEAKER_PHRASE_TIMEOUT,
max_phrases=config.INPUT_SPEAKER_MAX_PHRASES,
)
def spk_transcript_to_textbox():
spk_transcriber.transcribe_audio_queue(spk_audio_queue, transcription_lang[config.INPUT_SPEAKER_VOICE_LANGUAGE])
message = spk_transcriber.get_transcript()
if len(message) > 0:
# translate
if config.ENABLE_TRANSLATION is False:
voice_message = f"{message}"
elif model.getTranslatorStatus() is False:
print_textbox(log, "Auth Key or language setting is incorrect", "ERROR")
print_textbox(system_log, "Auth Key or language setting is incorrect", "ERROR")
voice_message = f"{message}"
else:
voice_message = model.getOutputTranslate(message)
if config.ENABLE_TRANSCRIPTION_RECEIVE is True:
# update textbox message receive log
print_textbox(log, f"{voice_message}", "RECEIVE")
print_textbox(receive_log, f"{voice_message}", "RECEIVE")
if config.ENABLE_NOTICE_XSOVERLAY is True:
notification_xsoverlay_for_vrct(content=f"{voice_message}")
self.spk_print_transcript = thread_fnc(spk_transcript_to_textbox)
self.spk_print_transcript.daemon = True
self.spk_print_transcript.start()
def stopSpeakerTranscript(self):
if isinstance(self.spk_print_transcript, thread_fnc):
self.spk_print_transcript.stop()
if self.spk_audio_recorder.stop != None:
self.spk_audio_recorder.stop()
self.spk_audio_recorder.stop = None
def startCheckSpeakerEnergy(self, progressBar):
def progressBar_input_speaker_energy_plot():
if speaker_energy_queue.empty() is False:
energy = speaker_energy_queue.get()
try:
progressBar.set(energy/config.MAX_SPEAKER_ENERGY_THRESHOLD)
except:
pass
sleep(0.01)
def progressBar_input_speaker_energy_get():
with self.speaker_energy_recorder.source as source:
energy = self.speaker_energy_recorder.recorder.listen_energy(source)
self.speaker_energy_queue.put(energy)
speaker_device = [device for device in get_output_device_list() if device["name"] == config.CHOICE_SPEAKER_DEVICE][0]
speaker_energy_queue = Queue()
self.speaker_energy_recorder = SelectedSpeakeEnergyRecorder(speaker_device)
self.speaker_energy_get_progressbar = thread_fnc(progressBar_input_speaker_energy_get)
self.speaker_energy_get_progressbar.daemon = True
self.speaker_energy_get_progressbar.start()
self.speaker_energy_plot_progressbar = thread_fnc(progressBar_input_speaker_energy_plot)
self.speaker_energy_plot_progressbar.daemon = True
self.speaker_energy_plot_progressbar.start()
def stopCheckSpeakerEnergy(self):
if self.speaker_energy_get_progressbar != None:
self.speaker_energy_get_progressbar.stop()
if self.speaker_energy_plot_progressbar != None:
self.speaker_energy_plot_progressbar.stop()
model = Model()

View File

@@ -1,17 +1,13 @@
from time import sleep
from queue import Queue
from os import path as os_path
from tkinter import DoubleVar, IntVar
from tkinter import font as tk_font
import customtkinter
from customtkinter import CTkToplevel, CTkTabview, CTkFont, CTkLabel, CTkSlider, CTkOptionMenu, StringVar, CTkEntry, CTkCheckBox, CTkProgressBar
from flashtext import KeywordProcessor
from threading import Thread
from config import config
from utils import print_textbox, thread_fnc, get_localized_text, get_key_by_value, widget_config_window_label_setter
from audio_utils import get_input_device_list, get_output_device_list, get_default_output_device
from audio_recorder import SelectedMicEnergyRecorder, SelectedSpeakeEnergyRecorder
from model import model
from utils import print_textbox, get_localized_text, get_key_by_value, widget_config_window_label_setter
from languages import translation_lang, transcription_lang, selectable_languages
from ctk_scrollable_dropdown import CTkScrollableDropdown
@@ -33,15 +29,6 @@ class ToplevelWindowConfig(CTkToplevel):
self.after(200, lambda: self.iconbitmap(os_path.join(os_path.dirname(__file__), "img", "app.ico")))
self.title("Config")
# init parameter
self.MAX_MIC_ENERGY_THRESHOLD = 2000
self.MAX_SPEAKER_ENERGY_THRESHOLD = 4000
self.mic_energy_recorder = None
self.mic_energy_plot_progressbar = None
self.speaker_energy_recorder = None
self.speaker_energy_get_progressbar = None
self.speaker_energy_plot_progressbar = None
# load ui language data
language_yaml_data = get_localized_text(f"{config.UI_LANGUAGE}")
# add tabview config
@@ -202,7 +189,7 @@ class ToplevelWindowConfig(CTkToplevel):
def optionmenu_translation_translator_callback(self, choice):
self.optionmenu_translation_translator.set(choice)
if self.parent.translator.authentication(choice, config.AUTH_KEYS[choice]) is False:
if model.authenticationTranslator(choice_translator=choice) is False:
print_textbox(self.parent.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR")
print_textbox(self.parent.textbox_message_system_log, "Auth Key or language setting is incorrect", "ERROR")
else:
@@ -253,15 +240,15 @@ class ToplevelWindowConfig(CTkToplevel):
def optionmenu_input_mic_host_callback(self, choice):
self.optionmenu_input_mic_host.set(choice)
config.CHOICE_MIC_HOST = choice
config.CHOICE_MIC_DEVICE = model.getInputDefaultDevice()
self.optionmenu_input_mic_device.configure(
values=[device["name"] for device in get_input_device_list()[choice]],
variable=StringVar(value=[device["name"] for device in get_input_device_list()[choice]][0]))
values=model.getListInputDevice(),
variable=StringVar(value=model.getInputDefaultDevice()))
if SCROLLABLE_DROPDOWN:
self.scrollableDropdown_input_mic_device.configure(values=[device["name"] for device in get_input_device_list()[choice]])
config.CHOICE_MIC_HOST = choice
config.CHOICE_MIC_DEVICE = [device["name"] for device in get_input_device_list()[choice]][0]
self.scrollableDropdown_input_mic_device.configure(values=model.getListInputDevice())
def optionmenu_input_mic_device_callback(self, choice):
self.optionmenu_input_mic_device.set(choice)
@@ -273,31 +260,13 @@ class ToplevelWindowConfig(CTkToplevel):
self.optionmenu_input_mic_voice_language.set(choice)
config.INPUT_MIC_VOICE_LANGUAGE = choice
def progressBar_input_mic_energy_plot(self):
if self.mic_energy_queue.empty() is False:
energy = self.mic_energy_queue.get()
try:
self.progressBar_input_mic_energy_threshold.set(energy/self.MAX_MIC_ENERGY_THRESHOLD)
except:
pass
sleep(0.01)
def mic_threshold_check_start(self):
self.mic_energy_queue = Queue()
mic_device = [device for device in get_input_device_list()[config.CHOICE_MIC_HOST] if device["name"] == config.CHOICE_MIC_DEVICE][0]
self.mic_energy_recorder = SelectedMicEnergyRecorder(mic_device)
self.mic_energy_recorder.record_into_queue(self.mic_energy_queue)
self.mic_energy_plot_progressbar = thread_fnc(self.progressBar_input_mic_energy_plot)
self.mic_energy_plot_progressbar.daemon = True
self.mic_energy_plot_progressbar.start()
model.startCheckMicEnergy(self.progressBar_input_mic_energy_threshold)
self.checkbox_input_mic_threshold_check.configure(state="normal")
self.checkbox_input_speaker_threshold_check.configure(state="normal")
def mic_threshold_check_stop(self):
if self.mic_energy_recorder != None:
self.mic_energy_recorder.stop()
if self.mic_energy_plot_progressbar != None:
self.mic_energy_plot_progressbar.stop()
model.stopCheckMicEnergy()
self.progressBar_input_mic_energy_threshold.set(0)
self.checkbox_input_mic_threshold_check.configure(state="normal")
self.checkbox_input_speaker_threshold_check.configure(state="normal")
@@ -338,13 +307,11 @@ class ToplevelWindowConfig(CTkToplevel):
config.INPUT_MIC_WORD_FILTER = word_filter.split(",")
else:
config.INPUT_MIC_WORD_FILTER = []
self.parent.keyword_processor = KeywordProcessor()
for f in self.parent.INPUT_MIC_WORD_FILTER:
self.parent.keyword_processor.add_keyword(f)
model.resetKeywordProcessor()
model.addKeywords()
def optionmenu_input_speaker_device_callback(self, choice):
speaker_device = [device for device in get_output_device_list() if device["name"] == choice][0]
if get_default_output_device()["index"] == speaker_device["index"]:
if model.checkSpeakerStatus(choice):
self.optionmenu_input_speaker_device.set(choice)
config.CHOICE_SPEAKER_DEVICE = choice
else:
@@ -356,45 +323,13 @@ class ToplevelWindowConfig(CTkToplevel):
self.optionmenu_input_speaker_voice_language.set(choice)
config.INPUT_SPEAKER_VOICE_LANGUAGE = choice
def progressBar_input_speaker_energy_plot(self):
if self.speaker_energy_queue.empty() is False:
energy = self.speaker_energy_queue.get()
try:
self.progressBar_input_speaker_energy_threshold.set(energy/self.MAX_SPEAKER_ENERGY_THRESHOLD)
except:
pass
sleep(0.01)
def progressBar_input_speaker_energy_get(self):
with self.speaker_energy_recorder.source as source:
energy = self.speaker_energy_recorder.recorder.listen_energy(source)
self.speaker_energy_queue.put(energy)
def speaker_threshold_check_start(self):
speaker_device = [device for device in get_output_device_list() if device["name"] == config.CHOICE_SPEAKER_DEVICE][0]
if get_default_output_device()["index"] == speaker_device["index"]:
self.speaker_energy_queue = Queue()
self.speaker_energy_recorder = SelectedSpeakeEnergyRecorder(speaker_device)
self.speaker_energy_get_progressbar = thread_fnc(self.progressBar_input_speaker_energy_get)
self.speaker_energy_get_progressbar.daemon = True
self.speaker_energy_get_progressbar.start()
self.speaker_energy_plot_progressbar = thread_fnc(self.progressBar_input_speaker_energy_plot)
self.speaker_energy_plot_progressbar.daemon = True
self.speaker_energy_plot_progressbar.start()
else:
print_textbox(self.parent.textbox_message_log, "Windows playback device and selected device do not match. Change the Windows playback device.", "ERROR")
print_textbox(self.parent.textbox_message_system_log, "Windows playback device and selected device do not match. Change the Windows playback device.", "ERROR")
self.checkbox_input_speaker_threshold_check.deselect()
model.startCheckSpeakerEnergy(self.progressBar_input_speaker_energy_threshold)
self.checkbox_input_mic_threshold_check.configure(state="normal")
self.checkbox_input_speaker_threshold_check.configure(state="normal")
def speaker_threshold_check_stop(self):
if self.speaker_energy_get_progressbar != None:
self.speaker_energy_get_progressbar.stop()
if self.speaker_energy_plot_progressbar != None:
self.speaker_energy_plot_progressbar.stop()
model.stopCheckSpeakerEnergy()
self.progressBar_input_speaker_energy_threshold.set(0)
self.checkbox_input_mic_threshold_check.configure(state="normal")
self.checkbox_input_speaker_threshold_check.configure(state="normal")
@@ -404,9 +339,14 @@ class ToplevelWindowConfig(CTkToplevel):
self.checkbox_input_speaker_threshold_check.configure(state="disabled")
self.update()
if self.checkbox_input_speaker_threshold_check.get():
if model.checkSpeakerStatus():
th_speaker_threshold_check_start = Thread(target=self.speaker_threshold_check_start)
th_speaker_threshold_check_start.daemon = True
th_speaker_threshold_check_start.start()
else:
print_textbox(self.parent.textbox_message_log, "Windows playback device and selected device do not match. Change the Windows playback device.", "ERROR")
print_textbox(self.parent.textbox_message_system_log, "Windows playback device and selected device do not match. Change the Windows playback device.", "ERROR")
self.checkbox_input_speaker_threshold_check.deselect()
else:
th_speaker_threshold_check_stop = Thread(target=self.speaker_threshold_check_stop)
th_speaker_threshold_check_stop.daemon = True
@@ -436,10 +376,7 @@ class ToplevelWindowConfig(CTkToplevel):
def entry_authkey_callback(self, event):
value = self.entry_authkey.get()
if len(value) > 0:
if self.parent.translator.authentication("DeepL(auth)", value) is True:
auth_keys = config.AUTH_KEYS
auth_keys["DeepL(auth)"] = value
config.AUTH_KEYS = auth_keys
if model.authenticationTranslator(choice_translator="DeepL(auth)", auth_key=value) is True:
print_textbox(self.parent.textbox_message_log, "Auth key update completed", "INFO")
print_textbox(self.parent.textbox_message_system_log, "Auth key update completed", "INFO")
else:
@@ -683,7 +620,7 @@ class ToplevelWindowConfig(CTkToplevel):
self.label_translation_translator.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw")
self.optionmenu_translation_translator = CTkOptionMenu(
self.tabview_config.tab(config_tab_title_translation),
values=list(self.parent.translator.translator_status.keys()),
values=model.getListTranslatorName(),
command=self.optionmenu_translation_translator_callback,
variable=StringVar(value=config.CHOICE_TRANSLATOR),
font=CTkFont(family=config.FONT_FAMILY),
@@ -695,7 +632,7 @@ class ToplevelWindowConfig(CTkToplevel):
if SCROLLABLE_DROPDOWN:
self.scrollableDropdown_translation_translator = CTkScrollableDropdown(
self.optionmenu_translation_translator,
values=list(self.parent.translator.translator_status.keys()),
values=model.getListTranslatorName(),
justify="left",
button_color="transparent",
command=self.optionmenu_translation_translator_callback,
@@ -862,7 +799,7 @@ class ToplevelWindowConfig(CTkToplevel):
self.label_input_mic_host.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw")
self.optionmenu_input_mic_host = CTkOptionMenu(
self.tabview_config.tab(config_tab_title_transcription),
values=[host for host in get_input_device_list().keys()],
values=model.getListInputHost(),
command=self.optionmenu_input_mic_host_callback,
variable=StringVar(value=config.CHOICE_MIC_HOST),
font=CTkFont(family=config.FONT_FAMILY),
@@ -874,7 +811,7 @@ class ToplevelWindowConfig(CTkToplevel):
if SCROLLABLE_DROPDOWN:
self.scrollableDropdown_input_mic_host = CTkScrollableDropdown(
self.optionmenu_input_mic_host,
values=[host for host in get_input_device_list().keys()],
values=model.getListInputHost(),
justify="left",
button_color="transparent",
command=self.optionmenu_input_mic_host_callback,
@@ -896,7 +833,7 @@ class ToplevelWindowConfig(CTkToplevel):
self.label_input_mic_device.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw")
self.optionmenu_input_mic_device = CTkOptionMenu(
self.tabview_config.tab(config_tab_title_transcription),
values=[device["name"] for device in get_input_device_list()[config.CHOICE_MIC_HOST]],
values=model.getListInputDevice(),
command=self.optionmenu_input_mic_device_callback,
variable=StringVar(value=config.CHOICE_MIC_DEVICE),
font=CTkFont(family=config.FONT_FAMILY),
@@ -908,7 +845,7 @@ class ToplevelWindowConfig(CTkToplevel):
if SCROLLABLE_DROPDOWN:
self.scrollableDropdown_input_mic_device = CTkScrollableDropdown(
self.optionmenu_input_mic_device,
values=[device["name"] for device in get_input_device_list()[config.CHOICE_MIC_HOST]],
values=model.getListInputDevice(),
justify="left",
button_color="transparent",
command=self.optionmenu_input_mic_device_callback,
@@ -966,11 +903,11 @@ class ToplevelWindowConfig(CTkToplevel):
self.slider_input_mic_energy_threshold = CTkSlider(
self.tabview_config.tab(config_tab_title_transcription),
from_=0,
to=self.MAX_MIC_ENERGY_THRESHOLD,
to=config.MAX_MIC_ENERGY_THRESHOLD,
border_width=7,
button_length=0,
button_corner_radius=3,
number_of_steps=self.MAX_MIC_ENERGY_THRESHOLD,
number_of_steps=config.MAX_MIC_ENERGY_THRESHOLD,
command=self.slider_input_mic_energy_threshold_callback,
variable=IntVar(value=config.INPUT_MIC_ENERGY_THRESHOLD),
)
@@ -1102,7 +1039,7 @@ class ToplevelWindowConfig(CTkToplevel):
self.label_input_speaker_device.grid(row=row, column=0, columnspan=1, padx=padx, pady=pady, sticky="nsw")
self.optionmenu_input_speaker_device = CTkOptionMenu(
self.tabview_config.tab(config_tab_title_transcription),
values=[device["name"] for device in get_output_device_list()],
values=model.getListOutputDevice(),
command=self.optionmenu_input_speaker_device_callback,
variable=StringVar(value=config.CHOICE_SPEAKER_DEVICE),
font=CTkFont(family=config.FONT_FAMILY),
@@ -1114,7 +1051,7 @@ class ToplevelWindowConfig(CTkToplevel):
if SCROLLABLE_DROPDOWN:
self.scrollableDropdown_input_speaker_device = CTkScrollableDropdown(
self.optionmenu_input_speaker_device,
values=[device["name"] for device in get_output_device_list()],
values=model.getListOutputDevice(),
justify="left",
button_color="transparent",
command=self.optionmenu_input_speaker_device_callback,
@@ -1173,11 +1110,11 @@ class ToplevelWindowConfig(CTkToplevel):
self.slider_input_speaker_energy_threshold = CTkSlider(
self.tabview_config.tab(config_tab_title_transcription),
from_=0,
to=self.MAX_SPEAKER_ENERGY_THRESHOLD,
to=config.MAX_SPEAKER_ENERGY_THRESHOLD,
border_width=7,
button_length=0,
button_corner_radius=3,
number_of_steps=self.MAX_SPEAKER_ENERGY_THRESHOLD,
number_of_steps=config.MAX_SPEAKER_ENERGY_THRESHOLD,
command=self.slider_input_speaker_energy_threshold_callback,
variable=IntVar(value=config.INPUT_SPEAKER_ENERGY_THRESHOLD),
)