diff --git a/.gitignore b/.gitignore index ebe1be37..39debb4c 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,8 @@ pyinstaller/ VRCT.build/ VRCT.dist/ VRCT.onefile-build/ -VRCT.exe \ No newline at end of file +VRCT.exe +VRCT.spec +deepl-translate/ +translators/ +test/ \ No newline at end of file diff --git a/REDME.txt b/REDME.txt index 50abae82..c13a7629 100644 --- a/REDME.txt +++ b/REDME.txt @@ -11,7 +11,7 @@ VRChatで使用されるChatBoxをOSC経由でメッセージを送信するツ   0. VRChatのOSCを有効にする(重要)   (任意) - 1. DeepLのAPIを使用するためにアカウント登録し、認証キーを取得する +  1. DeepLのAPIを使用するためにアカウント登録し、認証キーを取得する   2. ギアアイコンのボタンでconfigウィンドウを開く   3. ParameterタブのDeepL Auth Keyに認証キーを記載し、フロッピーアイコンのボタンを押す   4. configウィンドウを閉じる @@ -22,16 +22,34 @@ VRChatで使用されるChatBoxをOSC経由でメッセージを送信するツ  その他の設定   - translation チェックボックス: 翻訳の有効無効 +  - voice2chatbox チェックボックス : マイクの音声を文字起こししてチャットボックスに送信する +  - speaker2log チェックボックス : スピーカーの音声から文字起こししてログに表示する   - foreground チェックボックス: 最前面表示の有効無効 -  - Configウィンドウ -   - GUIタブ -    - Select translator: 翻訳エンジンの変更 -    - Select Language: 翻訳する言語[source, target]を選択 +   - テキストボックス +    - logタブ: すべてのログを表示 +    - sendタブ: 送信したメッセージを表示 +    - receiveタブ: 受信したメッセージを表示 +    - systemタブ: 機能についてのメッセージを表示 + +  - configウィンドウ +   - UIタブ     - Transparency: ウィンドウの透過度の調整     - Appearance Theme: ウィンドウテーマを選択     - UI Scaling: UIサイズを調整     - Font Family: 表示フォントを選択 +   - Translationタブ +    - Select Translator: 翻訳エンジンの変更 +    - Send Language: 送信するメッセージに対して翻訳する言語[source, target]を選択 +    - Receive Language: 受信したメッセージに対して翻訳する言語[source, target]を選択 +   - Transcriptionタブ +    - Input Mic Device: 音声を入力するマイクを選択 +    - Input Mic Voice Language: 入力する音声の言語 +    - Input Mic IsDynamic: マイクの自動調整 +    - Input Mic Threshold: 音声取得のしきい値 +    - Input Speaker Device: 音声を受信するスピーカーを選択 +    - Input Speaker Voice Language: 受信する音声の言語 +    - Input Speaker Interval: 受信する音声の調整   - Parameterタブ     - OSC IP address: 変更不要     - OSC port: 変更不要 @@ -64,4 +82,10 @@ https://twitter.com/misya_ai - フォントの変更機能を追加 [2023-06-06: v0.4b] - 翻訳エンジンを追加 -- 入力と出力の翻訳言語を選択できるように変更 \ No newline at end of file +- 入力と出力の翻訳言語を選択できるように変更 +[2023-06-20: v1.0] +- マイクからの音声の文字起こし機能を追加 +- スピーカーからの音声の文字起こし機能を追加 + +# 注意事項 +再配布とかはやめてね \ No newline at end of file diff --git a/VRCT.py b/VRCT.py index 1483e25a..c138dcde 100644 --- a/VRCT.py +++ b/VRCT.py @@ -1,529 +1,16 @@ import os import json -import deepl -import deepl_translate -import translators as ts -from pythonosc import osc_message_builder -from pythonosc import udp_client -import tkinter as tk +import queue import customtkinter from PIL import Image +import pyaudiowpatch as pyaudio -def save_json(path, key, value): - with open(path, "r") as fp: - json_data = json.load(fp) - json_data[key] = value - with open(path, "w") as fp: - json.dump(json_data, fp, indent=4) - -# Translator -class Translator(): - def __init__(self): - self.translator_status = { - "DeepL(web)": False, - "DeepL(auth)": False, - "Google(web)": False, - "Bing(web)": False, - } - self.languages = {} - self.languages["DeepL(web)"] = [ - "JA","EN","BG","ZH","CS","DA","NL","ET","FI","FR","DE","EL","HU","IT", - "LV","LT","PL","PT","RO","RU","SK","SL","ES","SV", - ] - self.languages["DeepL(auth)"] = [ - "JA","EN-US","EN-GB","BG","CS","DA","DE","EL","ES","ET","FI","FR","HU", - "ID","IT","KO","LT","LV","NB","NL","PL","PT","PT-BR","PT-PT","RO","RU", - "SK","SL","SV","TR","UK","ZH", - ] - self.languages["Google(web)"] = [ - "ja","en","zh","ar","ru","fr","de","es","pt","it","ko","el","nl","hi", - "tr","ms","th","vi","id","he","pl","mn","cs","hu","et","bg","da","fi", - "ro","sv","sl","fa","bs","sr","tl","ht","ca","hr","lv","lt","ur","uk", - "cy","sw","sm","sk","af","no","bn","mg","mt","gu","ta","te","pa","am", - "az","be","ceb","eo","eu","ga" - ] - self.languages["Bing(web)"] = [ - "ja","en","zh","ar","ru","fr","de","es","pt","it","ko","el","nl","hi", - "tr","ms","th","vi","id","he","pl","cs","hu","et","bg","da","fi","ro", - "sv","sl","fa","bs","sr","fj","tl","ht","ca","hr","lv","lt","ur","uk", - "cy","ty","to","sw","sm","sk","af","no","bn","mg","mt","otq","tlh","gu", - "ta","te","pa","ga" - ] - self.deepl_client = None - - def authentication(self, translator_name, authkey=None): - result = False - try: - if translator_name == "DeepL(web)": - self.translator_status["DeepL(web)"] = True - result = True - elif translator_name == "DeepL(auth)": - self.deepl_client = deepl.Translator(authkey) - self.deepl_client.translate_text(" ", target_lang="EN-US") - self.translator_status["DeepL(auth)"] = True - result = True - elif translator_name == "Google(web)": - self.translator_status["Google(web)"] = True - result = True - elif translator_name == "Bing(web)": - self.translator_status["Bing(web)"] = True - result = True - except: - pass - return result - - def translate(self, translator_name, source_language, target_language, message): - result = False - try: - if translator_name == "DeepL(web)": - result = deepl_translate.translate(source_language=source_language, target_language=target_language, text=message) - elif translator_name == "DeepL(auth)": - result = self.deepl_client.translate_text(message, source_lang=source_language, target_lang=target_language).text - elif translator_name == "Google(web)": - result = ts.translate_text(query_text=message, translator="google", from_language=source_language, to_language=target_language) - elif translator_name == "Bing(web)": - result = ts.translate_text(query_text=message, translator="bing", from_language=source_language, to_language=target_language) - except: - pass - return result - -class ToplevelWindowInformation(customtkinter.CTkToplevel): - def __init__(self, parent, *args, **kwargs): - super().__init__(parent, *args, **kwargs) - self.parent = parent - self.grid_columnconfigure(0, weight=1) - self.grid_rowconfigure(0, weight=1) - # self.geometry(f"{500}x{300}") - self.minsize(500, 300) - - self.after(200, lambda: self.iconbitmap(os.path.join(os.path.dirname(__file__), "img", "app.ico"))) - self.title("Information") - # create textbox information - self.textbox_information = customtkinter.CTkTextbox( - self, - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.textbox_information.grid(row=0, column=0, padx=(10, 10), pady=(10, 10), sticky="nsew") - textbox_information_message = """VRCT(v0.4b) - -# 概要 -VRChatで使用されるChatBoxをOSC経由でメッセージを送信するツールになります。 -翻訳エンジンを使用してメッセージとその翻訳部分を同時に送信することができます。 -(翻訳エンジンはDeepL,Google,Bingに対応) - -# 使用方法 - 初期設定時 - 0. VRChatのOSCを有効にする(重要) - - (任意) - 1. DeepLのAPIを使用するためにアカウント登録し、認証キーを取得する - 2. ギアアイコンのボタンでconfigウィンドウを開く - 3. ParameterタブのDeepL Auth Keyに認証キーを記載し、フロッピーアイコンのボタンを押す - 4. configウィンドウを閉じる - - 通常使用時 - 1. メッセージボックスにメッセージを記入 - 2. Enterキーを押し、メッセージを送信する - -# その他の設定 - translation チェックボックス: 翻訳の有効無効 - foreground チェックボックス: 最前面表示の有効無効 - - configウィンドウ - GUIタブ - Select translator: 翻訳エンジンの変更 - Select Language: 翻訳する言語[source, target]を選択 - Transparency: ウィンドウの透過度の調整 - Appearance Theme: ウィンドウテーマを選択 - UI Scaling: UIサイズを調整 - Font Family: 表示フォントを選択 - Parameterタブ - OSC IP address: 変更不要 - OSC port: 変更不要 - DeepL Auth key: DeepLの認証キーの設定 - Message Format: 送信するメッセージのデコレーションの設定 - [message]がメッセージボックスに記入したメッセージに置換される - [translation]が翻訳されたメッセージに置換される - 初期フォーマット:"[message]([translation])" - - 設定の初期化 - config.jsonを削除 - -# お問い合わせ -要望などはTwitterまで -https://twitter.com/misya_ai - -# アップデート履歴 -[2023-05-29: v0.1b] v0.1b リリース -[2023-05-30: v0.2b] -- configボタンをギアアイコンに変更 -- 詳細情報のボタンを追加 -- 翻訳機能有効無効のチェックボックスを追加 -- 最前面表示の有効無効のチェックボックスを追加 -- いくつかのバグを修正 -[2023-06-03: v0.3b] -- 全体的にUIを刷新 -- 透過機能を追加 -- テーマのLight/Dark/Systemのモードの変更機能を追加 -- UIのスケール変更機能を追加 -- フォントの変更機能を追加 -[2023-06-06: v0.4b] -- 翻訳エンジンを追加 -- 入力と出力の翻訳言語を選択できるように変更 - -# 注意事項 -再配布とかはやめてね -""" - - self.textbox_information.insert("0.0", textbox_information_message) - self.textbox_information.configure(state='disabled') - -class ToplevelWindowConfig(customtkinter.CTkToplevel): - def __init__(self, parent, *args, **kwargs): - super().__init__(parent, *args, **kwargs) - self.parent = parent - # self.geometry(f"{350}x{270}") - # self.resizable(False, False) - self.grid_columnconfigure(0, weight=1) - self.grid_rowconfigure(0, weight=1) - - self.after(200, lambda: self.iconbitmap(os.path.join(os.path.dirname(__file__), "img", "app.ico"))) - self.title("Config") - - # tabwiew config - self.tabview_config = customtkinter.CTkTabview(self) - self.tabview_config.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") - self.tabview_config.add("GUI") - self.tabview_config.add("Parameter") - self.tabview_config.tab("GUI").grid_columnconfigure(2, weight=1) - self.tabview_config.tab("Parameter").grid_columnconfigure(1, weight=1) - self.tabview_config._segmented_button.configure(font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY)) - - # optionmenu translator - self.label_translator = customtkinter.CTkLabel( - self.tabview_config.tab("GUI"), - text="Select Translator:", - fg_color="transparent", - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_translator.grid(row=0, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") - self.optionmenu_translator = customtkinter.CTkOptionMenu( - self.tabview_config.tab("GUI"), - values=list(self.parent.translator.translator_status.keys()), - command=self.optionmenu_translator_callback, - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY), - variable=customtkinter.StringVar(value=self.parent.CHOICE_TRANSLATOR) - ) - self.optionmenu_translator.grid(row=0, column=1, columnspan=3 ,padx=5, pady=5, sticky="nsew") - - # optionmenu language - self.label_language = customtkinter.CTkLabel( - self.tabview_config.tab("GUI"), - text="Select Language:", - fg_color="transparent", - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_language.grid(row=1, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") - - ## select source language - self.optionmenu_source_language = customtkinter.CTkOptionMenu( - self.tabview_config.tab("GUI"), - command=self.optionmenu_source_language_callback, - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY), - values=self.parent.translator.languages[self.parent.CHOICE_TRANSLATOR], - variable=customtkinter.StringVar(value=self.parent.SOURCE_LANG), - ) - self.optionmenu_source_language.grid(row=1, column=1, columnspan=1, padx=5, pady=5, sticky="nsew") - - ## label --> - self.label_arrow = customtkinter.CTkLabel( - self.tabview_config.tab("GUI"), - text="-->", - fg_color="transparent", - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_arrow.grid(row=1, column=2, columnspan=1, padx=5, pady=5, sticky="nsew") - - ## select target language - self.optionmenu_target_language = customtkinter.CTkOptionMenu( - self.tabview_config.tab("GUI"), - command=self.optionmenu_target_language_callback, - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY), - values=self.parent.translator.languages[self.parent.CHOICE_TRANSLATOR], - variable=customtkinter.StringVar(value=self.parent.TARGET_LANG), - ) - self.optionmenu_target_language.grid(row=1, column=3, columnspan=1, padx=5, pady=5, sticky="nsew") - - # slider transparency - self.label_transparency = customtkinter.CTkLabel( - self.tabview_config.tab("GUI"), - text="Transparency:", - fg_color="transparent", - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_transparency.grid(row=2, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") - self.slider_transparency = customtkinter.CTkSlider( - self.tabview_config.tab("GUI"), - from_=50, - to=100, - command=self.slider_transparency_callback, - variable=tk.DoubleVar(value=self.parent.TRANSPARENCY), - ) - self.slider_transparency.grid(row=2, column=1, columnspan=3, padx=5, pady=10, sticky="nsew") - - # optionmenu theme - self.label_appearance_theme = customtkinter.CTkLabel( - self.tabview_config.tab("GUI"), - text="Appearance Theme:", - fg_color="transparent", - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_appearance_theme.grid(row=3, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") - self.optionmenu_appearance_theme = customtkinter.CTkOptionMenu( - self.tabview_config.tab("GUI"), - values=["Light", "Dark", "System"], - command=self.optionmenu_theme_callback, - variable=customtkinter.StringVar(value=self.parent.APPEARANCE_THEME) - ) - self.optionmenu_appearance_theme.grid(row=3, column=1, columnspan=3, padx=5, pady=5, sticky="nsew") - - # optionmenu UI scaling - self.label_ui_scaling = customtkinter.CTkLabel( - self.tabview_config.tab("GUI"), - text="UI Scaling:", - fg_color="transparent", - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_ui_scaling.grid(row=4, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") - self.optionmenu_ui_scaling = customtkinter.CTkOptionMenu( - self.tabview_config.tab("GUI"), - values=["80%", "90%", "100%", "110%", "120%"], - command=self.optionmenu_ui_scaling_callback, - variable=customtkinter.StringVar(value=self.parent.UI_SCALING) - ) - self.optionmenu_ui_scaling.grid(row=4, column=1, columnspan=3, padx=5, pady=5, sticky="nsew") - - # optionmenu font family - self.label_font_family = customtkinter.CTkLabel( - self.tabview_config.tab("GUI"), - text="Font Family:", - fg_color="transparent", - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_font_family.grid(row=5, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") - font_families = list(tk.font.families()) - self.optionmenu_font_family = customtkinter.CTkOptionMenu( - self.tabview_config.tab("GUI"), - values=font_families, - command=self.optionmenu_font_family_callback, - variable=customtkinter.StringVar(value=self.parent.FONT_FAMILY) - ) - self.optionmenu_font_family.grid(row=5, column=1, columnspan=3, padx=5, pady=5, sticky="nsew") - - # entry ip address - self.label_ip_address = customtkinter.CTkLabel( - self.tabview_config.tab("Parameter"), - text="OSC IP address:", - fg_color="transparent", - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_ip_address.grid(row=0, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") - self.entry_ip_address = customtkinter.CTkEntry( - self.tabview_config.tab("Parameter"), - textvariable=customtkinter.StringVar(value=self.parent.OSC_IP_ADDRESS), - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.entry_ip_address.grid(row=0, column=1, columnspan=1, padx=1, pady=5, sticky="nsew") - self.button_ip_address = customtkinter.CTkButton( - self.tabview_config.tab("Parameter"), - text="", - width=1, - command=self.update_ip_address, - image=customtkinter.CTkImage(Image.open(os.path.join(os.path.dirname(__file__), "img", "save-icon.png"))) - ) - self.button_ip_address.grid(row=0, column=2, columnspan=1, padx=5, pady=5, sticky="nsew") - - # entry port - self.label_port = customtkinter.CTkLabel( - self.tabview_config.tab("Parameter"), - text="OSC Port:", - fg_color="transparent", - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_port.grid(row=1, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") - self.entry_port = customtkinter.CTkEntry( - self.tabview_config.tab("Parameter"), - textvariable=customtkinter.StringVar(value=self.parent.OSC_PORT), - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.entry_port.grid(row=1, column=1, columnspan=1, padx=1, pady=5, sticky="nsew") - self.button_port = customtkinter.CTkButton( - self.tabview_config.tab("Parameter"), - text="", - width=1, - command=self.update_port, - image=customtkinter.CTkImage(Image.open(os.path.join(os.path.dirname(__file__), "img", "save-icon.png"))) - ) - self.button_port.grid(row=1, column=2, columnspan=1, padx=5, pady=5, sticky="nsew") - - # entry authkey - self.label_authkey = customtkinter.CTkLabel( - self.tabview_config.tab("Parameter"), - text="DeepL Auth Key:", - fg_color="transparent", - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_authkey.grid(row=2, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") - self.entry_authkey = customtkinter.CTkEntry( - self.tabview_config.tab("Parameter"), - textvariable=customtkinter.StringVar(value=self.parent.AUTH_KEYS["DeepL(auth)"]), - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.entry_authkey.grid(row=2, column=1, columnspan=1, padx=1, pady=5, sticky="nsew") - self.button_authkey = customtkinter.CTkButton( - self.tabview_config.tab("Parameter"), - text="", - width=1, - command=self.update_authkey, - image=customtkinter.CTkImage(Image.open(os.path.join(os.path.dirname(__file__), "img", "save-icon.png"))) - ) - self.button_authkey.grid(row=2, column=2, columnspan=1, padx=5, pady=5, sticky="nsew") - - # entry message format - self.label_message_format = customtkinter.CTkLabel( - self.tabview_config.tab("Parameter"), - text="Message Format:", - fg_color="transparent", - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.label_message_format.grid(row=3, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") - self.entry_message_format = customtkinter.CTkEntry( - self.tabview_config.tab("Parameter"), - textvariable=customtkinter.StringVar(value=self.parent.MESSAGE_FORMAT), - font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) - ) - self.entry_message_format.grid(row=3, column=1, columnspan=1, padx=1, pady=5, sticky="nsew") - self.button_message_format = customtkinter.CTkButton( - self.tabview_config.tab("Parameter"), - text="", - width=1, - command=self.update_message_format, - image=customtkinter.CTkImage(Image.open(os.path.join(os.path.dirname(__file__), "img", "save-icon.png"))) - ) - self.button_message_format.grid(row=3, column=2, columnspan=1, padx=5, pady=5, sticky="nsew") - - def update_ip_address(self): - value = self.entry_ip_address.get() - if len(value) > 0: - self.parent.OSC_IP_ADDRESS = value - save_json(self.parent.PATH_CONFIG, "OSC_IP_ADDRESS", self.parent.OSC_IP_ADDRESS) - - def update_port(self): - value = self.entry_port.get() - if len(value) > 0: - self.parent.OSC_PORT = value - save_json(self.parent.PATH_CONFIG, "OSC_PORT", self.parent.OSC_PORT) - - def update_authkey(self): - value = self.entry_authkey.get() - if len(value) > 0: - self.parent.textbox_message_log.configure(state='normal') - self.parent.textbox_message_log.delete("0.0", "end") - self.parent.textbox_message_log.configure(state='disabled') - - if self.parent.translator.authentication(self.parent.CHOICE_TRANSLATOR, self.parent.AUTH_KEYS[self.parent.CHOICE_TRANSLATOR]) is True: - self.parent.AUTH_KEYS["DeepL(auth)"] = value - save_json(self.parent.PATH_CONFIG, "AUTH_KEYS", self.parent.AUTH_KEYS) - else: - self.parent.textbox_message_log.configure(state='normal') - self.parent.textbox_message_log.insert("0.0", f"Auth Keyを設定してないか間違っています\n") - self.parent.textbox_message_log.configure(state='disabled') - - def update_message_format(self): - value = self.entry_message_format.get() - if len(value) > 0: - self.parent.MESSAGE_FORMAT = value - save_json(self.parent.PATH_CONFIG, "MESSAGE_FORMAT", self.parent.MESSAGE_FORMAT) - - def slider_transparency_callback(self, value): - self.parent.wm_attributes("-alpha", value/100) - self.parent.TRANSPARENCY = value - save_json(self.parent.PATH_CONFIG, "TRANSPARENCY", self.parent.TRANSPARENCY) - - def optionmenu_translator_callback(self, choice): - if self.parent.translator.authentication(choice, self.parent.AUTH_KEYS[choice]) is False: - self.parent.textbox_message_log.configure(state='normal') - self.parent.textbox_message_log.insert("0.0", f"Auth Keyを設定してないか間違っています\n") - self.parent.textbox_message_log.configure(state='disabled') - else: - self.optionmenu_source_language.configure( - values=self.parent.translator.languages[choice], - variable=customtkinter.StringVar(value=self.parent.translator.languages[choice][0])) - self.optionmenu_target_language.configure( - values=self.parent.translator.languages[choice], - variable=customtkinter.StringVar(value=self.parent.translator.languages[choice][1])) - - self.parent.CHOICE_TRANSLATOR = choice - self.parent.SOURCE_LANG = self.parent.translator.languages[choice][0] - self.parent.TARGET_LANG = self.parent.translator.languages[choice][1] - save_json(self.parent.PATH_CONFIG, "CHOICE_TRANSLATOR", self.parent.CHOICE_TRANSLATOR) - save_json(self.parent.PATH_CONFIG, "SOURCE_LANG", self.parent.SOURCE_LANG) - save_json(self.parent.PATH_CONFIG, "TARGET_LANG", self.parent.TARGET_LANG) - - def optionmenu_source_language_callback(self, choice): - self.parent.SOURCE_LANG = choice - save_json(self.parent.PATH_CONFIG, "SOURCE_LANG", self.parent.SOURCE_LANG) - - def optionmenu_target_language_callback(self, choice): - self.parent.TARGET_LANG = choice - save_json(self.parent.PATH_CONFIG, "TARGET_LANG", self.parent.TARGET_LANG) - - def optionmenu_theme_callback(self, choice): - customtkinter.set_appearance_mode(choice) - - self.parent.APPEARANCE_THEME = choice - save_json(self.parent.PATH_CONFIG, "APPEARANCE_THEME", self.parent.APPEARANCE_THEME) - - def optionmenu_ui_scaling_callback(self, choice): - new_scaling_float = int(choice.replace("%", "")) / 100 - customtkinter.set_widget_scaling(new_scaling_float) - self.parent.UI_SCALING = choice - save_json(self.parent.PATH_CONFIG, "UI_SCALING", self.parent.UI_SCALING) - - def optionmenu_font_family_callback(self, choice): - self.parent.checkbox_translation.configure(font=customtkinter.CTkFont(family=choice)) - self.parent.checkbox_foreground.configure(font=customtkinter.CTkFont(family=choice)) - self.parent.textbox_message_log.configure(font=customtkinter.CTkFont(family=choice)) - self.parent.entry_message_box.configure(font=customtkinter.CTkFont(family=choice)) - try: - self.parent.information_window.textbox_information.configure(font=customtkinter.CTkFont(family=choice)) - except: - pass - self.tabview_config._segmented_button.configure(font=customtkinter.CTkFont(family=choice)) - self.label_translator.configure(font=customtkinter.CTkFont(family=choice)) - self.optionmenu_translator.configure(font=customtkinter.CTkFont(family=choice)) - self.label_language.configure(font=customtkinter.CTkFont(family=choice)) - self.optionmenu_source_language.configure(font=customtkinter.CTkFont(family=choice)) - self.label_arrow.configure(font=customtkinter.CTkFont(family=choice)) - self.optionmenu_target_language.configure(font=customtkinter.CTkFont(family=choice)) - self.label_transparency.configure(font=customtkinter.CTkFont(family=choice)) - self.label_appearance_theme.configure(font=customtkinter.CTkFont(family=choice)) - self.optionmenu_appearance_theme.configure(font=customtkinter.CTkFont(family=choice)) - self.label_ui_scaling.configure(font=customtkinter.CTkFont(family=choice)) - self.optionmenu_ui_scaling.configure(font=customtkinter.CTkFont(family=choice)) - self.label_font_family.configure(font=customtkinter.CTkFont(family=choice)) - self.optionmenu_font_family.configure(font=customtkinter.CTkFont(family=choice)) - self.label_ip_address.configure(font=customtkinter.CTkFont(family=choice)) - self.entry_ip_address.configure(font=customtkinter.CTkFont(family=choice)) - self.label_port.configure(font=customtkinter.CTkFont(family=choice)) - self.entry_port.configure(font=customtkinter.CTkFont(family=choice)) - self.label_authkey.configure(font=customtkinter.CTkFont(family=choice)) - self.entry_authkey.configure(font=customtkinter.CTkFont(family=choice)) - self.label_message_format.configure(font=customtkinter.CTkFont(family=choice)) - self.entry_message_format.configure(font=customtkinter.CTkFont(family=choice)) - - self.parent.FONT_FAMILY = choice - save_json(self.parent.PATH_CONFIG, "FONT_FAMILY", self.parent.FONT_FAMILY) +import utils +import translation +import transcription +import osc_tools +import window_config +import window_information class App(customtkinter.CTk): def __init__(self, *args, **kwargs): @@ -531,13 +18,34 @@ class App(customtkinter.CTk): # init config self.PATH_CONFIG = "./config.json" + ## main window + self.ENABLE_TRANSLATION = False + self.ENABLE_TRANSCRIPTION_SEND = False + self.ENABLE_TRANSCRIPTION_RECEIVE = False + self.ENABLE_FOREGROUND = False + ## UI + self.TRANSPARENCY = 100 + self.APPEARANCE_THEME = "System" + self.UI_SCALING = "100%" + self.FONT_FAMILY = "Yu Gothic UI" + ## Translation + self.CHOICE_TRANSLATOR = "DeepL(web)" + self.INPUT_SOURCE_LANG = "JA" + self.INPUT_TARGET_LANG = "EN" + self.OUTPUT_SOURCE_LANG = "EN" + self.OUTPUT_TARGET_LANG = "JA" + ## Transcription + self.CHOICE_MIC_DEVICE = None + self.INPUT_MIC_VOICE_LANGUAGE = "ja-JP" + self.INPUT_MIC_IS_DYNAMIC = False + self.INPUT_MIC_THRESHOLD = 300 + self.CHOICE_SPEAKER_DEVICE = None + self.INPUT_SPEAKER_VOICE_LANGUAGE = "en-US" + self.INPUT_SPEAKER_INTERVAL = 4 + + ## Parameter self.OSC_IP_ADDRESS = "127.0.0.1" self.OSC_PORT = 9000 - self.ENABLE_TRANSLATION = True - self.CHOICE_TRANSLATOR = "DeepL(web)" - self.SOURCE_LANG = "JA" - self.TARGET_LANG = "EN" - self.ENABLE_FOREGROUND = False self.AUTH_KEYS = { "DeepL(web)": None, "DeepL(auth)": None, @@ -545,65 +53,107 @@ class App(customtkinter.CTk): "Google(web)": None, } self.MESSAGE_FORMAT = "[message]([translation])" - self.FONT_FAMILY = "Yu Gothic UI" - self.TRANSPARENCY = 100 - self.APPEARANCE_THEME = "System" - self.UI_SCALING = "100%" # load config if os.path.isfile(self.PATH_CONFIG) is not False: with open(self.PATH_CONFIG, 'r') as fp: config = json.load(fp) - if "OSC_IP_ADDRESS" in config.keys(): - self.OSC_IP_ADDRESS = config["OSC_IP_ADDRESS"] - if "OSC_PORT" in config.keys(): - self.OSC_PORT = config["OSC_PORT"] + # main window if "ENABLE_TRANSLATION" in config.keys(): self.ENABLE_TRANSLATION = config["ENABLE_TRANSLATION"] - if "CHOICE_TRANSLATOR" in config.keys(): - self.CHOICE_TRANSLATOR = config["CHOICE_TRANSLATOR"] - if "SOURCE_LANG" in config.keys(): - self.SOURCE_LANG = config["SOURCE_LANG"] - if "TARGET_LANG" in config.keys(): - self.TARGET_LANG = config["TARGET_LANG"] + if "ENABLE_TRANSCRIPTION_SEND" in config.keys(): + self.ENABLE_TRANSCRIPTION_SEND = config["ENABLE_TRANSCRIPTION_SEND"] + if "ENABLE_TRANSCRIPTION_RECEIVE" in config.keys(): + self.ENABLE_TRANSCRIPTION_RECEIVE = config["ENABLE_TRANSCRIPTION_RECEIVE"] if "ENABLE_FOREGROUND" in config.keys(): self.ENABLE_FOREGROUND = config["ENABLE_FOREGROUND"] - if "AUTH_KEYS" in config.keys(): - self.AUTH_KEYS = config["AUTH_KEYS"] - if "MESSAGE_FORMAT" in config.keys(): - self.MESSAGE_FORMAT = config["MESSAGE_FORMAT"] - if "FONT_FAMILY" in config.keys(): - self.FONT_FAMILY = config["FONT_FAMILY"] + + # tab ui if "TRANSPARENCY" in config.keys(): self.TRANSPARENCY = config["TRANSPARENCY"] if "APPEARANCE_THEME" in config.keys(): self.APPEARANCE_THEME = config["APPEARANCE_THEME"] if "UI_SCALING" in config.keys(): self.UI_SCALING = config["UI_SCALING"] + if "FONT_FAMILY" in config.keys(): + self.FONT_FAMILY = config["FONT_FAMILY"] + + # translation + if "CHOICE_TRANSLATOR" in config.keys(): + self.CHOICE_TRANSLATOR = config["CHOICE_TRANSLATOR"] + if "INPUT_SOURCE_LANG" in config.keys(): + self.INPUT_SOURCE_LANG = config["INPUT_SOURCE_LANG"] + if "INPUT_TARGET_LANG" in config.keys(): + self.INPUT_TARGET_LANG = config["INPUT_TARGET_LANG"] + if "OUTPUT_SOURCE_LANG" in config.keys(): + self.OUTPUT_SOURCE_LANG = config["OUTPUT_SOURCE_LANG"] + if "OUTPUT_TARGET_LANG" in config.keys(): + self.OUTPUT_TARGET_LANG = config["OUTPUT_TARGET_LANG"] + + # Transcription + if "CHOICE_MIC_DEVICE" in config.keys(): + self.CHOICE_MIC_DEVICE = config["CHOICE_MIC_DEVICE"] + if "INPUT_MIC_VOICE_LANGUAGE" in config.keys(): + self.INPUT_MIC_VOICE_LANGUAGE = config["INPUT_MIC_VOICE_LANGUAGE"] + if "INPUT_MIC_IS_DYNAMIC" in config.keys(): + self.INPUT_MIC_IS_DYNAMIC = config["INPUT_MIC_IS_DYNAMIC"] + if "INPUT_MIC_THRESHOLD" in config.keys(): + self.INPUT_MIC_THRESHOLD = config["INPUT_MIC_THRESHOLD"] + if "CHOICE_SPEAKER_DEVICE" in config.keys(): + self.CHOICE_SPEAKER_DEVICE = config["CHOICE_SPEAKER_DEVICE"] + if "INPUT_SPEAKER_VOICE_LANGUAGE" in config.keys(): + self.INPUT_SPEAKER_VOICE_LANGUAGE = config["INPUT_SPEAKER_VOICE_LANGUAGE"] + if "INPUT_SPEAKER_INTERVAL" in config.keys(): + self.INPUT_SPEAKER_INTERVAL = config["INPUT_SPEAKER_INTERVAL"] + + # Parameter + if "OSC_IP_ADDRESS" in config.keys(): + self.OSC_IP_ADDRESS = config["OSC_IP_ADDRESS"] + if "OSC_PORT" in config.keys(): + self.OSC_PORT = config["OSC_PORT"] + if "AUTH_KEYS" in config.keys(): + self.AUTH_KEYS = config["AUTH_KEYS"] + if "MESSAGE_FORMAT" in config.keys(): + self.MESSAGE_FORMAT = config["MESSAGE_FORMAT"] with open(self.PATH_CONFIG, 'w') as fp: config = { - "OSC_IP_ADDRESS": self.OSC_IP_ADDRESS, - "OSC_PORT": self.OSC_PORT, "ENABLE_TRANSLATION": self.ENABLE_TRANSLATION, - "CHOICE_TRANSLATOR": self.CHOICE_TRANSLATOR, - "SOURCE_LANG": self.SOURCE_LANG, - "TARGET_LANG": self.TARGET_LANG, + "ENABLE_TRANSCRIPTION_SEND": self.ENABLE_TRANSCRIPTION_SEND, + "ENABLE_TRANSCRIPTION_RECEIVE": self.ENABLE_TRANSCRIPTION_RECEIVE, "ENABLE_FOREGROUND": self.ENABLE_FOREGROUND, - "AUTH_KEYS": self.AUTH_KEYS, - "MESSAGE_FORMAT": self.MESSAGE_FORMAT, - "FONT_FAMILY": self.FONT_FAMILY, "TRANSPARENCY": self.TRANSPARENCY, "APPEARANCE_THEME": self.APPEARANCE_THEME, "UI_SCALING": self.UI_SCALING, + "FONT_FAMILY": self.FONT_FAMILY, + "CHOICE_TRANSLATOR": self.CHOICE_TRANSLATOR, + "INPUT_SOURCE_LANG": self.INPUT_SOURCE_LANG, + "INPUT_TARGET_LANG": self.INPUT_TARGET_LANG, + "OUTPUT_SOURCE_LANG": self.OUTPUT_SOURCE_LANG, + "OUTPUT_TARGET_LANG": self.OUTPUT_TARGET_LANG, + "CHOICE_MIC_DEVICE": self.CHOICE_MIC_DEVICE, + "INPUT_MIC_VOICE_LANGUAGE": self.INPUT_MIC_VOICE_LANGUAGE, + "INPUT_MIC_IS_DYNAMIC": self.INPUT_MIC_IS_DYNAMIC, + "INPUT_MIC_THRESHOLD": self.INPUT_MIC_THRESHOLD, + "CHOICE_SPEAKER_DEVICE": self.CHOICE_SPEAKER_DEVICE, + "INPUT_SPEAKER_VOICE_LANGUAGE": self.INPUT_SPEAKER_VOICE_LANGUAGE, + "INPUT_SPEAKER_INTERVAL": self.INPUT_SPEAKER_INTERVAL, + "OSC_IP_ADDRESS": self.OSC_IP_ADDRESS, + "OSC_PORT": self.OSC_PORT, + "AUTH_KEYS": self.AUTH_KEYS, + "MESSAGE_FORMAT": self.MESSAGE_FORMAT, } json.dump(config, fp, indent=4) + ## set UI theme + customtkinter.set_appearance_mode(self.APPEARANCE_THEME) + customtkinter.set_default_color_theme("blue") + # init main window self.iconbitmap(os.path.join(os.path.dirname(__file__), "img", "app.ico")) self.title("VRCT") - self.geometry(f"{400}x{110}") - self.minsize(400, 110) + self.geometry(f"{400}x{175}") + self.minsize(400, 175) self.grid_columnconfigure(1, weight=1) self.grid_rowconfigure(0, weight=1) @@ -623,6 +173,28 @@ class App(customtkinter.CTk): ) self.checkbox_translation.grid(row=0, column=0, columnspan=2 ,padx=10, pady=(5, 5), sticky="we") + # add checkbox transcription send + self.checkbox_transcription_send = customtkinter.CTkCheckBox( + self.sidebar_frame, + text="voice2chatbox", + onvalue=True, + offvalue=False, + command=self.checkbox_transcription_send_callback, + font=customtkinter.CTkFont(family=self.FONT_FAMILY) + ) + self.checkbox_transcription_send.grid(row=1, column=0, columnspan=2 ,padx=10, pady=(5, 5), sticky="we") + + # add checkbox transcription receive + self.checkbox_transcription_receive = customtkinter.CTkCheckBox( + self.sidebar_frame, + text="speaker2log", + onvalue=True, + offvalue=False, + command=self.checkbox_transcription_receive_callback, + font=customtkinter.CTkFont(family=self.FONT_FAMILY) + ) + self.checkbox_transcription_receive.grid(row=2, column=0, columnspan=2 ,padx=10, pady=(5, 5), sticky="we") + # add checkbox foreground self.checkbox_foreground = customtkinter.CTkCheckBox( self.sidebar_frame, @@ -632,7 +204,7 @@ class App(customtkinter.CTk): command=self.checkbox_foreground_callback, font=customtkinter.CTkFont(family=self.FONT_FAMILY) ) - self.checkbox_foreground.grid(row=1, column=0, columnspan=2 ,padx=10, pady=(5, 5), sticky="we") + self.checkbox_foreground.grid(row=3, column=0, columnspan=2 ,padx=10, pady=(5, 5), sticky="we") # add button information self.button_information = customtkinter.CTkButton( @@ -656,50 +228,117 @@ class App(customtkinter.CTk): self.button_config.grid(row=5, column=1, padx=(5, 10), pady=(5, 5), sticky="wse") self.config_window = None + # add tabview textbox + self.tabview_logs = customtkinter.CTkTabview(master=self) + self.tabview_logs.add("log") + self.tabview_logs.add("send") + self.tabview_logs.add("receive") + self.tabview_logs.add("system") + self.tabview_logs.grid(row=0, column=1, padx=0, pady=0, sticky="nsew") + self.tabview_logs._segmented_button.grid(sticky="W") + self.tabview_logs.tab("log").grid_rowconfigure(0, weight=1) + self.tabview_logs.tab("log").grid_columnconfigure(0, weight=1) + self.tabview_logs.tab("send").grid_rowconfigure(0, weight=1) + self.tabview_logs.tab("send").grid_columnconfigure(0, weight=1) + self.tabview_logs.tab("receive").grid_rowconfigure(0, weight=1) + self.tabview_logs.tab("receive").grid_columnconfigure(0, weight=1) + self.tabview_logs.tab("system").grid_rowconfigure(0, weight=1) + self.tabview_logs.tab("system").grid_columnconfigure(0, weight=1) + self.tabview_logs.configure(fg_color="transparent") + # add textbox message log self.textbox_message_log = customtkinter.CTkTextbox( - self, + self.tabview_logs.tab("log"), font=customtkinter.CTkFont(family=self.FONT_FAMILY) ) - self.textbox_message_log.grid(row=0, column=1, padx=(10, 10), pady=(10, 5), sticky="nsew") + self.textbox_message_log.grid(row=0, column=0, padx=0, pady=0, sticky="nsew") self.textbox_message_log.configure(state='disabled') + # add textbox message send log + self.textbox_message_send_log = customtkinter.CTkTextbox( + self.tabview_logs.tab("send"), + font=customtkinter.CTkFont(family=self.FONT_FAMILY) + ) + self.textbox_message_send_log.grid(row=0, column=0, padx=0, pady=0, sticky="nsew") + self.textbox_message_send_log.configure(state='disabled') + + # add textbox message receive log + self.textbox_message_receive_log = customtkinter.CTkTextbox( + self.tabview_logs.tab("receive"), + font=customtkinter.CTkFont(family=self.FONT_FAMILY) + ) + self.textbox_message_receive_log.grid(row=0, column=0, padx=0, pady=0, sticky="nsew") + self.textbox_message_receive_log.configure(state='disabled') + + # add textbox message system log + self.textbox_message_system_log = customtkinter.CTkTextbox( + self.tabview_logs.tab("system"), + font=customtkinter.CTkFont(family=self.FONT_FAMILY) + ) + self.textbox_message_system_log.grid(row=0, column=0, padx=0, pady=0, sticky="nsew") + self.textbox_message_system_log.configure(state='disabled') + # add entry message box self.entry_message_box = customtkinter.CTkEntry( self, placeholder_text="message", font=customtkinter.CTkFont(family=self.FONT_FAMILY) ) - self.entry_message_box.grid(row=1, column=1, columnspan=2, padx=(10, 10), pady=(5, 10), sticky="nsew") + self.entry_message_box.grid(row=1, column=1, columnspan=2, padx=5, pady=(5, 10), sticky="nsew") # set default values + ## set translator instance + self.translator = translation.Translator() + if self.translator.authentication(self.CHOICE_TRANSLATOR, self.AUTH_KEYS[self.CHOICE_TRANSLATOR]) is False: + # error update Auth key + utils.print_textbox(self.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR") + utils.print_textbox(self.textbox_message_system_log, "Auth Key or language setting is incorrect", "ERROR") + + ## set transcription instance + self.mic_queue = queue.Queue() + self.spk_queue = queue.Queue() + self.p = pyaudio.PyAudio() + self.vr = transcription.VoiceRecognizer(self.p, self.mic_queue, self.spk_queue) + self.CHOICE_MIC_DEVICE = self.CHOICE_MIC_DEVICE if self.CHOICE_MIC_DEVICE is not None else self.vr.search_default_device()[0] + self.CHOICE_SPEAKER_DEVICE = self.CHOICE_SPEAKER_DEVICE if self.CHOICE_SPEAKER_DEVICE is not None else self.vr.search_default_device()[1] + ## set checkbox enable translation if self.ENABLE_TRANSLATION: self.checkbox_translation.select() + self.checkbox_translation_callback() else: self.checkbox_translation.deselect() + ## set checkbox enable transcription send + self.th_vr_listen_mic = None + self.th_vr_recognize_mic = None + if self.ENABLE_TRANSCRIPTION_SEND: + self.checkbox_transcription_send.select() + self.checkbox_transcription_send_callback() + else: + self.checkbox_transcription_send.deselect() + + ## set checkbox enable transcription receive + self.th_vr_listen_spk = None + self.th_vr_recognize_spk = None + if self.ENABLE_TRANSCRIPTION_RECEIVE: + self.checkbox_transcription_receive.select() + self.checkbox_transcription_receive_callback() + else: + self.checkbox_transcription_receive.deselect() + ## set set checkbox enable foreground if self.ENABLE_FOREGROUND: self.checkbox_foreground.select() - self.attributes("-topmost", True) + self.checkbox_foreground_callback() else: self.checkbox_foreground.deselect() - self.attributes("-topmost", False) ## set bind entry message box self.entry_message_box.bind("", self.entry_message_box_press_key_enter) self.entry_message_box.bind("", self.entry_message_box_press_key_any) self.entry_message_box.bind("", self.entry_message_box_leave) - ## set translator instance - self.translator = Translator() - if self.translator.authentication(self.CHOICE_TRANSLATOR, self.AUTH_KEYS[self.CHOICE_TRANSLATOR]) is False: - # error update Auth key - self.textbox_message_log.configure(state='normal') - self.textbox_message_log.insert("0.0", f"Auth Keyを設定してないか間違っています\n") - self.textbox_message_log.configure(state='disabled') - ## set transparency for main window self.wm_attributes("-alpha", self.TRANSPARENCY/100) @@ -707,36 +346,150 @@ class App(customtkinter.CTk): new_scaling_float = int(self.UI_SCALING.replace("%", "")) / 100 customtkinter.set_widget_scaling(new_scaling_float) - ## set UI theme - customtkinter.set_appearance_mode(self.APPEARANCE_THEME) - customtkinter.set_default_color_theme("blue") + # delete window + self.protocol("WM_DELETE_WINDOW", self.delete_window) def button_config_callback(self): if self.config_window is None or not self.config_window.winfo_exists(): - self.config_window = ToplevelWindowConfig(self) + self.config_window = window_config.ToplevelWindowConfig(self) self.config_window.focus() def button_information_callback(self): if self.information_window is None or not self.information_window.winfo_exists(): - self.information_window = ToplevelWindowInformation(self) + self.information_window = window_information.ToplevelWindowInformation(self) self.information_window.focus() def checkbox_translation_callback(self): self.ENABLE_TRANSLATION = self.checkbox_translation.get() - save_json(self.PATH_CONFIG, "ENABLE_TRANSLATION", self.ENABLE_TRANSLATION) + if self.ENABLE_TRANSLATION: + utils.print_textbox(self.textbox_message_log, "Start translation", "INFO") + utils.print_textbox(self.textbox_message_system_log, "Start translation", "INFO") + else: + utils.print_textbox(self.textbox_message_log, "Stop translation", "INFO") + utils.print_textbox(self.textbox_message_system_log, "Stop translation", "INFO") + utils.save_json(self.PATH_CONFIG, "ENABLE_TRANSLATION", self.ENABLE_TRANSLATION) + + def checkbox_transcription_send_callback(self): + self.ENABLE_TRANSCRIPTION_SEND = self.checkbox_transcription_send.get() + if self.ENABLE_TRANSCRIPTION_SEND is True: + utils.print_textbox(self.textbox_message_log, "Start voice2chatbox", "INFO") + utils.print_textbox(self.textbox_message_system_log, "Start voice2chatbox", "INFO") + # start threading + self.vr.set_mic( + device_name=self.CHOICE_MIC_DEVICE, + threshold=int(self.INPUT_MIC_THRESHOLD), + is_dynamic=self.INPUT_MIC_IS_DYNAMIC, + language=self.INPUT_MIC_VOICE_LANGUAGE, + ) + self.vr.init_mic() + self.th_vr_listen_mic = utils.thread_fnc(self.vr_listen_mic) + self.th_vr_recognize_mic = utils.thread_fnc(self.vr_recognize_mic) + self.th_vr_listen_mic.start() + self.th_vr_recognize_mic.start() + else: + if isinstance(self.th_vr_listen_mic, utils.thread_fnc): + self.th_vr_listen_mic.stop() + if isinstance(self.th_vr_recognize_mic, utils.thread_fnc): + self.th_vr_recognize_mic.stop() + + utils.print_textbox(self.textbox_message_log, "Stop voice2chatbox", "INFO") + utils.print_textbox(self.textbox_message_system_log, "Stop voice2chatbox", "INFO") + utils.save_json(self.PATH_CONFIG, "ENABLE_TRANSCRIPTION_SEND", self.ENABLE_TRANSCRIPTION_SEND) + + def checkbox_transcription_receive_callback(self): + self.ENABLE_TRANSCRIPTION_RECEIVE = self.checkbox_transcription_receive.get() + if self.ENABLE_TRANSCRIPTION_RECEIVE is True: + utils.print_textbox(self.textbox_message_log, "Start speaker2log", "INFO") + utils.print_textbox(self.textbox_message_system_log, "Start speaker2log", "INFO") + + self.vr.set_spk( + device_name=self.CHOICE_SPEAKER_DEVICE, + interval=int(self.INPUT_SPEAKER_INTERVAL), + language=self.INPUT_SPEAKER_VOICE_LANGUAGE, + ) + self.vr.start_spk_recording() + self.th_vr_recognize_spk = utils.thread_fnc(self.vr_recognize_spk) + self.th_vr_recognize_spk.start() + else: + if self.vr.spk_stream is not None: + self.vr.close_spk_stream() + if isinstance(self.th_vr_recognize_spk, utils.thread_fnc): + self.th_vr_recognize_spk.stop() + + utils.print_textbox(self.textbox_message_log, "Stop speaker2log", "INFO") + utils.print_textbox(self.textbox_message_system_log, "Stop speaker2log", "INFO") + utils.save_json(self.PATH_CONFIG, "ENABLE_TRANSCRIPTION_RECEIVE", self.ENABLE_TRANSCRIPTION_RECEIVE) + + def vr_listen_mic(self): + self.vr.listen_mic() + + def vr_recognize_mic(self): + message = self.vr.recognize_mic() + if len(message) > 0: + # translate + if self.checkbox_translation.get() is False: + voice_message = f"{message}" + elif self.translator.translator_status[self.CHOICE_TRANSLATOR] is False: + utils.print_textbox(self.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR") + utils.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=self.CHOICE_TRANSLATOR, + source_language=self.INPUT_SOURCE_LANG, + target_language=self.INPUT_TARGET_LANG, + message=message + ) + voice_message = self.MESSAGE_FORMAT.replace("[message]", message).replace("[translation]", result) + # send OSC message + osc_tools.send_message(voice_message, self.OSC_IP_ADDRESS, self.OSC_PORT) + # update textbox message log + utils.print_textbox(self.textbox_message_log, f"{voice_message}", "SEND") + utils.print_textbox(self.textbox_message_send_log, f"{voice_message}", "SEND") + + def vr_listen_spk(self): + self.vr.listen_spk() + + def vr_recognize_spk(self): + message = self.vr.recognize_spk() + if len(message) > 0: + # translate + if self.checkbox_translation.get() is False: + voice_message = f"{message}" + elif self.translator.translator_status[self.CHOICE_TRANSLATOR] is False: + utils.print_textbox(self.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR") + utils.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=self.CHOICE_TRANSLATOR, + source_language=self.OUTPUT_SOURCE_LANG, + target_language=self.OUTPUT_TARGET_LANG, + message=message + ) + voice_message = self.MESSAGE_FORMAT.replace("[message]", message).replace("[translation]", result) + # send OSC message + # osc_tools.send_message(voice_message, self.OSC_IP_ADDRESS, self.OSC_PORT) + # update textbox message receive log + utils.print_textbox(self.textbox_message_log, f"{voice_message}", "RECEIVE") + utils.print_textbox(self.textbox_message_receive_log, f"{voice_message}", "RECEIVE") def checkbox_foreground_callback(self): - value = self.checkbox_foreground.get() - - if value: + self.ENABLE_FOREGROUND = self.checkbox_foreground.get() + if self.ENABLE_FOREGROUND: self.attributes("-topmost", True) + utils.print_textbox(self.textbox_message_log, "Start foreground", "INFO") + utils.print_textbox(self.textbox_message_system_log, "Start foreground", "INFO") else: self.attributes("-topmost", False) - - self.ENABLE_FOREGROUND = value - save_json(self.PATH_CONFIG, "ENABLE_FOREGROUND", self.ENABLE_FOREGROUND) + utils.print_textbox(self.textbox_message_log, "Stop foreground", "INFO") + utils.print_textbox(self.textbox_message_system_log, "Stop foreground", "INFO") + utils.save_json(self.PATH_CONFIG, "ENABLE_FOREGROUND", self.ENABLE_FOREGROUND) def entry_message_box_press_key_enter(self, event): + # send OSC typing + osc_tools.send_typing(False, self.OSC_IP_ADDRESS, self.OSC_PORT) + if self.ENABLE_FOREGROUND: self.attributes("-topmost", True) @@ -745,45 +498,49 @@ class App(customtkinter.CTk): # translate if self.checkbox_translation.get() is False: chat_message = f"{message}" - elif (self.translator.translator_status[self.CHOICE_TRANSLATOR] is False) or (self.SOURCE_LANG == "None") or (self.TARGET_LANG == "None"): - self.textbox_message_log.configure(state='normal') - self.textbox_message_log.insert("0.0", f"Auth Keyもしくは言語の設定が間違っています\n") - self.textbox_message_log.configure(state='disabled') + elif self.translator.translator_status[self.CHOICE_TRANSLATOR] is False: + utils.print_textbox(self.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR") + utils.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=self.CHOICE_TRANSLATOR, - source_language=self.SOURCE_LANG, - target_language=self.TARGET_LANG, + source_language=self.INPUT_SOURCE_LANG, + target_language=self.INPUT_TARGET_LANG, message=message ) chat_message = self.MESSAGE_FORMAT.replace("[message]", message).replace("[translation]", result) # send OSC message - message = osc_message_builder.OscMessageBuilder(address="/chatbox/input") - message.add_arg(f"{chat_message}") - message.add_arg(True) - message.add_arg(True) - message = message.build() - client = udp_client.SimpleUDPClient(self.OSC_IP_ADDRESS, self.OSC_PORT) - client.send(message) + osc_tools.send_message(chat_message, self.OSC_IP_ADDRESS, self.OSC_PORT) # update textbox message log - self.textbox_message_log.configure(state='normal') - self.textbox_message_log.insert("0.0", f"{chat_message}\n") - self.textbox_message_log.configure(state='disabled') + utils.print_textbox(self.textbox_message_log, f"{chat_message}", "SEND") + utils.print_textbox(self.textbox_message_send_log, f"{chat_message}", "SEND") # delete message in entry message box - self.entry_message_box.delete(0, customtkinter.END) + # self.entry_message_box.delete(0, customtkinter.END) def entry_message_box_press_key_any(self, event): + # send OSC typing + osc_tools.send_typing(True, self.OSC_IP_ADDRESS, self.OSC_PORT) if self.ENABLE_FOREGROUND: self.attributes("-topmost", False) def entry_message_box_leave(self, event): + # send OSC typing + osc_tools.send_typing(False, self.OSC_IP_ADDRESS, self.OSC_PORT) if self.ENABLE_FOREGROUND: self.attributes("-topmost", True) + def delete_window(self): + self.quit() + self.destroy() + if __name__ == "__main__": - app = App() - app.mainloop() \ No newline at end of file + try: + app = App() + app.mainloop() + except Exception as e: + with open("./error.log", "r") as fp: + fp.write(f"{e}") \ No newline at end of file diff --git a/osc_tools.py b/osc_tools.py new file mode 100644 index 00000000..dacda522 --- /dev/null +++ b/osc_tools.py @@ -0,0 +1,21 @@ +from pythonosc import osc_message_builder +from pythonosc import udp_client + +# send OSC message typing +def send_typing(flag=False, ip_address="127.0.0.1", port=9000): + typing = osc_message_builder.OscMessageBuilder(address="/chatbox/typing") + typing.add_arg(flag) + b_typing = typing.build() + client = udp_client.SimpleUDPClient(ip_address, port) + client.send(b_typing) + +# send OSC message +def send_message(message=None, ip_address="127.0.0.1", port=9000): + if message != None: + msg = osc_message_builder.OscMessageBuilder(address="/chatbox/input") + msg.add_arg(f"{message}") + msg.add_arg(True) + msg.add_arg(True) + b_msg = msg.build() + client = udp_client.SimpleUDPClient(ip_address, port) + client.send(b_msg) \ No newline at end of file diff --git a/transcription.py b/transcription.py new file mode 100644 index 00000000..9a746d79 --- /dev/null +++ b/transcription.py @@ -0,0 +1,177 @@ +import sounddevice as sd +import speech_recognition as sr +import pyaudiowpatch as pyaudio + +# VoiceRecognizer +class VoiceRecognizer(): + def __init__(self, p_audio, mic_queue, spk_queue): + self.r = sr.Recognizer() + self.p = p_audio + + self.languages = [ + "ja-JP","en-US","en-GB","af-ZA","ar-DZ","ar-BH","ar-EG","ar-IL","ar-IQ","ar-JO","ar-KW","ar-LB","ar-MA", + "ar-OM","ar-PS","ar-QA","ar-SA","ar-TN","ar-AE","eu-ES","bg-BG","ca-ES","cmn-Hans-CN","cmn-Hans-HK", + "cmn-Hant-TW","yue-Hant-HK","hr_HR","cs-CZ","da-DK","en-AU","en-CA","en-IN","en-IE","en-NZ","en-PH", + "en-ZA","fa-IR","fr-FR","fil-PH","gl-ES","de-DE","el-GR","fi-FI","he-IL","hi-IN","hu-HU","id-ID","is-IS", + "it-IT","it-CH","ko-KR","lt-LT","ms-MY","nl-NL","nb-NO","pl-PL","pt-BR","pt-PT","ro-RO","ru-RU","sr-RS", + "sk-SK","sl-SI","es-AR","es-BO","es-CL","es-CO","es-CR","es-DO","es-EC","es-SV","es-GT","es-HN","es-MX", + "es-NI","es-PA","es-PY","es-PE","es-PR","es-ES","es-UY","es-US","es-VE","sv-SE","th-TH","tr-TR","uk-UA", + "vi-VN","zu-ZA" + ] + self.mic_device_name = None + self.mic_threshold = 50 + self.mic_is_dynamic = False + self.mic_language = "ja-JP" + self.mic_queue = mic_queue + + self.spk_device = None + self.spk_interval = 3 + self.spk_language = "ja-JP" + self.spk_stream = None + self.spk_queue = spk_queue + + def search_input_device(self): + devices = [] + device_list = sd.query_devices() + for device in device_list: + if device["max_input_channels"] > 0: + devices.append(device) + return devices + + def search_output_device(self): + devices =[] + with pyaudio.PyAudio() as p: + wasapi_info = p.get_host_api_info_by_type(pyaudio.paWASAPI) + for host_index in range(0, p.get_host_api_count()): + for device_index in range(0, p. get_host_api_info_by_index(host_index)['deviceCount']): + device = p.get_device_info_by_host_api_device_index(host_index, device_index) + if device["hostApi"] == wasapi_info["index"] and device["isLoopbackDevice"] is True: + devices.append(device) + return devices + + def search_default_device(self): + device_list = sd.query_devices() + mic_index = sd.default.device[0] + name_mic = device_list[mic_index]["name"] + with pyaudio.PyAudio() as p: + wasapi_info = p.get_host_api_info_by_type(pyaudio.paWASAPI) + default_speakers = p.get_device_info_by_index(wasapi_info["defaultOutputDevice"]) + + if not default_speakers["isLoopbackDevice"]: + for loopback in p.get_loopback_device_info_generator(): + if default_speakers["name"] in loopback["name"]: + name_spk = loopback["name"] + break + return name_mic, name_spk + + def set_mic(self, device_name, threshold=50, is_dynamic=False, language="ja-JP"): + input_device_list = self.search_input_device() + self.mic_device_name = [device["index"] for device in input_device_list if device["name"] == device_name][0] + self.mic_threshold = threshold + self.mic_is_dynamic = is_dynamic + self.mic_language = language + + def init_mic(self): + self.r.energy_threshold = self.mic_threshold + if self.mic_is_dynamic: + with self.mic as source: + self.r.adjust_for_ambient_noise(source, 3.0) + + def listen_mic(self): + with sr.Microphone(device_index=self.mic_device_name) as source: + audio = self.r.listen(source) + self.mic_queue.put(audio) + + def recognize_mic(self): + try: + audio = self.mic_queue.get() + text = self.r.recognize_google(audio, language=self.mic_language) + except: + text = "" + return text + + def set_spk(self, device_name, interval, language): + output_device_list = self.search_output_device() + self.spk_device = [device for device in output_device_list if device["name"] == device_name][0] + self.spk_interval = interval + self.spk_language = language + + def spk_record_callback(self, in_data, frame_count, time_info, status): + self.spk_queue.put(in_data) + return (in_data, pyaudio.paContinue) + + def start_spk_recording(self): + self.close_spk_stream() + self.spk_stream = self.p.open(format=pyaudio.paInt16, + channels=self.spk_device["maxInputChannels"], + rate=int(self.spk_device["defaultSampleRate"]), + frames_per_buffer=int(self.spk_device["defaultSampleRate"])*self.spk_interval, + input=True, + input_device_index=self.spk_device["index"], + stream_callback=self.spk_record_callback + ) + + def stop_spk_stream(self): + self.spk_stream.stop_stream() + + def start_spk_stream(self): + self.spk_stream.start_stream() + + def close_spk_stream(self): + if self.spk_stream is not None: + self.spk_stream.stop_stream() + self.spk_stream.close() + self.spk_stream = None + + def recognize_spk(self): + try: + in_data = self.spk_queue.get() + audio_data = sr.AudioData(in_data, int(self.spk_device["defaultSampleRate"]), self.spk_interval) + text = self.r.recognize_google(audio_data, language=self.spk_language) + except: + text = "" + return text + +if __name__ == "__main__": + import queue + import threading + + mic_queue = queue.Queue() + spk_queue = queue.Queue() + vr = VoiceRecognizer(mic_queue, spk_queue) + + mic_name, spk_name = vr.search_default_device() + print("mic_name", mic_name) + print("spk_name", spk_name) + + ############################################################### + vr.set_mic(device_name=mic_name, threshold=300, is_dynamic=False, language="ja-JP") + vr.init_mic() + + def vr_listen_mic(): + while True: + vr.listen_mic() + + def vr_recognize_mic(): + while True: + text = vr.recognize_mic() + if len(text) > 0: + print(text) + th_vr_listen_mic = threading.Thread(target=vr_listen_mic) + th_vr_listen_mic.start() + th_vr_recognize_mic = threading.Thread(target=vr_recognize_mic) + th_vr_recognize_mic.start() + ############################################################### + + ############################################################### + vr.set_spk(device_name=spk_name, interval=4, language="ja-JP") + vr.start_spk_recording() + + def vr_recognize_spk(): + while True: + text = vr.recognize_spk() + if len(text) > 0: + print(text) + th_vr_recognize_spk = threading.Thread(target=vr_recognize_spk) + th_vr_recognize_spk.start() + ############################################################### \ No newline at end of file diff --git a/translation.py b/translation.py new file mode 100644 index 00000000..cbb96f4f --- /dev/null +++ b/translation.py @@ -0,0 +1,74 @@ +import deepl +import deepl_translate +import translators as ts + +# Translator +class Translator(): + def __init__(self): + self.translator_status = { + "DeepL(web)": False, + "DeepL(auth)": False, + "Google(web)": False, + "Bing(web)": False, + } + self.languages = {} + self.languages["DeepL(web)"] = [ + "JA","EN","BG","ZH","CS","DA","NL","ET","FI","FR","DE","EL","HU","IT", + "LV","LT","PL","PT","RO","RU","SK","SL","ES","SV", + ] + self.languages["DeepL(auth)"] = [ + "JA","EN-US","EN-GB","BG","CS","DA","DE","EL","ES","ET","FI","FR","HU", + "ID","IT","KO","LT","LV","NB","NL","PL","PT","PT-BR","PT-PT","RO","RU", + "SK","SL","SV","TR","UK","ZH", + ] + self.languages["Google(web)"] = [ + "ja","en","zh","ar","ru","fr","de","es","pt","it","ko","el","nl","hi", + "tr","ms","th","vi","id","he","pl","mn","cs","hu","et","bg","da","fi", + "ro","sv","sl","fa","bs","sr","tl","ht","ca","hr","lv","lt","ur","uk", + "cy","sw","sm","sk","af","no","bn","mg","mt","gu","ta","te","pa","am", + "az","be","ceb","eo","eu","ga" + ] + self.languages["Bing(web)"] = [ + "ja","en","zh","ar","ru","fr","de","es","pt","it","ko","el","nl","hi", + "tr","ms","th","vi","id","he","pl","cs","hu","et","bg","da","fi","ro", + "sv","sl","fa","bs","sr","fj","tl","ht","ca","hr","lv","lt","ur","uk", + "cy","ty","to","sw","sm","sk","af","no","bn","mg","mt","otq","tlh","gu", + "ta","te","pa","ga" + ] + self.deepl_client = None + + def authentication(self, translator_name, authkey=None): + result = False + try: + if translator_name == "DeepL(web)": + self.translator_status["DeepL(web)"] = True + result = True + elif translator_name == "DeepL(auth)": + self.deepl_client = deepl.Translator(authkey) + self.deepl_client.translate_text(" ", target_lang="EN-US") + self.translator_status["DeepL(auth)"] = True + result = True + elif translator_name == "Google(web)": + self.translator_status["Google(web)"] = True + result = True + elif translator_name == "Bing(web)": + self.translator_status["Bing(web)"] = True + result = True + except: + pass + return result + + def translate(self, translator_name, source_language, target_language, message): + result = False + try: + if translator_name == "DeepL(web)": + result = deepl_translate.translate(source_language=source_language, target_language=target_language, text=message) + elif translator_name == "DeepL(auth)": + result = self.deepl_client.translate_text(message, source_lang=source_language, target_lang=target_language).text + elif translator_name == "Google(web)": + result = ts.translate_text(query_text=message, translator="google", from_language=source_language, to_language=target_language) + elif translator_name == "Bing(web)": + result = ts.translate_text(query_text=message, translator="bing", from_language=source_language, to_language=target_language) + except: + pass + return result \ No newline at end of file diff --git a/utils.py b/utils.py new file mode 100644 index 00000000..234bacf7 --- /dev/null +++ b/utils.py @@ -0,0 +1,41 @@ +import json +import datetime +import threading + +def save_json(path, key, value): + with open(path, "r") as fp: + json_data = json.load(fp) + json_data[key] = value + with open(path, "w") as fp: + json.dump(json_data, fp, indent=4) + +def print_textbox(textbox, message, tags=None): + now = datetime.datetime.now() + now = now.strftime('%H:%M:%S') + + textbox.tag_config("ERROR", foreground="#FF0000") + textbox.tag_config("INFO", foreground="#1BFF00") + textbox.tag_config("SEND", foreground="#0378e2") + textbox.tag_config("RECEIVE", foreground="#ffa500") + + textbox.configure(state='normal') + textbox.insert("end", f"[{now}][") + textbox.insert("end", f"{tags}", tags) + textbox.insert("end", f"]{message}\n") + textbox.configure(state='disabled') + textbox.see("end") + +class thread_fnc(threading.Thread): + def __init__(self, fnc, daemon=True, *args, **kwargs): + super(thread_fnc, self).__init__(daemon=daemon, *args, **kwargs) + self.fnc = fnc + self._stop = threading.Event() + def stop(self): + self._stop.set() + def stopped(self): + return self._stop.isSet() + def run(self): + while True: + if self.stopped(): + return + self.fnc() \ No newline at end of file diff --git a/window_config.py b/window_config.py new file mode 100644 index 00000000..f3b75268 --- /dev/null +++ b/window_config.py @@ -0,0 +1,566 @@ +import os +import tkinter as tk +import customtkinter +import utils + +class ToplevelWindowConfig(customtkinter.CTkToplevel): + def __init__(self, parent, *args, **kwargs): + super().__init__(parent, *args, **kwargs) + self.parent = parent + # self.geometry(f"{350}x{270}") + # self.resizable(False, False) + self.grid_columnconfigure(0, weight=1) + self.grid_rowconfigure(0, weight=1) + + self.after(200, lambda: self.iconbitmap(os.path.join(os.path.dirname(__file__), "img", "app.ico"))) + self.title("Config") + + # tabwiew config + self.tabview_config = customtkinter.CTkTabview(self) + self.tabview_config.grid(row=0, column=0, padx=5, pady=5, sticky="nsew") + self.tabview_config.add("UI") + self.tabview_config.add("Translation") + self.tabview_config.add("Transcription") + self.tabview_config.add("Parameter") + self.tabview_config.tab("UI").grid_columnconfigure(1, weight=1) + self.tabview_config.tab("Translation").grid_columnconfigure([1,2,3], weight=1) + self.tabview_config.tab("Transcription").grid_columnconfigure(1, weight=1) + self.tabview_config.tab("Parameter").grid_columnconfigure(1, weight=1) + self.tabview_config._segmented_button.configure(font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY)) + self.tabview_config._segmented_button.grid(sticky="W") + + # tab UI + ## slider transparency + self.label_transparency = customtkinter.CTkLabel( + self.tabview_config.tab("UI"), + text="Transparency:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_transparency.grid(row=0, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + self.slider_transparency = customtkinter.CTkSlider( + self.tabview_config.tab("UI"), + from_=50, + to=100, + command=self.slider_transparency_callback, + variable=tk.DoubleVar(value=self.parent.TRANSPARENCY), + ) + self.slider_transparency.grid(row=0, column=1, columnspan=1, padx=5, pady=10, sticky="nsew") + + ## optionmenu theme + self.label_appearance_theme = customtkinter.CTkLabel( + self.tabview_config.tab("UI"), + text="Appearance Theme:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_appearance_theme.grid(row=1, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + self.optionmenu_appearance_theme = customtkinter.CTkOptionMenu( + self.tabview_config.tab("UI"), + values=["Light", "Dark", "System"], + command=self.optionmenu_theme_callback, + variable=customtkinter.StringVar(value=self.parent.APPEARANCE_THEME) + ) + self.optionmenu_appearance_theme.grid(row=1, column=1, columnspan=1, padx=5, pady=5, sticky="nsew") + + ## optionmenu UI scaling + self.label_ui_scaling = customtkinter.CTkLabel( + self.tabview_config.tab("UI"), + text="UI Scaling:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_ui_scaling.grid(row=2, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + self.optionmenu_ui_scaling = customtkinter.CTkOptionMenu( + self.tabview_config.tab("UI"), + values=["80%", "90%", "100%", "110%", "120%"], + command=self.optionmenu_ui_scaling_callback, + variable=customtkinter.StringVar(value=self.parent.UI_SCALING) + ) + self.optionmenu_ui_scaling.grid(row=2, column=1, columnspan=1, padx=5, pady=5, sticky="nsew") + + ## optionmenu font family + self.label_font_family = customtkinter.CTkLabel( + self.tabview_config.tab("UI"), + text="Font Family:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_font_family.grid(row=3, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + font_families = list(tk.font.families()) + self.optionmenu_font_family = customtkinter.CTkOptionMenu( + self.tabview_config.tab("UI"), + values=font_families, + command=self.optionmenu_font_family_callback, + variable=customtkinter.StringVar(value=self.parent.FONT_FAMILY) + ) + self.optionmenu_font_family.grid(row=3, column=1, columnspan=1, padx=5, pady=5, sticky="nsew") + + # tab Translation + ## optionmenu translation translator + self.label_translation_translator = customtkinter.CTkLabel( + self.tabview_config.tab("Translation"), + text="Select Translator:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_translation_translator.grid(row=0, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + self.optionmenu_translation_translator = customtkinter.CTkOptionMenu( + self.tabview_config.tab("Translation"), + values=list(self.parent.translator.translator_status.keys()), + command=self.optionmenu_translation_translator_callback, + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY), + variable=customtkinter.StringVar(value=self.parent.CHOICE_TRANSLATOR) + ) + self.optionmenu_translation_translator.grid(row=0, column=1, columnspan=3 ,padx=5, pady=5, sticky="nsew") + + ## optionmenu translation input language + self.label_translation_input_language = customtkinter.CTkLabel( + self.tabview_config.tab("Translation"), + text="Send Language:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_translation_input_language.grid(row=1, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + + ## select translation input source language + self.optionmenu_translation_input_source_language = customtkinter.CTkOptionMenu( + self.tabview_config.tab("Translation"), + command=self.optionmenu_translation_input_source_language_callback, + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY), + values=self.parent.translator.languages[self.parent.CHOICE_TRANSLATOR], + variable=customtkinter.StringVar(value=self.parent.INPUT_SOURCE_LANG), + ) + self.optionmenu_translation_input_source_language.grid(row=1, column=1, columnspan=1, padx=5, pady=5, sticky="nsew") + + ## label translation input arrow + self.label_translation_input_arrow = customtkinter.CTkLabel( + self.tabview_config.tab("Translation"), + text="-->", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_translation_input_arrow.grid(row=1, column=2, columnspan=1, padx=5, pady=5, sticky="nsew") + + ## select translation input target language + self.optionmenu_translation_input_target_language = customtkinter.CTkOptionMenu( + self.tabview_config.tab("Translation"), + command=self.optionmenu_translation_input_target_language_callback, + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY), + values=self.parent.translator.languages[self.parent.CHOICE_TRANSLATOR], + variable=customtkinter.StringVar(value=self.parent.INPUT_TARGET_LANG), + ) + self.optionmenu_translation_input_target_language.grid(row=1, column=3, columnspan=1, padx=5, pady=5, sticky="nsew") + + ## optionmenu translation output language + self.label_translation_output_language = customtkinter.CTkLabel( + self.tabview_config.tab("Translation"), + text="Receive Language:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_translation_output_language.grid(row=2, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + + ## select translation output source language + self.optionmenu_translation_output_source_language = customtkinter.CTkOptionMenu( + self.tabview_config.tab("Translation"), + command=self.optionmenu_translation_output_source_language_callback, + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY), + values=self.parent.translator.languages[self.parent.CHOICE_TRANSLATOR], + variable=customtkinter.StringVar(value=self.parent.OUTPUT_SOURCE_LANG), + ) + self.optionmenu_translation_output_source_language.grid(row=2, column=1, columnspan=1, padx=5, pady=5, sticky="nsew") + + ## label translation output arrow + self.label_translation_output_arrow = customtkinter.CTkLabel( + self.tabview_config.tab("Translation"), + text="-->", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_translation_output_arrow.grid(row=2, column=2, columnspan=1, padx=5, pady=5, sticky="nsew") + + ## select translation output target language + self.optionmenu_translation_output_target_language = customtkinter.CTkOptionMenu( + self.tabview_config.tab("Translation"), + command=self.optionmenu_translation_output_target_language_callback, + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY), + values=self.parent.translator.languages[self.parent.CHOICE_TRANSLATOR], + variable=customtkinter.StringVar(value=self.parent.OUTPUT_TARGET_LANG), + ) + self.optionmenu_translation_output_target_language.grid(row=2, column=3, columnspan=1, padx=5, pady=5, sticky="nsew") + + # tab Transcription + ## optionmenu input mic device + self.label_input_mic_device = customtkinter.CTkLabel( + self.tabview_config.tab("Transcription"), + text="Input Mic Device:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_input_mic_device.grid(row=0, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + self.optionmenu_input_mic_device = customtkinter.CTkOptionMenu( + self.tabview_config.tab("Transcription"), + values=[device["name"] for device in self.parent.vr.search_input_device()], + command=self.optionmenu_input_mic_device_callback, + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY), + variable=customtkinter.StringVar(value=self.parent.CHOICE_MIC_DEVICE) + ) + self.optionmenu_input_mic_device.grid(row=0, column=1, columnspan=1 ,padx=5, pady=5, sticky="nsew") + + ## optionmenu input mic voice language + self.label_input_mic_voice_language = customtkinter.CTkLabel( + self.tabview_config.tab("Transcription"), + text="Input Mic Voice Language:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_input_mic_voice_language.grid(row=1, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + self.optionmenu_input_mic_voice_language = customtkinter.CTkOptionMenu( + self.tabview_config.tab("Transcription"), + values=list(self.parent.vr.languages), + command=self.optionmenu_input_mic_voice_language_callback, + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY), + variable=customtkinter.StringVar(value=self.parent.INPUT_MIC_VOICE_LANGUAGE) + ) + self.optionmenu_input_mic_voice_language.grid(row=1, column=1, columnspan=1 ,padx=5, pady=5, sticky="nsew") + + ## checkbox input mic in dynamic + self.label_input_mic_is_dynamic = customtkinter.CTkLabel( + self.tabview_config.tab("Transcription"), + text="Input Mic IsDynamic:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_input_mic_is_dynamic.grid(row=2, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + self.checkbox_input_mic_is_dynamic = customtkinter.CTkCheckBox( + self.tabview_config.tab("Transcription"), + text="", + onvalue=True, + offvalue=False, + command=self.checkbox_input_mic_is_dynamic_callback, + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.checkbox_input_mic_is_dynamic.grid(row=2, column=1, columnspan=1 ,padx=5, pady=5, sticky="nsew") + if self.parent.INPUT_MIC_IS_DYNAMIC is True: + self.checkbox_input_mic_is_dynamic.select() + else: + self.checkbox_input_mic_is_dynamic.deselect() + + ## entry input mic threshold + self.label_input_mic_threshold = customtkinter.CTkLabel( + self.tabview_config.tab("Transcription"), + text="Input Mic Threshold:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_input_mic_threshold.grid(row=3, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + self.entry_input_mic_threshold = customtkinter.CTkEntry( + self.tabview_config.tab("Transcription"), + textvariable=customtkinter.StringVar(value=self.parent.INPUT_MIC_THRESHOLD), + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.entry_input_mic_threshold.grid(row=3, column=1, columnspan=1 ,padx=5, pady=10, sticky="nsew") + self.entry_input_mic_threshold.bind("", self.entry_input_mic_threshold_callback) + + ## optionmenu input speaker device + self.label_input_speaker_device = customtkinter.CTkLabel( + self.tabview_config.tab("Transcription"), + text="Input Speaker Device:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_input_speaker_device.grid(row=4, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + self.optionmenu_input_speaker_device = customtkinter.CTkOptionMenu( + self.tabview_config.tab("Transcription"), + values=[device["name"] for device in self.parent.vr.search_output_device()], + command=self.optionmenu_input_speaker_device_callback, + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY), + variable=customtkinter.StringVar(value=self.parent.CHOICE_SPEAKER_DEVICE), + ) + self.optionmenu_input_speaker_device.grid(row=4, column=1, columnspan=1 ,padx=5, pady=5, sticky="nsew") + + ## optionmenu input speaker voice language + self.label_input_speaker_voice_language = customtkinter.CTkLabel( + self.tabview_config.tab("Transcription"), + text="Input Speaker Voice Language:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_input_speaker_voice_language.grid(row=5, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + self.optionmenu_input_speaker_voice_language = customtkinter.CTkOptionMenu( + self.tabview_config.tab("Transcription"), + values=list(self.parent.vr.languages), + command=self.optionmenu_input_speaker_voice_language_callback, + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY), + variable=customtkinter.StringVar(value=self.parent.INPUT_SPEAKER_VOICE_LANGUAGE), + ) + self.optionmenu_input_speaker_voice_language.grid(row=5, column=1, columnspan=1 ,padx=5, pady=5, sticky="nsew") + + ## entry input speaker interval + self.label_input_speaker_interval = customtkinter.CTkLabel( + self.tabview_config.tab("Transcription"), + text="Input Speaker Interval:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_input_speaker_interval.grid(row=6, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + self.entry_input_speaker_interval = customtkinter.CTkEntry( + self.tabview_config.tab("Transcription"), + textvariable=customtkinter.StringVar(value=self.parent.INPUT_SPEAKER_INTERVAL), + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.entry_input_speaker_interval.grid(row=6, column=1, columnspan=1 ,padx=5, pady=5, sticky="nsew") + self.entry_input_speaker_interval.bind("", self.entry_input_speaker_interval_callback) + + # tab Parameter + ## entry ip address + self.label_ip_address = customtkinter.CTkLabel( + self.tabview_config.tab("Parameter"), + text="OSC IP address:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_ip_address.grid(row=0, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + self.entry_ip_address = customtkinter.CTkEntry( + self.tabview_config.tab("Parameter"), + textvariable=customtkinter.StringVar(value=self.parent.OSC_IP_ADDRESS), + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.entry_ip_address.grid(row=0, column=1, columnspan=1, padx=1, pady=5, sticky="nsew") + self.entry_ip_address.bind("", self.entry_ip_address_callback) + + ## entry port + self.label_port = customtkinter.CTkLabel( + self.tabview_config.tab("Parameter"), + text="OSC Port:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_port.grid(row=1, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + self.entry_port = customtkinter.CTkEntry( + self.tabview_config.tab("Parameter"), + textvariable=customtkinter.StringVar(value=self.parent.OSC_PORT), + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.entry_port.grid(row=1, column=1, columnspan=1, padx=1, pady=5, sticky="nsew") + self.entry_port.bind("", self.entry_port_callback) + + ## entry authkey + self.label_authkey = customtkinter.CTkLabel( + self.tabview_config.tab("Parameter"), + text="DeepL Auth Key:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_authkey.grid(row=2, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + self.entry_authkey = customtkinter.CTkEntry( + self.tabview_config.tab("Parameter"), + textvariable=customtkinter.StringVar(value=self.parent.AUTH_KEYS["DeepL(auth)"]), + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.entry_authkey.grid(row=2, column=1, columnspan=1, padx=1, pady=5, sticky="nsew") + self.entry_authkey.bind("", self.entry_authkey_callback) + + ## entry message format + self.label_message_format = customtkinter.CTkLabel( + self.tabview_config.tab("Parameter"), + text="Message Format:", + fg_color="transparent", + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.label_message_format.grid(row=3, column=0, columnspan=1, padx=5, pady=5, sticky="nsw") + self.entry_message_format = customtkinter.CTkEntry( + self.tabview_config.tab("Parameter"), + textvariable=customtkinter.StringVar(value=self.parent.MESSAGE_FORMAT), + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.entry_message_format.grid(row=3, column=1, columnspan=1, padx=1, pady=5, sticky="nsew") + self.entry_message_format.bind("", self.entry_message_format_callback) + + def slider_transparency_callback(self, value): + self.parent.wm_attributes("-alpha", value/100) + self.parent.TRANSPARENCY = value + utils.save_json(self.parent.PATH_CONFIG, "TRANSPARENCY", self.parent.TRANSPARENCY) + + def optionmenu_theme_callback(self, choice): + customtkinter.set_appearance_mode(choice) + self.parent.APPEARANCE_THEME = choice + utils.save_json(self.parent.PATH_CONFIG, "APPEARANCE_THEME", self.parent.APPEARANCE_THEME) + + def optionmenu_ui_scaling_callback(self, choice): + new_scaling_float = int(choice.replace("%", "")) / 100 + customtkinter.set_widget_scaling(new_scaling_float) + self.parent.UI_SCALING = choice + utils.save_json(self.parent.PATH_CONFIG, "UI_SCALING", self.parent.UI_SCALING) + + def optionmenu_font_family_callback(self, choice): + # tab menu + self.tabview_config._segmented_button.configure(font=customtkinter.CTkFont(family=choice)) + + # tab UI + self.label_transparency.configure(font=customtkinter.CTkFont(family=choice)) + self.label_appearance_theme.configure(font=customtkinter.CTkFont(family=choice)) + self.optionmenu_appearance_theme.configure(font=customtkinter.CTkFont(family=choice)) + self.label_ui_scaling.configure(font=customtkinter.CTkFont(family=choice)) + self.optionmenu_ui_scaling.configure(font=customtkinter.CTkFont(family=choice)) + self.label_font_family.configure(font=customtkinter.CTkFont(family=choice)) + self.optionmenu_font_family.configure(font=customtkinter.CTkFont(family=choice)) + + # tab Translation + self.label_translation_translator.configure(font=customtkinter.CTkFont(family=choice)) + self.optionmenu_translation_translator.configure(font=customtkinter.CTkFont(family=choice)) + self.label_translation_input_language.configure(font=customtkinter.CTkFont(family=choice)) + self.optionmenu_translation_input_source_language.configure(font=customtkinter.CTkFont(family=choice)) + self.label_translation_input_arrow.configure(font=customtkinter.CTkFont(family=choice)) + self.optionmenu_translation_input_target_language.configure(font=customtkinter.CTkFont(family=choice)) + self.label_translation_output_language.configure(font=customtkinter.CTkFont(family=choice)) + self.optionmenu_translation_output_source_language.configure(font=customtkinter.CTkFont(family=choice)) + self.label_translation_output_arrow.configure(font=customtkinter.CTkFont(family=choice)) + self.label_translation_output_arrow.configure(font=customtkinter.CTkFont(family=choice)) + self.optionmenu_translation_output_target_language.configure(font=customtkinter.CTkFont(family=choice)) + + # tab Transcription + self.label_input_mic_device.configure(font=customtkinter.CTkFont(family=choice)) + self.optionmenu_input_mic_device.configure(font=customtkinter.CTkFont(family=choice)) + self.label_input_mic_voice_language.configure(font=customtkinter.CTkFont(family=choice)) + self.optionmenu_input_mic_voice_language.configure(font=customtkinter.CTkFont(family=choice)) + self.label_input_mic_is_dynamic.configure(font=customtkinter.CTkFont(family=choice)) + self.label_input_mic_threshold.configure(font=customtkinter.CTkFont(family=choice)) + self.label_input_speaker_device.configure(font=customtkinter.CTkFont(family=choice)) + self.optionmenu_input_speaker_device.configure(font=customtkinter.CTkFont(family=choice)) + self.label_input_speaker_voice_language.configure(font=customtkinter.CTkFont(family=choice)) + self.optionmenu_input_speaker_voice_language.configure(font=customtkinter.CTkFont(family=choice)) + self.label_input_speaker_interval.configure(font=customtkinter.CTkFont(family=choice)) + self.entry_input_speaker_interval.configure(font=customtkinter.CTkFont(family=choice)) + + # tab Parameter + self.label_ip_address.configure(font=customtkinter.CTkFont(family=choice)) + self.entry_ip_address.configure(font=customtkinter.CTkFont(family=choice)) + self.label_port.configure(font=customtkinter.CTkFont(family=choice)) + self.entry_port.configure(font=customtkinter.CTkFont(family=choice)) + self.label_authkey.configure(font=customtkinter.CTkFont(family=choice)) + self.entry_authkey.configure(font=customtkinter.CTkFont(family=choice)) + self.label_message_format.configure(font=customtkinter.CTkFont(family=choice)) + self.entry_message_format.configure(font=customtkinter.CTkFont(family=choice)) + + # main window + self.parent.checkbox_translation.configure(font=customtkinter.CTkFont(family=choice)) + self.parent.checkbox_transcription_send.configure(font=customtkinter.CTkFont(family=choice)) + self.parent.checkbox_transcription_receive.configure(font=customtkinter.CTkFont(family=choice)) + self.parent.checkbox_foreground.configure(font=customtkinter.CTkFont(family=choice)) + self.parent.textbox_message_log.configure(font=customtkinter.CTkFont(family=choice)) + self.parent.textbox_message_send_log.configure(font=customtkinter.CTkFont(family=choice)) + self.parent.textbox_message_receive_log.configure(font=customtkinter.CTkFont(family=choice)) + self.parent.textbox_message_system_log.configure(font=customtkinter.CTkFont(family=choice)) + self.parent.entry_message_box.configure(font=customtkinter.CTkFont(family=choice)) + self.parent.tabview_logs._segmented_button.configure(font=customtkinter.CTkFont(family=choice)) + + # window information + try: + self.parent.information_window.textbox_information.configure(font=customtkinter.CTkFont(family=choice)) + except: + pass + + self.parent.FONT_FAMILY = choice + utils.save_json(self.parent.PATH_CONFIG, "FONT_FAMILY", self.parent.FONT_FAMILY) + + def optionmenu_translation_translator_callback(self, choice): + if self.parent.translator.authentication(choice, self.parent.AUTH_KEYS[choice]) is False: + utils.print_textbox(self.parent.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR") + utils.print_textbox(self.parent.textbox_message_system_log, "Auth Key or language setting is incorrect", "ERROR") + else: + self.optionmenu_translation_input_source_language.configure( + values=self.parent.translator.languages[choice], + variable=customtkinter.StringVar(value=self.parent.translator.languages[choice][0])) + self.optionmenu_translation_input_target_language.configure( + values=self.parent.translator.languages[choice], + variable=customtkinter.StringVar(value=self.parent.translator.languages[choice][1])) + self.optionmenu_translation_output_source_language.configure( + values=self.parent.translator.languages[choice], + variable=customtkinter.StringVar(value=self.parent.translator.languages[choice][1])) + self.optionmenu_translation_output_target_language.configure( + values=self.parent.translator.languages[choice], + variable=customtkinter.StringVar(value=self.parent.translator.languages[choice][0])) + + self.parent.CHOICE_TRANSLATOR = choice + self.parent.INPUT_SOURCE_LANG = self.parent.translator.languages[choice][0] + self.parent.INPUT_TARGET_LANG = self.parent.translator.languages[choice][1] + self.parent.OUTPUT_SOURCE_LANG = self.parent.translator.languages[choice][1] + self.parent.OUTPUT_TARGET_LANG = self.parent.translator.languages[choice][0] + utils.save_json(self.parent.PATH_CONFIG, "CHOICE_TRANSLATOR", self.parent.CHOICE_TRANSLATOR) + utils.save_json(self.parent.PATH_CONFIG, "INPUT_SOURCE_LANG", self.parent.INPUT_SOURCE_LANG) + utils.save_json(self.parent.PATH_CONFIG, "INPUT_TARGET_LANG", self.parent.INPUT_TARGET_LANG) + utils.save_json(self.parent.PATH_CONFIG, "OUTPUT_SOURCE_LANG", self.parent.OUTPUT_SOURCE_LANG) + utils.save_json(self.parent.PATH_CONFIG, "OUTPUT_TARGET_LANG", self.parent.OUTPUT_TARGET_LANG) + + def optionmenu_translation_input_source_language_callback(self, choice): + self.parent.INPUT_SOURCE_LANG = choice + utils.save_json(self.parent.PATH_CONFIG, "INPUT_SOURCE_LANG", self.parent.INPUT_SOURCE_LANG) + + def optionmenu_translation_input_target_language_callback(self, choice): + self.parent.INPUT_TARGET_LANG = choice + utils.save_json(self.parent.PATH_CONFIG, "INPUT_TARGET_LANG", self.parent.INPUT_TARGET_LANG) + + def optionmenu_translation_output_source_language_callback(self, choice): + self.parent.OUTPUT_SOURCE_LANG = choice + utils.save_json(self.parent.PATH_CONFIG, "OUTPUT_SOURCE_LANG", self.parent.OUTPUT_SOURCE_LANG) + + def optionmenu_translation_output_target_language_callback(self, choice): + self.parent.OUTPUT_TARGET_LANG = choice + utils.save_json(self.parent.PATH_CONFIG, "OUTPUT_TARGET_LANG", self.parent.OUTPUT_TARGET_LANG) + + def optionmenu_input_mic_device_callback(self, choice): + self.parent.CHOICE_MIC_DEVICE = choice + utils.save_json(self.parent.PATH_CONFIG, "CHOICE_MIC_DEVICE", self.parent.CHOICE_MIC_DEVICE) + + def optionmenu_input_mic_voice_language_callback(self, choice): + self.parent.INPUT_MIC_VOICE_LANGUAGE = choice + utils.save_json(self.parent.PATH_CONFIG, "INPUT_MIC_VOICE_LANGUAGE", self.parent.INPUT_MIC_VOICE_LANGUAGE) + + def checkbox_input_mic_is_dynamic_callback(self): + value = self.checkbox_input_mic_is_dynamic.get() + self.parent.INPUT_MIC_IS_DYNAMIC = value + utils.save_json(self.parent.PATH_CONFIG, "INPUT_MIC_IS_DYNAMIC", self.parent.INPUT_MIC_IS_DYNAMIC) + + def entry_input_mic_threshold_callback(self, event): + self.parent.INPUT_MIC_THRESHOLD = int(self.entry_input_mic_threshold.get()) + utils.save_json(self.parent.PATH_CONFIG, "INPUT_MIC_THRESHOLD", self.parent.INPUT_MIC_THRESHOLD) + + def optionmenu_input_speaker_device_callback(self, choice): + self.parent.CHOICE_SPEAKER_DEVICE = choice + utils.save_json(self.parent.PATH_CONFIG, "CHOICE_SPEAKER_DEVICE", self.parent.CHOICE_SPEAKER_DEVICE) + + def optionmenu_input_speaker_voice_language_callback(self, choice): + self.parent.INPUT_SPEAKER_VOICE_LANGUAGE = choice + utils.save_json(self.parent.PATH_CONFIG, "INPUT_SPEAKER_VOICE_LANGUAGE", self.parent.INPUT_SPEAKER_VOICE_LANGUAGE) + + def entry_input_speaker_interval_callback(self, event): + self.parent.INPUT_SPEAKER_INTERVAL = int(self.entry_input_speaker_interval.get()) + utils.save_json(self.parent.PATH_CONFIG, "INPUT_SPEAKER_INTERVAL", self.parent.INPUT_SPEAKER_INTERVAL) + + def entry_ip_address_callback(self, event): + self.parent.OSC_IP_ADDRESS = self.entry_ip_address.get() + utils.save_json(self.parent.PATH_CONFIG, "OSC_IP_ADDRESS", self.parent.OSC_IP_ADDRESS) + + def entry_port_callback(self, event): + self.parent.OSC_PORT = self.entry_port.get() + utils.save_json(self.parent.PATH_CONFIG, "OSC_PORT", self.parent.OSC_PORT) + + def entry_authkey_callback(self, event): + value = self.entry_authkey.get() + if len(value) > 0: + if self.parent.translator.authentication("DeepL(auth)", self.parent.AUTH_KEYS["DeepL(auth)"]) is True: + self.parent.AUTH_KEYS["DeepL(auth)"] = value + utils.save_json(self.parent.PATH_CONFIG, "AUTH_KEYS", self.parent.AUTH_KEYS) + utils.print_textbox(self.parent.textbox_message_log, "Auth key update completed", "INFO") + utils.print_textbox(self.parent.textbox_message_system_log, "Auth key update completed", "INFO") + else: + utils.print_textbox(self.parent.textbox_message_log, "Auth Key or language setting is incorrect", "ERROR") + utils.print_textbox(self.parent.textbox_message_system_log, "Auth Key or language setting is incorrect", "ERROR") + + def entry_message_format_callback(self, event): + value = self.entry_message_format.get() + if len(value) > 0: + self.parent.MESSAGE_FORMAT = value + utils.save_json(self.parent.PATH_CONFIG, "MESSAGE_FORMAT", self.parent.MESSAGE_FORMAT) \ No newline at end of file diff --git a/window_information.py b/window_information.py new file mode 100644 index 00000000..2b7056e9 --- /dev/null +++ b/window_information.py @@ -0,0 +1,118 @@ +import os +import customtkinter + +class ToplevelWindowInformation(customtkinter.CTkToplevel): + def __init__(self, parent, *args, **kwargs): + super().__init__(parent, *args, **kwargs) + self.parent = parent + self.grid_columnconfigure(0, weight=1) + self.grid_rowconfigure(0, weight=1) + # self.geometry(f"{500}x{300}") + self.minsize(500, 300) + + self.after(200, lambda: self.iconbitmap(os.path.join(os.path.dirname(__file__), "img", "app.ico"))) + self.title("Information") + # create textbox information + self.textbox_information = customtkinter.CTkTextbox( + self, + font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY) + ) + self.textbox_information.grid(row=0, column=0, padx=(10, 10), pady=(10, 10), sticky="nsew") + textbox_information_message = """VRCT(v1.0) + +# 概要 +VRChatで使用されるChatBoxをOSC経由でメッセージを送信するツールになります。 +翻訳エンジンを使用してメッセージとその翻訳部分を同時に送信することができます。 +(翻訳エンジンはDeepL,Google,Bingに対応) + +# 使用方法 + 初期設定時 + 0. VRChatのOSCを有効にする(重要) + + (任意) + 1. DeepLのAPIを使用するためにアカウント登録し、認証キーを取得する + 2. ギアアイコンのボタンでconfigウィンドウを開く + 3. ParameterタブのDeepL Auth Keyに認証キーを記載し、フロッピーアイコンのボタンを押す + 4. configウィンドウを閉じる + + 通常使用時 + 1. メッセージボックスにメッセージを記入 + 2. Enterキーを押し、メッセージを送信する + +# その他の設定 + translation チェックボックス: 翻訳の有効無効 + voice2chatbox チェックボックス : マイクの音声を文字起こししてチャットボックスに送信する + speaker2log チェックボックス : スピーカーの音声から文字起こししてログに表示する + foreground チェックボックス: 最前面表示の有効無効 + + テキストボックス + logタブ + すべてのログを表示 + sendタブ + 送信したメッセージを表示 + receiveタブ + 受信したメッセージを表示 + systemタブ + 機能についてのメッセージを表示 + + configウィンドウ + UIタブ + Transparency: ウィンドウの透過度の調整 + Appearance Theme: ウィンドウテーマを選択 + UI Scaling: UIサイズを調整 + Font Family: 表示フォントを選択 + Translationタブ + Select Translator: 翻訳エンジンの変更 + Send Language: 送信するメッセージに対して翻訳する言語[source, target]を選択 + Receive Language: 受信したメッセージに対して翻訳する言語[source, target]を選択 + Transcriptionタブ + Input Mic Device: 音声を入力するマイクを選択 + Input Mic Voice Language: 入力する音声の言語 + Input Mic IsDynamic: マイクの自動調整 + Input Mic Threshold: 音声取得のしきい値 + Input Speaker Device: 音声を受信するスピーカーを選択 + Input Speaker Voice Language: 受信する音声の言語 + Input Speaker Interval: 受信する音声の調整 + Parameterタブ + OSC IP address: 変更不要 + OSC port: 変更不要 + DeepL Auth key: DeepLの認証キーの設定 + Message Format: 送信するメッセージのデコレーションの設定 + [message]がメッセージボックスに記入したメッセージに置換される + [translation]が翻訳されたメッセージに置換される + 初期フォーマット:"[message]([translation])" + + 設定の初期化 + config.jsonを削除 + +# お問い合わせ +要望などはTwitterまで +https://twitter.com/misya_ai + +# アップデート履歴 +[2023-05-29: v0.1b] v0.1b リリース +[2023-05-30: v0.2b] +- configボタンをギアアイコンに変更 +- 詳細情報のボタンを追加 +- 翻訳機能有効無効のチェックボックスを追加 +- 最前面表示の有効無効のチェックボックスを追加 +- いくつかのバグを修正 +[2023-06-03: v0.3b] +- 全体的にUIを刷新 +- 透過機能を追加 +- テーマのLight/Dark/Systemのモードの変更機能を追加 +- UIのスケール変更機能を追加 +- フォントの変更機能を追加 +[2023-06-06: v0.4b] +- 翻訳エンジンを追加 +- 入力と出力の翻訳言語を選択できるように変更 +[2023-06-20: v1.0] +- マイクからの音声の文字起こし機能を追加 +- スピーカーからの音声の文字起こし機能を追加 + +# 注意事項 +再配布とかはやめてね +""" + + self.textbox_information.insert("end", textbox_information_message) + self.textbox_information.configure(state='disabled') \ No newline at end of file