Merge branch 'develop'

This commit is contained in:
misygauziya
2023-07-22 05:47:38 +09:00
14 changed files with 2408 additions and 1028 deletions

View File

@@ -24,11 +24,17 @@ pip install -r requirements.txt
```bash
git clone https://github.com/misyaguziya/translators.git
python ./translators/setup.py install
cd translators
python ./setup.py install
cd ../
git clone https://github.com/misyaguziya/deepl-translate.git
python ./deepl_translate/setup.py install
cd deepl-translate
python ./setup.py install
cd ../
git clone https://github.com/misyaguziya/custom_speech_recognition.git
python ./custom_speech_recognition/setup.py install
cd custom_speech_recognition
python ./setup.py install
cd ../
```
## Usage
@@ -44,7 +50,7 @@ ptyhon VRCT.py
(任意)
1. DeepLのAPIを使用するためにアカウント登録し、認証キーを取得する
2. ギアアイコンのボタンでconfigウィンドウを開く
3. ParameterタブのDeepL Auth Keyに認証キーを記載し、フロッピーアイコンのボタンを押す
3. ParameterタブのDeepL Auth Keyに認証キーを記載
4. configウィンドウを閉じる
### Normal use
@@ -69,22 +75,29 @@ ptyhon VRCT.py
- Appearance Theme: ウィンドウテーマを選択
- UI Scaling: UIサイズを調整
- Font Family: 表示フォントを選択
- **(New!) UI Language: UIの表示言語を選択**
- Translation tab
- Select Translator: 翻訳エンジンの変更
- Send Language: 送信するメッセージに対して翻訳する言語[source, target]を選択
- Receive Language: 受信したメッセージに対して翻訳する言語[source, target]を選択
- Transcription tab
- **(New!) Input Mic Host: マイクのホストAPIを選択**
- Input Mic Device: マイクを選択
- Input Mic Voice Language: 入力する音声の言語
- Input Mic Energy Threshold: 音声取得のしきい値
- **(New!) Check threshold point: Input Mic Energy Thresholdのしきい値を視覚化**
- Input Mic Dynamic Energy Threshold: 音声取得のしきい値の自動調整
- Input Mic Phase Timeout: 文字起こしする音声時間の上限
- Input Mic Record Timeout: 音声の区切りの無音時間
- Input Mic Max Phrases: 保留する単語の上限
- **(New!) Input Mic Word Filter: MICの文字起こし時にWord Filterで設定した文字が入っていた場合にChatboxに表示しない (ex AAA,BBB,CCC)**
- Input Speaker Device: スピーカーを選択
- Input Speaker Voice Language: 受信する音声の言語
- Input Speaker Energy Threshold: 音声取得のしきい値
- **(New!) Check threshold point: (New!)Input Speaker Energy Thresholdのしきい値を視覚化**
- Input Speaker Dynamic Energy Threshold: 音声取得のしきい値の自動調整
- Input Speaker Record Timeout: 音声の区切りの無音時間
- Input Speaker Phase Timeout: 文字起こしする音声時間の上限
- Input Speaker Max Phrases: 保留する単語の上限
- Parameter tab
- OSC IP address: 変更不要
@@ -94,8 +107,12 @@ ptyhon VRCT.py
- [message]がメッセージボックスに記入したメッセージに置換される
- [translation]が翻訳されたメッセージに置換される
- 初期フォーマット:`[message]([translation])`
- Others tab
- **(New!) Auto clear chat box: メッセージ送信後に書き込んだメッセージを空にする**
## Author
みしゃ(misyaguzi)
twitter: https://twitter.com/misya_ai
booth: https://misyaguziya.booth.pm/items/4814313
Shiina_12siy

View File

@@ -1,77 +1,90 @@
ご購入ありがとうございます。
フィードバックお待ちしております。
概要
# 概要
VRChatで使用されるChatBoxをOSC経由でメッセージを送信するツールになります。
翻訳エンジンを使用してメッセージとその翻訳部分を同時に送信することができます。
(翻訳エンジンはDeepL,Google,Bingに対応)
使用方法
 初期設定時
  0. VRChatのOSCを有効にする重要
# 使用方法
初期設定時
0. VRChatのOSCを有効にする重要
  (任意)
  1. DeepLのAPIを使用するためにアカウント登録し、認証キーを取得する
  2. ギアアイコンのボタンでconfigウィンドウを開く
  3. ParameterタブのDeepL Auth Keyに認証キーを記載し、フロッピーアイコンのボタンを押す
  4. configウィンドウを閉じる
(任意)
1. DeepLのAPIを使用するためにアカウント登録し、認証キーを取得する
2. ギアアイコンのボタンでconfigウィンドウを開く
3. ParameterタブのDeepL Auth Keyに認証キーを記載
4. configウィンドウを閉じる
 通常使用時
  1. メッセージボックスにメッセージを記入
  2. Enterキーを押し、メッセージを送信する
通常使用時
1. メッセージボックスにメッセージを記入
2. Enterキーを押し、メッセージを送信する
 その他の設定
  - translation チェックボックス: 翻訳の有効無効
  - voice2chatbox チェックボックス : マイクの音声を文字起こししてチャットボックスに送信する
  - speaker2log チェックボックス : スピーカーの音声から文字起こししてログに表示する
  - foreground チェックボックス: 最前面表示の有効無効
# その他の設定
translation チェックボックス: 翻訳の有効無効
voice2chatbox チェックボックス : マイクの音声を文字起こししてチャットボックスに送信する
speaker2log チェックボックス : スピーカーの音声から文字起こししてログに表示する
foreground チェックボックス: 最前面表示の有効無効
   - テキストボックス
    - logタブ: すべてのログを表示
    - sendタブ: 送信したメッセージを表示
    - receiveタブ: 受信したメッセージを表示
    - systemタブ: 機能についてのメッセージを表示
テキストボックス
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 Energy Threshold: 音声取得のしきい値
    - Input Mic Dynamic Energy Threshold: 音声取得のしきい値の自動調整
    - Input Mic Record Timeout: 音声の区切りの無音時間
    - Input Mic Max Phrases: 保留する単語の上限
    - Input Speaker Device: スピーカーを選択
    - Input Speaker Voice Language: 受信する音声の言語
    - Input Speaker Energy Threshold: 音声取得のしきい値
    - Input Speaker Dynamic Energy Threshold: 音声取得のしきい値の自動調整
    - Input Speaker Record Timeout: 音声の区切りの無音時間
    - Input Speaker Max Phrases: 保留する単語の上限
  - Parameterタブ
    - OSC IP address: 変更不要
    - OSC port: 変更不要
    - DeepL Auth key: DeepLの認証キーの設定
    - Message Format: 送信するメッセージのデコレーションの設定
     - [message]がメッセージボックスに記入したメッセージに置換される
     - [translation]が翻訳されたメッセージに置換される
     - 初期フォーマット:"[message]([translation])"
configウィンドウ
UIタブ
Transparency: ウィンドウの透過度の調整
Appearance Theme: ウィンドウテーマを選択
UI Scaling: UIサイズを調整
Font Family: 表示フォントを選択
(New!) UI Language: UIの表示言語を選択
Translationタブ
Select Translator: 翻訳エンジンの変更
Send Language: 送信するメッセージに対して翻訳する言語[source, target]を選択
Receive Language: 受信したメッセージに対して翻訳する言語[source, target]を選択
Transcriptionタブ
(New!) Input Mic Host: マイクのホストAPIを選択
Input Mic Device: マイクを選択
Input Mic Voice Language: 入力する音声の言語
Input Mic Energy Threshold: 音声取得のしきい値
(New!) Check threshold point: Input Mic Energy Thresholdのしきい値を視覚化
Input Mic Dynamic Energy Threshold: 音声取得のしきい値の自動調整
Input Mic Record Timeout: 音声の区切りの無音時間
Input Mic Phase Timeout: 文字起こしする音声時間の上限
Input Mic Max Phrases: 保留する単語の上限
(New!) Input Mic Word Filter: MICの文字起こし時にWord Filterで設定した文字が入っていた場合にChatboxに表示しない (ex AAA,BBB,CCC)
Input Speaker Device: スピーカーを選択
Input Speaker Voice Language: 受信する音声の言語
Input Speaker Energy Threshold: 音声取得のしきい値
(New!) Check threshold point: (New!)Input Speaker Energy Thresholdのしきい値を視覚化
Input Speaker Dynamic Energy Threshold: 音声取得のしきい値の自動調整
Input Speaker Record Timeout: 音声の区切りの無音時間
Input Speaker Phase Timeout: 文字起こしする音声時間の上限
Input Speaker Max Phrases: 保留する単語の上限
Parameterタブ
OSC IP address: 変更不要
OSC port: 変更不要
DeepL Auth key: DeepLの認証キーの設定
Message Format: 送信するメッセージのデコレーションの設定
[message]がメッセージボックスに記入したメッセージに置換される
[translation]が翻訳されたメッセージに置換される
初期フォーマット:"[message]([translation])"
Othersタブ
(New!) Auto clear chat box: メッセージ送信後に書き込んだメッセージを空にする
 設定の初期化
  - config.jsonを削除
設定の初期化
config.jsonを削除
お問い合わせ
# お問い合わせ
要望などはTwitterまで
https://twitter.com/misya_ai
アップデート履歴
# アップデート履歴
[2023-05-29: v0.1b] v0.1b リリース
[2023-05-30: v0.2b]
- configボタンをギアアイコンに変更
@@ -97,6 +110,15 @@ https://twitter.com/misya_ai
- 文字起こしの処理の軽量化
[2023-07-05: v1.2]
- 文字起こし精度の向上
[2023-07-21: v1.3]
- UIの表示言語を日本語/英語を選択できる機能を追加
- Energy Thresholdの視覚化機能を追加
- 文字起こしの誤認識対策のため、Word Filterを追加
- WASAPI以外のHostAPIでもマイクとして使用できるようにHostAPIを選択できる機能を追加
- メッセージ送信後に書き込んだメッセージを空にするか選択できる機能を追加
- バグ対策のため、translation/voice2chatbox/speaker2log/foregroundは起動時はOFFになるように変更
- バグ対策のため、スピーカーについて既定デバイス以外を選択した時にERRORが出るように変更
- 半角入力時に一部の文字が書き込めないバグを修正
# 注意事項
再配布とかはやめてね

562
VRCT.py
View File

@@ -1,29 +1,35 @@
import os
import json
import queue
from os import path as os_path
from json import load as json_load
from json import dump as json_dump
from queue import Queue
import tkinter as tk
import customtkinter
from PIL import Image
from customtkinter import CTk, CTkFrame, CTkCheckBox, CTkFont, CTkButton, CTkImage, CTkTabview, CTkTextbox, CTkEntry
from PIL.Image import open as Image_open
from flashtext import KeywordProcessor
import utils
import osc_tools
import window_config
import window_information
import languages
import audio_utils
import audio_recorder
import audio_transcriber
import translation
from threading import Thread
from utils import save_json, print_textbox, thread_fnc, get_localized_text, widget_main_window_label_setter
from osc_tools import send_typing, send_message
from window_config import ToplevelWindowConfig
from window_information import ToplevelWindowInformation
from languages import transcription_lang, translators, translation_lang, selectable_languages
from audio_utils import get_input_device_list, get_output_device_list, get_default_input_device, get_default_output_device
from audio_recorder import SelectedMicRecorder, SelectedSpeakerRecorder
from audio_transcriber import AudioTranscriber
from translation import Translator
class App(customtkinter.CTk):
class App(CTk):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# init instance
self.translator = translation.Translator()
self.translator = Translator()
self.keyword_processor = KeywordProcessor()
# init config
self.PATH_CONFIG = "./config.json"
## main window
self.ENABLE_TRANSLATION = False
self.ENABLE_TRANSCRIPTION_SEND = False
@@ -34,23 +40,26 @@ class App(customtkinter.CTk):
self.APPEARANCE_THEME = "System"
self.UI_SCALING = "100%"
self.FONT_FAMILY = "Yu Gothic UI"
self.UI_LANGUAGE = "en"
## Translation
self.CHOICE_TRANSLATOR = languages.translators[0]
self.INPUT_SOURCE_LANG = list(languages.translation_lang[self.CHOICE_TRANSLATOR].keys())[0]
self.INPUT_TARGET_LANG = list(languages.translation_lang[self.CHOICE_TRANSLATOR].keys())[1]
self.OUTPUT_SOURCE_LANG = list(languages.translation_lang[self.CHOICE_TRANSLATOR].keys())[1]
self.OUTPUT_TARGET_LANG = list(languages.translation_lang[self.CHOICE_TRANSLATOR].keys())[0]
self.CHOICE_TRANSLATOR = translators[0]
self.INPUT_SOURCE_LANG = list(translation_lang[self.CHOICE_TRANSLATOR].keys())[0]
self.INPUT_TARGET_LANG = list(translation_lang[self.CHOICE_TRANSLATOR].keys())[1]
self.OUTPUT_SOURCE_LANG = list(translation_lang[self.CHOICE_TRANSLATOR].keys())[1]
self.OUTPUT_TARGET_LANG = list(translation_lang[self.CHOICE_TRANSLATOR].keys())[0]
## Transcription Send
self.CHOICE_MIC_DEVICE = audio_utils.get_default_input_device()["name"]
self.INPUT_MIC_VOICE_LANGUAGE = list(languages.transcription_lang.keys())[0]
self.CHOICE_MIC_HOST = get_default_input_device()["host"]["name"]
self.CHOICE_MIC_DEVICE = get_default_input_device()["device"]["name"]
self.INPUT_MIC_VOICE_LANGUAGE = list(transcription_lang.keys())[0]
self.INPUT_MIC_ENERGY_THRESHOLD = 300
self.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD = True
self.INPUT_MIC_RECORD_TIMEOUT = 3
self.INPUT_MIC_PHRASE_TIMEOUT = 3
self.INPUT_MIC_MAX_PHRASES = 10
self.INPUT_MIC_WORD_FILTER = []
## Transcription Receive
self.CHOICE_SPEAKER_DEVICE = audio_utils.get_default_output_device()["name"]
self.INPUT_SPEAKER_VOICE_LANGUAGE = list(languages.transcription_lang.keys())[1]
self.CHOICE_SPEAKER_DEVICE = get_default_output_device()["name"]
self.INPUT_SPEAKER_VOICE_LANGUAGE = list(transcription_lang.keys())[1]
self.INPUT_SPEAKER_ENERGY_THRESHOLD = 300
self.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD = True
self.INPUT_SPEAKER_RECORD_TIMEOUT = 3
@@ -66,24 +75,30 @@ class App(customtkinter.CTk):
"Google(web)": None,
}
self.MESSAGE_FORMAT = "[message]([translation])"
# Others
self.ENABLE_AUTO_CLEAR_CHATBOX = False
# load config
if os.path.isfile(self.PATH_CONFIG) is not False:
if os_path.isfile(self.PATH_CONFIG) is not False:
with open(self.PATH_CONFIG, 'r') as fp:
config = json.load(fp)
config = json_load(fp)
# main window
if "ENABLE_TRANSLATION" in config.keys():
if type(config["ENABLE_TRANSLATION"]) is bool:
self.ENABLE_TRANSLATION = config["ENABLE_TRANSLATION"]
if "ENABLE_TRANSCRIPTION_SEND" in config.keys():
if type(config["ENABLE_TRANSCRIPTION_SEND"]) is bool:
self.ENABLE_TRANSCRIPTION_SEND = config["ENABLE_TRANSCRIPTION_SEND"]
if "ENABLE_TRANSCRIPTION_RECEIVE" in config.keys():
if type(config["ENABLE_TRANSCRIPTION_RECEIVE"]) is bool:
self.ENABLE_TRANSCRIPTION_RECEIVE = config["ENABLE_TRANSCRIPTION_RECEIVE"]
if "ENABLE_FOREGROUND" in config.keys():
if type(config["ENABLE_FOREGROUND"]) is bool:
self.ENABLE_FOREGROUND = config["ENABLE_FOREGROUND"]
# main windowは初期はすべてOFFにする
# if "ENABLE_TRANSLATION" in config.keys():
# if type(config["ENABLE_TRANSLATION"]) is bool:
# self.ENABLE_TRANSLATION = config["ENABLE_TRANSLATION"]
# 環境に依ってマイクとスピーカーを同時起動するとエラーが発生するため、起動時は強制的にOFFにする
# if "ENABLE_TRANSCRIPTION_SEND" in config.keys():
# if type(config["ENABLE_TRANSCRIPTION_SEND"]) is bool:
# self.ENABLE_TRANSCRIPTION_SEND = config["ENABLE_TRANSCRIPTION_SEND"]
# if "ENABLE_TRANSCRIPTION_RECEIVE" in config.keys():
# if type(config["ENABLE_TRANSCRIPTION_RECEIVE"]) is bool:
# self.ENABLE_TRANSCRIPTION_RECEIVE = config["ENABLE_TRANSCRIPTION_RECEIVE"]
# if "ENABLE_FOREGROUND" in config.keys():
# if type(config["ENABLE_FOREGROUND"]) is bool:
# self.ENABLE_FOREGROUND = config["ENABLE_FOREGROUND"]
# tab ui
if "TRANSPARENCY" in config.keys():
@@ -99,30 +114,36 @@ class App(customtkinter.CTk):
if "FONT_FAMILY" in config.keys():
if config["FONT_FAMILY"] in list(tk.font.families()):
self.FONT_FAMILY = config["FONT_FAMILY"]
if "UI_LANGUAGE" in config.keys():
if config["UI_LANGUAGE"] in list(selectable_languages.keys()):
self.UI_LANGUAGE = config["UI_LANGUAGE"]
# translation
if "CHOICE_TRANSLATOR" in config.keys():
if config["CHOICE_TRANSLATOR"] in list(self.translator.translator_status.keys()):
self.CHOICE_TRANSLATOR = config["CHOICE_TRANSLATOR"]
if "INPUT_SOURCE_LANG" in config.keys():
if config["INPUT_SOURCE_LANG"] in list(languages.translation_lang[self.CHOICE_TRANSLATOR].keys()):
if config["INPUT_SOURCE_LANG"] in list(translation_lang[self.CHOICE_TRANSLATOR].keys()):
self.INPUT_SOURCE_LANG = config["INPUT_SOURCE_LANG"]
if "INPUT_TARGET_LANG" in config.keys():
if config["INPUT_SOURCE_LANG"] in list(languages.translation_lang[self.CHOICE_TRANSLATOR].keys()):
if config["INPUT_SOURCE_LANG"] in list(translation_lang[self.CHOICE_TRANSLATOR].keys()):
self.INPUT_TARGET_LANG = config["INPUT_TARGET_LANG"]
if "OUTPUT_SOURCE_LANG" in config.keys():
if config["INPUT_SOURCE_LANG"] in list(languages.translation_lang[self.CHOICE_TRANSLATOR].keys()):
if config["INPUT_SOURCE_LANG"] in list(translation_lang[self.CHOICE_TRANSLATOR].keys()):
self.OUTPUT_SOURCE_LANG = config["OUTPUT_SOURCE_LANG"]
if "OUTPUT_TARGET_LANG" in config.keys():
if config["INPUT_SOURCE_LANG"] in list(languages.translation_lang[self.CHOICE_TRANSLATOR].keys()):
if config["INPUT_SOURCE_LANG"] in list(translation_lang[self.CHOICE_TRANSLATOR].keys()):
self.OUTPUT_TARGET_LANG = config["OUTPUT_TARGET_LANG"]
# Transcription
if "CHOICE_MIC_HOST" in config.keys():
if config["CHOICE_MIC_HOST"] in [host for host in get_input_device_list().keys()]:
self.CHOICE_MIC_HOST = config["CHOICE_MIC_HOST"]
if "CHOICE_MIC_DEVICE" in config.keys():
if config["CHOICE_MIC_DEVICE"] in [device["name"] for device in audio_utils.get_input_device_list()]:
if config["CHOICE_MIC_DEVICE"] in [device["name"] for device in get_input_device_list()[self.CHOICE_MIC_HOST]]:
self.CHOICE_MIC_DEVICE = config["CHOICE_MIC_DEVICE"]
if "INPUT_MIC_VOICE_LANGUAGE" in config.keys():
if config["INPUT_MIC_VOICE_LANGUAGE"] in list(languages.transcription_lang.keys()):
if config["INPUT_MIC_VOICE_LANGUAGE"] in list(transcription_lang.keys()):
self.INPUT_MIC_VOICE_LANGUAGE = config["INPUT_MIC_VOICE_LANGUAGE"]
if "INPUT_MIC_ENERGY_THRESHOLD" in config.keys():
if type(config["INPUT_MIC_ENERGY_THRESHOLD"]) is int:
@@ -139,12 +160,17 @@ class App(customtkinter.CTk):
if "INPUT_MIC_MAX_PHRASES" in config.keys():
if type(config["INPUT_MIC_MAX_PHRASES"]) is int:
self.INPUT_MIC_MAX_PHRASES = config["INPUT_MIC_MAX_PHRASES"]
if "INPUT_MIC_WORD_FILTER" in config.keys():
if type(config["INPUT_MIC_WORD_FILTER"]) is list:
self.INPUT_MIC_WORD_FILTER = config["INPUT_MIC_WORD_FILTER"]
if "CHOICE_SPEAKER_DEVICE" in config.keys():
if config["CHOICE_SPEAKER_DEVICE"] in [device["name"] for device in audio_utils.get_output_device_list()]:
if config["CHOICE_SPEAKER_DEVICE"] in [device["name"] for device in get_output_device_list()]:
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.CHOICE_SPEAKER_DEVICE = config["CHOICE_SPEAKER_DEVICE"]
if "INPUT_SPEAKER_VOICE_LANGUAGE" in config.keys():
if config["INPUT_SPEAKER_VOICE_LANGUAGE"] in list(languages.transcription_lang.keys()):
if config["INPUT_SPEAKER_VOICE_LANGUAGE"] in list(transcription_lang.keys()):
self.INPUT_SPEAKER_VOICE_LANGUAGE = config["INPUT_SPEAKER_VOICE_LANGUAGE"]
if "INPUT_SPEAKER_ENERGY_THRESHOLD" in config.keys():
if type(config["INPUT_SPEAKER_ENERGY_THRESHOLD"]) is int:
@@ -179,21 +205,28 @@ class App(customtkinter.CTk):
if type(config["MESSAGE_FORMAT"]) is str:
self.MESSAGE_FORMAT = config["MESSAGE_FORMAT"]
# Others
if "ENABLE_AUTO_CLEAR_CHATBOX" in config.keys():
if type(config["ENABLE_AUTO_CLEAR_CHATBOX"]) is bool:
self.ENABLE_AUTO_CLEAR_CHATBOX = config["ENABLE_AUTO_CLEAR_CHATBOX"]
with open(self.PATH_CONFIG, 'w') as fp:
config = {
"ENABLE_TRANSLATION": self.ENABLE_TRANSLATION,
"ENABLE_TRANSCRIPTION_SEND": self.ENABLE_TRANSCRIPTION_SEND,
"ENABLE_TRANSCRIPTION_RECEIVE": self.ENABLE_TRANSCRIPTION_RECEIVE,
"ENABLE_FOREGROUND": self.ENABLE_FOREGROUND,
# "ENABLE_TRANSLATION": self.ENABLE_TRANSLATION,
# "ENABLE_TRANSCRIPTION_SEND": self.ENABLE_TRANSCRIPTION_SEND,
# "ENABLE_TRANSCRIPTION_RECEIVE": self.ENABLE_TRANSCRIPTION_RECEIVE,
# "ENABLE_FOREGROUND": self.ENABLE_FOREGROUND,
"TRANSPARENCY": self.TRANSPARENCY,
"APPEARANCE_THEME": self.APPEARANCE_THEME,
"UI_SCALING": self.UI_SCALING,
"UI_LANGUAGE": self.UI_LANGUAGE,
"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_HOST": self.CHOICE_MIC_HOST,
"CHOICE_MIC_DEVICE": self.CHOICE_MIC_DEVICE,
"INPUT_MIC_VOICE_LANGUAGE": self.INPUT_MIC_VOICE_LANGUAGE,
"INPUT_MIC_ENERGY_THRESHOLD": self.INPUT_MIC_ENERGY_THRESHOLD,
@@ -201,6 +234,7 @@ class App(customtkinter.CTk):
"INPUT_MIC_RECORD_TIMEOUT": self.INPUT_MIC_RECORD_TIMEOUT,
"INPUT_MIC_PHRASE_TIMEOUT": self.INPUT_MIC_PHRASE_TIMEOUT,
"INPUT_MIC_MAX_PHRASES": self.INPUT_MIC_MAX_PHRASES,
"INPUT_MIC_WORD_FILTER": self.INPUT_MIC_WORD_FILTER,
"CHOICE_SPEAKER_DEVICE": self.CHOICE_SPEAKER_DEVICE,
"INPUT_SPEAKER_VOICE_LANGUAGE": self.INPUT_SPEAKER_VOICE_LANGUAGE,
"INPUT_SPEAKER_ENERGY_THRESHOLD": self.INPUT_SPEAKER_ENERGY_THRESHOLD,
@@ -212,15 +246,16 @@ class App(customtkinter.CTk):
"OSC_PORT": self.OSC_PORT,
"AUTH_KEYS": self.AUTH_KEYS,
"MESSAGE_FORMAT": self.MESSAGE_FORMAT,
"ENABLE_AUTO_CLEAR_CHATBOX": self.ENABLE_AUTO_CLEAR_CHATBOX,
}
json.dump(config, fp, indent=4)
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.iconbitmap(os_path.join(os_path.dirname(__file__), "img", "app.ico"))
self.title("VRCT")
self.geometry(f"{400}x{175}")
self.minsize(400, 175)
@@ -228,132 +263,87 @@ class App(customtkinter.CTk):
self.grid_rowconfigure(0, weight=1)
# add sidebar left
self.sidebar_frame = customtkinter.CTkFrame(self, corner_radius=0)
self.sidebar_frame = CTkFrame(self, corner_radius=0)
self.sidebar_frame.grid(row=0, column=0, rowspan=4, sticky="nsw")
self.sidebar_frame.grid_rowconfigure(5, weight=1)
init_lang_text = "Loading..."
# add checkbox translation
self.checkbox_translation = customtkinter.CTkCheckBox(
self.checkbox_translation = CTkCheckBox(
self.sidebar_frame,
text="translation",
text=init_lang_text,
onvalue=True,
offvalue=False,
command=self.checkbox_translation_callback,
font=customtkinter.CTkFont(family=self.FONT_FAMILY)
font=CTkFont(family=self.FONT_FAMILY)
)
self.checkbox_translation.grid(row=0, column=0, columnspan=2 ,padx=10, pady=(5, 5), sticky="we")
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.checkbox_transcription_send = CTkCheckBox(
self.sidebar_frame,
text="voice2chatbox",
text=init_lang_text,
onvalue=True,
offvalue=False,
command=self.checkbox_transcription_send_callback,
font=customtkinter.CTkFont(family=self.FONT_FAMILY)
font=CTkFont(family=self.FONT_FAMILY)
)
self.checkbox_transcription_send.grid(row=1, column=0, columnspan=2 ,padx=10, pady=(5, 5), sticky="we")
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.checkbox_transcription_receive = CTkCheckBox(
self.sidebar_frame,
text="speaker2log",
text=init_lang_text,
onvalue=True,
offvalue=False,
command=self.checkbox_transcription_receive_callback,
font=customtkinter.CTkFont(family=self.FONT_FAMILY)
font=CTkFont(family=self.FONT_FAMILY)
)
self.checkbox_transcription_receive.grid(row=2, column=0, columnspan=2 ,padx=10, pady=(5, 5), sticky="we")
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.checkbox_foreground = CTkCheckBox(
self.sidebar_frame,
text="foreground",
text=init_lang_text,
onvalue=True,
offvalue=False,
command=self.checkbox_foreground_callback,
font=customtkinter.CTkFont(family=self.FONT_FAMILY)
font=CTkFont(family=self.FONT_FAMILY)
)
self.checkbox_foreground.grid(row=3, 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(
self.button_information = CTkButton(
self.sidebar_frame,
text="",
width=25,
command=self.button_information_callback,
image=customtkinter.CTkImage(Image.open(os.path.join(os.path.dirname(__file__), "img", "info-icon-white.png")))
image=CTkImage(Image_open(os_path.join(os_path.dirname(__file__), "img", "info-icon-white.png")))
)
self.button_information.grid(row=5, column=0, padx=(10, 5), pady=(5, 5), sticky="wse")
self.information_window = None
# add button config
self.button_config = customtkinter.CTkButton(
self.button_config = CTkButton(
self.sidebar_frame,
text="",
width=25,
command=self.button_config_callback,
image=customtkinter.CTkImage(Image.open(os.path.join(os.path.dirname(__file__), "img", "config-icon-white.png")))
image=CTkImage(Image_open(os_path.join(os_path.dirname(__file__), "img", "config-icon-white.png")))
)
self.button_config.grid(row=5, column=1, padx=(5, 10), pady=(5, 5), sticky="wse")
self.config_window = None
# load ui language data
language_yaml_data = get_localized_text(f"{self.UI_LANGUAGE}")
# 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.configure(font=customtkinter.CTkFont(family=self.FONT_FAMILY))
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.tabview_logs.tab("log"),
font=customtkinter.CTkFont(family=self.FONT_FAMILY)
)
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')
self.add_tabview_logs(language_yaml_data)
# add entry message box
self.entry_message_box = customtkinter.CTkEntry(
self.entry_message_box = CTkEntry(
self,
placeholder_text="message",
font=customtkinter.CTkFont(family=self.FONT_FAMILY)
font=CTkFont(family=self.FONT_FAMILY),
)
self.entry_message_box.grid(row=1, column=1, columnspan=2, padx=5, pady=(5, 10), sticky="nsew")
@@ -361,36 +351,40 @@ class App(customtkinter.CTk):
## set 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")
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 checkbox enable translation
if self.ENABLE_TRANSLATION:
self.checkbox_translation.select()
self.checkbox_translation_callback()
else:
self.checkbox_translation.deselect()
# ## 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
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 send
# 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
if self.ENABLE_TRANSCRIPTION_RECEIVE:
self.checkbox_transcription_receive.select()
self.checkbox_transcription_receive_callback()
else:
self.checkbox_transcription_receive.deselect()
# ## set checkbox enable transcription receive
# 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.checkbox_foreground_callback()
else:
self.checkbox_foreground.deselect()
# ## set set checkbox enable foreground
# if self.ENABLE_FOREGROUND:
# self.checkbox_foreground.select()
# self.checkbox_foreground_callback()
# else:
# self.checkbox_foreground.deselect()
## set word filter
for f in self.INPUT_MIC_WORD_FILTER:
self.keyword_processor.add_keyword(f)
## set bind entry message box
self.entry_message_box.bind("<Return>", self.entry_message_box_press_key_enter)
@@ -407,51 +401,52 @@ class App(customtkinter.CTk):
# delete window
self.protocol("WM_DELETE_WINDOW", self.delete_window)
self.config_window = ToplevelWindowConfig(self)
def button_config_callback(self):
if self.config_window is None or not self.config_window.winfo_exists():
self.config_window = window_config.ToplevelWindowConfig(self)
self.checkbox_translation.configure(state="disabled")
self.checkbox_transcription_send.configure(state="disabled")
self.checkbox_transcription_receive.configure(state="disabled")
self.button_config.configure(state="disabled", fg_color=["gray92", "gray14"])
self.config_window.deiconify()
self.config_window.focus_set()
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 = window_information.ToplevelWindowInformation(self)
self.information_window = ToplevelWindowInformation(self)
self.information_window.focus()
def checkbox_translation_callback(self):
self.ENABLE_TRANSLATION = self.checkbox_translation.get()
if self.ENABLE_TRANSLATION:
self.button_config.configure(state="disabled", fg_color=["gray92", "gray14"])
utils.print_textbox(self.textbox_message_log, "Start translation", "INFO")
utils.print_textbox(self.textbox_message_system_log, "Start translation", "INFO")
print_textbox(self.textbox_message_log, "Start translation", "INFO")
print_textbox(self.textbox_message_system_log, "Start translation", "INFO")
else:
if ((self.checkbox_translation.get() is False) and
(self.checkbox_transcription_send.get() is False) and
(self.checkbox_transcription_receive.get() is False)):
self.button_config.configure(state="normal", fg_color=["#3B8ED0", "#1F6AA5"])
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)
print_textbox(self.textbox_message_log, "Stop translation", "INFO")
print_textbox(self.textbox_message_system_log, "Stop translation", "INFO")
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:
self.button_config.configure(state="disabled", fg_color=["gray92", "gray14"])
self.mic_audio_queue = queue.Queue()
mic_device = [device for device in audio_utils.get_input_device_list() if device["name"] == self.CHOICE_MIC_DEVICE][0]
self.mic_audio_recorder = audio_recorder.SelectedMicRecorder(
def transcription_send_start(self):
self.mic_audio_queue = Queue()
mic_device = [device for device in get_input_device_list()[self.CHOICE_MIC_HOST] if device["name"] == self.CHOICE_MIC_DEVICE][0]
self.mic_audio_recorder = SelectedMicRecorder(
mic_device,
self.INPUT_MIC_ENERGY_THRESHOLD,
self.INPUT_MIC_DYNAMIC_ENERGY_THRESHOLD,
self.INPUT_MIC_RECORD_TIMEOUT,
)
self.mic_audio_recorder.record_into_queue(self.mic_audio_queue)
self.mic_transcriber = audio_transcriber.AudioTranscriber(
self.mic_transcriber = AudioTranscriber(
speaker=False,
source=self.mic_audio_recorder.source,
language=languages.transcription_lang[self.INPUT_MIC_VOICE_LANGUAGE],
language=transcription_lang[self.INPUT_MIC_VOICE_LANGUAGE],
phrase_timeout=self.INPUT_MIC_PHRASE_TIMEOUT,
max_phrases=self.INPUT_MIC_MAX_PHRASES,
)
@@ -459,12 +454,18 @@ class App(customtkinter.CTk):
self.mic_transcriber.transcribe_audio_queue(self.mic_audio_queue)
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[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")
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(
@@ -477,49 +478,65 @@ class App(customtkinter.CTk):
if self.checkbox_transcription_send.get() is True:
# send OSC message
osc_tools.send_message(voice_message, self.OSC_IP_ADDRESS, self.OSC_PORT)
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")
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 = utils.thread_fnc(mic_transcript_to_chatbox)
self.mic_print_transcript = thread_fnc(mic_transcript_to_chatbox)
self.mic_print_transcript.daemon = True
self.mic_print_transcript.start()
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")
self.checkbox_transcription_receive.configure(state="normal")
utils.print_textbox(self.textbox_message_log, "Start voice2chatbox", "INFO")
utils.print_textbox(self.textbox_message_system_log, "Start voice2chatbox", "INFO")
else:
if ((self.checkbox_translation.get() is False) and
(self.checkbox_transcription_send.get() is False) and
(self.checkbox_transcription_receive.get() is False)):
self.button_config.configure(state="normal", fg_color=["#3B8ED0", "#1F6AA5"])
if isinstance(self.mic_print_transcript, utils.thread_fnc):
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
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)
print_textbox(self.textbox_message_log, "Stop voice2chatbox", "INFO")
print_textbox(self.textbox_message_system_log, "Stop voice2chatbox", "INFO")
if ((self.checkbox_translation.get() is False) and
(self.checkbox_transcription_send.get() is False) and
(self.checkbox_transcription_receive.get() is False)):
self.button_config.configure(state="normal", fg_color=["#3B8ED0", "#1F6AA5"])
self.checkbox_transcription_send.configure(state="normal")
self.checkbox_transcription_receive.configure(state="normal")
def checkbox_transcription_receive_callback(self):
self.ENABLE_TRANSCRIPTION_RECEIVE = self.checkbox_transcription_receive.get()
if self.ENABLE_TRANSCRIPTION_RECEIVE is True:
def checkbox_transcription_send_callback(self):
self.checkbox_transcription_send.configure(state="disabled")
self.checkbox_transcription_receive.configure(state="disabled")
self.button_config.configure(state="disabled", fg_color=["gray92", "gray14"])
self.spk_audio_queue = queue.Queue()
spk_device = [device for device in audio_utils.get_output_device_list() if device["name"] == self.CHOICE_SPEAKER_DEVICE][0]
self.spk_audio_recorder = audio_recorder.SelectedSpeakerRecorder(
self.update()
self.ENABLE_TRANSCRIPTION_SEND = self.checkbox_transcription_send.get()
if self.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()
else:
th_transcription_send_stop = Thread(target=self.transcription_send_stop)
th_transcription_send_stop.daemon = True
th_transcription_send_stop.start()
save_json(self.PATH_CONFIG, "ENABLE_TRANSCRIPTION_SEND", self.ENABLE_TRANSCRIPTION_SEND)
def transcription_receive_start(self):
self.spk_audio_queue = Queue()
spk_device = [device for device in get_output_device_list() if device["name"] == self.CHOICE_SPEAKER_DEVICE][0]
self.spk_audio_recorder = SelectedSpeakerRecorder(
spk_device,
self.INPUT_SPEAKER_ENERGY_THRESHOLD,
self.INPUT_SPEAKER_DYNAMIC_ENERGY_THRESHOLD,
self.INPUT_SPEAKER_RECORD_TIMEOUT,
)
self.spk_audio_recorder.record_into_queue(self.spk_audio_queue)
self.spk_transcriber = audio_transcriber.AudioTranscriber(
self.spk_transcriber = AudioTranscriber(
speaker=True,
source=self.spk_audio_recorder.source,
language=languages.transcription_lang[self.INPUT_SPEAKER_VOICE_LANGUAGE],
language=transcription_lang[self.INPUT_SPEAKER_VOICE_LANGUAGE],
phrase_timeout=self.INPUT_SPEAKER_PHRASE_TIMEOUT,
max_phrases=self.INPUT_SPEAKER_MAX_PHRASES,
)
@@ -532,8 +549,8 @@ class App(customtkinter.CTk):
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")
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(
@@ -544,47 +561,69 @@ class App(customtkinter.CTk):
)
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)
# send_message(voice_message, self.OSC_IP_ADDRESS, self.OSC_PORT)
if self.checkbox_transcription_receive.get() is True:
# 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")
print_textbox(self.textbox_message_log, f"{voice_message}", "RECEIVE")
print_textbox(self.textbox_message_receive_log, f"{voice_message}", "RECEIVE")
self.spk_print_transcript = utils.thread_fnc(spk_transcript_to_textbox)
self.spk_print_transcript = thread_fnc(spk_transcript_to_textbox)
self.spk_print_transcript.daemon = True
self.spk_print_transcript.start()
utils.print_textbox(self.textbox_message_log, "Start speaker2log", "INFO")
utils.print_textbox(self.textbox_message_system_log, "Start speaker2log", "INFO")
else:
if ((self.checkbox_translation.get() is False) and
(self.checkbox_transcription_send.get() is False) and
(self.checkbox_transcription_receive.get() is False)):
self.button_config.configure(state="normal", fg_color=["#3B8ED0", "#1F6AA5"])
if isinstance(self.spk_print_transcript, utils.thread_fnc):
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")
self.checkbox_transcription_receive.configure(state="normal")
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
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)
print_textbox(self.textbox_message_log, "Stop speaker2log", "INFO")
print_textbox(self.textbox_message_system_log, "Stop speaker2log", "INFO")
if ((self.checkbox_translation.get() is False) and
(self.checkbox_transcription_send.get() is False) and
(self.checkbox_transcription_receive.get() is False)):
self.button_config.configure(state="normal", fg_color=["#3B8ED0", "#1F6AA5"])
self.checkbox_transcription_send.configure(state="normal")
self.checkbox_transcription_receive.configure(state="normal")
def checkbox_transcription_receive_callback(self):
self.checkbox_transcription_send.configure(state="disabled")
self.checkbox_transcription_receive.configure(state="disabled")
self.button_config.configure(state="disabled", fg_color=["gray92", "gray14"])
self.update()
self.ENABLE_TRANSCRIPTION_RECEIVE = self.checkbox_transcription_receive.get()
if self.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()
else:
th_transcription_receive_stop = Thread(target=self.transcription_receive_stop)
th_transcription_receive_stop.daemon = True
th_transcription_receive_stop.start()
save_json(self.PATH_CONFIG, "ENABLE_TRANSCRIPTION_RECEIVE", self.ENABLE_TRANSCRIPTION_RECEIVE)
def checkbox_foreground_callback(self):
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")
print_textbox(self.textbox_message_log, "Start foreground", "INFO")
print_textbox(self.textbox_message_system_log, "Start foreground", "INFO")
else:
self.attributes("-topmost", False)
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)
print_textbox(self.textbox_message_log, "Stop foreground", "INFO")
print_textbox(self.textbox_message_system_log, "Stop foreground", "INFO")
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)
send_typing(False, self.OSC_IP_ADDRESS, self.OSC_PORT)
if self.ENABLE_FOREGROUND:
self.attributes("-topmost", True)
@@ -595,8 +634,8 @@ class App(customtkinter.CTk):
if self.checkbox_translation.get() is False:
chat_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")
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(
@@ -608,24 +647,34 @@ class App(customtkinter.CTk):
chat_message = self.MESSAGE_FORMAT.replace("[message]", message).replace("[translation]", result)
# send OSC message
osc_tools.send_message(chat_message, self.OSC_IP_ADDRESS, self.OSC_PORT)
send_message(chat_message, self.OSC_IP_ADDRESS, self.OSC_PORT)
# update textbox message log
utils.print_textbox(self.textbox_message_log, f"{chat_message}", "SEND")
utils.print_textbox(self.textbox_message_send_log, f"{chat_message}", "SEND")
print_textbox(self.textbox_message_log, f"{chat_message}", "SEND")
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)
if self.ENABLE_AUTO_CLEAR_CHATBOX == 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
osc_tools.send_typing(True, self.OSC_IP_ADDRESS, self.OSC_PORT)
send_typing(True, self.OSC_IP_ADDRESS, self.OSC_PORT)
if self.ENABLE_FOREGROUND:
self.attributes("-topmost", False)
if event.keysym != "??":
if len(event.char) != 0 and event.keysym in self.BREAK_KEYSYM_LIST:
self.entry_message_box.insert("end", event.char)
return "break"
def entry_message_box_leave(self, event):
# send OSC typing
osc_tools.send_typing(False, self.OSC_IP_ADDRESS, self.OSC_PORT)
send_typing(False, self.OSC_IP_ADDRESS, self.OSC_PORT)
if self.ENABLE_FOREGROUND:
self.attributes("-topmost", True)
@@ -633,6 +682,71 @@ class App(customtkinter.CTk):
self.quit()
self.destroy()
def delete_tabview_logs(self, pre_language_yaml_data):
self.tabview_logs.delete(pre_language_yaml_data["main_tab_title_log"])
self.tabview_logs.delete(pre_language_yaml_data["main_tab_title_send"])
self.tabview_logs.delete(pre_language_yaml_data["main_tab_title_receive"])
self.tabview_logs.delete(pre_language_yaml_data["main_tab_title_system"])
def add_tabview_logs(self, language_yaml_data):
main_tab_title_log = language_yaml_data["main_tab_title_log"]
main_tab_title_send = language_yaml_data["main_tab_title_send"]
main_tab_title_receive = language_yaml_data["main_tab_title_receive"]
main_tab_title_system = language_yaml_data["main_tab_title_system"]
# add tabview textbox
self.tabview_logs = CTkTabview(master=self)
self.tabview_logs.add(main_tab_title_log)
self.tabview_logs.add(main_tab_title_send)
self.tabview_logs.add(main_tab_title_receive)
self.tabview_logs.add(main_tab_title_system)
self.tabview_logs.grid(row=0, column=1, padx=0, pady=0, sticky="nsew")
self.tabview_logs._segmented_button.configure(font=CTkFont(family=self.FONT_FAMILY))
self.tabview_logs._segmented_button.grid(sticky="W")
self.tabview_logs.tab(main_tab_title_log).grid_rowconfigure(0, weight=1)
self.tabview_logs.tab(main_tab_title_log).grid_columnconfigure(0, weight=1)
self.tabview_logs.tab(main_tab_title_send).grid_rowconfigure(0, weight=1)
self.tabview_logs.tab(main_tab_title_send).grid_columnconfigure(0, weight=1)
self.tabview_logs.tab(main_tab_title_receive).grid_rowconfigure(0, weight=1)
self.tabview_logs.tab(main_tab_title_receive).grid_columnconfigure(0, weight=1)
self.tabview_logs.tab(main_tab_title_system).grid_rowconfigure(0, weight=1)
self.tabview_logs.tab(main_tab_title_system).grid_columnconfigure(0, weight=1)
self.tabview_logs.configure(fg_color="transparent")
# add textbox message log
self.textbox_message_log = CTkTextbox(
self.tabview_logs.tab(main_tab_title_log),
font=CTkFont(family=self.FONT_FAMILY)
)
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 = CTkTextbox(
self.tabview_logs.tab(main_tab_title_send),
font=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 = CTkTextbox(
self.tabview_logs.tab(main_tab_title_receive),
font=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 = CTkTextbox(
self.tabview_logs.tab(main_tab_title_system),
font=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')
widget_main_window_label_setter(self, language_yaml_data)
if __name__ == "__main__":
try:
app = App()

View File

@@ -1,10 +1,10 @@
import speech_recognition as sr
import pyaudiowpatch as pyaudio
from speech_recognition import Recognizer, Microphone
from pyaudiowpatch import get_sample_size, paInt16
from datetime import datetime
class BaseRecorder:
def __init__(self, source, energy_threshold, dynamic_energy_threshold, record_timeout):
self.recorder = sr.Recognizer()
self.recorder = Recognizer()
self.recorder.energy_threshold = energy_threshold
self.recorder.dynamic_energy_threshold = dynamic_energy_threshold
self.record_timeout = record_timeout
@@ -20,14 +20,14 @@ class BaseRecorder:
self.recorder.adjust_for_ambient_noise(self.source)
def record_into_queue(self, audio_queue):
def record_callback(_, audio:sr.AudioData) -> None:
def record_callback(_, audio):
audio_queue.put((audio.get_raw_data(), datetime.now()))
self.stop = self.recorder.listen_in_background(self.source, record_callback, phrase_time_limit=self.record_timeout)
class SelectedMicRecorder(BaseRecorder):
def __init__(self, device, energy_threshold, dynamic_energy_threshold, record_timeout):
source=sr.Microphone(
source=Microphone(
device_index=device['index'],
sample_rate=int(device["defaultSampleRate"]),
)
@@ -37,11 +37,55 @@ class SelectedMicRecorder(BaseRecorder):
class SelectedSpeakerRecorder(BaseRecorder):
def __init__(self, device, energy_threshold, dynamic_energy_threshold, record_timeout):
source = sr.Microphone(speaker=True,
source = Microphone(speaker=True,
device_index= device["index"],
sample_rate=int(device["defaultSampleRate"]),
chunk_size=pyaudio.get_sample_size(pyaudio.paInt16),
chunk_size=get_sample_size(paInt16),
channels=device["maxInputChannels"]
)
super().__init__(source=source, energy_threshold=energy_threshold, dynamic_energy_threshold=dynamic_energy_threshold, record_timeout=record_timeout)
self.adjust_for_noise()
class BaseEnergyRecorder:
def __init__(self, source):
self.recorder = Recognizer()
self.recorder.energy_threshold = 0
self.recorder.dynamic_energy_threshold = False
self.record_timeout = 0
self.stop = None
if source is None:
raise ValueError("audio source can't be None")
self.source = source
def adjust_for_noise(self):
with self.source:
self.recorder.adjust_for_ambient_noise(self.source)
def record_into_queue(self, energy_queue):
def record_callback(_, energy):
energy_queue.put(energy)
self.stop = self.recorder.listen_energy_in_background(self.source, record_callback)
class SelectedMicEnergyRecorder(BaseEnergyRecorder):
def __init__(self, device):
source=Microphone(
device_index=device['index'],
sample_rate=int(device["defaultSampleRate"]),
)
super().__init__(source=source)
self.adjust_for_noise()
class SelectedSpeakeEnergyRecorder(BaseEnergyRecorder):
def __init__(self, device):
source = Microphone(speaker=True,
device_index= device["index"],
sample_rate=int(device["defaultSampleRate"]),
chunk_size=get_sample_size(paInt16),
channels=device["maxInputChannels"]
)
super().__init__(source=source)
self.adjust_for_noise()

View File

@@ -1,9 +1,9 @@
import io
import threading
from io import BytesIO
from threading import Event
import wave
import speech_recognition as sr
from speech_recognition import Recognizer, AudioData, AudioFile
from datetime import timedelta
import pyaudiowpatch as pyaudio
from pyaudiowpatch import get_sample_size, paInt16
PHRASE_TIMEOUT = 3
MAX_PHRASES = 10
@@ -15,8 +15,8 @@ class AudioTranscriber:
self.phrase_timeout = phrase_timeout
self.max_phrases = max_phrases
self.transcript_data = []
self.transcript_changed_event = threading.Event()
self.audio_recognizer = sr.Recognizer()
self.transcript_changed_event = Event()
self.audio_recognizer = Recognizer()
self.audio_sources = {
"sample_rate": source.SAMPLE_RATE,
"sample_width": source.SAMPLE_WIDTH,
@@ -59,19 +59,18 @@ class AudioTranscriber:
source_info["last_spoken"] = time_spoken
def process_mic_data(self):
audio_data = sr.AudioData(self.audio_sources["last_sample"], self.audio_sources["sample_rate"], self.audio_sources["sample_width"])
audio_data = AudioData(self.audio_sources["last_sample"], self.audio_sources["sample_rate"], self.audio_sources["sample_width"])
return audio_data
def process_speaker_data(self):
temp_file = io.BytesIO()
temp_file = BytesIO()
with wave.open(temp_file, 'wb') as wf:
wf.setnchannels(self.audio_sources["channels"])
p = pyaudio.PyAudio()
wf.setsampwidth(p.get_sample_size(pyaudio.paInt16))
wf.setsampwidth(get_sample_size(paInt16))
wf.setframerate(self.audio_sources["sample_rate"])
wf.writeframes(self.audio_sources["last_sample"])
temp_file.seek(0)
with sr.AudioFile(temp_file) as source:
with AudioFile(temp_file) as source:
audio = self.audio_recognizer.record(source)
return audio

View File

@@ -1,40 +1,43 @@
import pyaudiowpatch as pyaudio
from pyaudiowpatch import PyAudio, paWASAPI
def get_input_device_list():
devices = []
with pyaudio.PyAudio() as p:
wasapi_info = p.get_host_api_info_by_type(pyaudio.paWASAPI)
devices = {}
with PyAudio() as p:
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']):
host = p.get_host_api_info_by_index(host_index)
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["maxInputChannels"] > 0 and device["isLoopbackDevice"] is False:
devices.append(device)
if device["maxInputChannels"] > 0 and device["isLoopbackDevice"] is False:
if host["name"] in devices.keys():
devices[host["name"]].append(device)
else:
devices[host["name"]] = [device]
return devices
def get_output_device_list():
devices =[]
with pyaudio.PyAudio() as p:
wasapi_info = p.get_host_api_info_by_type(pyaudio.paWASAPI)
with PyAudio() as p:
wasapi_info = p.get_host_api_info_by_type(paWASAPI)
for device in p.get_loopback_device_info_generator():
if device["hostApi"] == wasapi_info["index"] and device["isLoopbackDevice"] is True:
devices.append(device)
return devices
def get_default_input_device():
with pyaudio.PyAudio() as p:
wasapi_info = p.get_host_api_info_by_type(pyaudio.paWASAPI)
defaultInputDevice = wasapi_info["defaultInputDevice"]
with PyAudio() as p:
api_info = p.get_default_host_api_info()
defaultInputDevice = api_info["defaultInputDevice"]
for host_index in range(0, p.get_host_api_count()):
host = p.get_host_api_info_by_index(host_index)
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["index"] == defaultInputDevice:
default_device = device
return default_device
return {"host":host, "device": device}
def get_default_output_device():
with pyaudio.PyAudio() as p:
wasapi_info = p.get_host_api_info_by_type(pyaudio.paWASAPI)
with PyAudio() as p:
wasapi_info = p.get_host_api_info_by_type(paWASAPI)
defaultOutputDevice = wasapi_info["defaultOutputDevice"]
for host_index in range(0, p.get_host_api_count()):

325
ctk_scrollable_dropdown.py Normal file
View File

@@ -0,0 +1,325 @@
"""
CustomTkinter Scrollable Dropdown Menu
Author: Akash Bora
License: MIT
This is a custom dropdown menu for customtkinter.
Homepage: https://github.com/Akascape/CTkScrollableDropdown
Advanced Scrollable Dropdown class for customtkinter widgets
Author: Akash Bora
"""
import customtkinter
import sys
import time
class CTkScrollableDropdown(customtkinter.CTkToplevel):
def __init__(self, attach, x=None, y=None, button_color=None, height: int = 200, width: int = None,
fg_color=None, button_height: int = 20, justify="center", scrollbar_button_color=None,
scrollbar=True, scrollbar_button_hover_color=None, frame_border_width=2, values=[],
command=None, image_values=[], alpha: float = 0.97, frame_corner_radius=20, double_click=False,
resize=True, frame_border_color=None, text_color=None, autocomplete=False, **button_kwargs):
super().__init__(takefocus=1)
self.transient(self.master)
self.alpha = alpha
self.attach = attach
self.corner = frame_corner_radius
self.padding = 0
self.focus_something = False
self.disable = True
if sys.platform.startswith("win"):
self.after(100, lambda: self.overrideredirect(True))
self.transparent_color = self._apply_appearance_mode(self._fg_color)
self.attributes("-transparentcolor", self.transparent_color)
elif sys.platform.startswith("darwin"):
self.overrideredirect(True)
self.transparent_color = 'systemTransparent'
self.attributes("-transparent", True)
self.focus_something = True
else:
self.overrideredirect(True)
self.transparent_color = '#000001'
self.corner = 0
self.padding = 18
self.withdraw()
self.hide = True
self.attach.bind('<Configure>', lambda e: self._withdraw() if not self.disable else None, add="+")
self.attach.winfo_toplevel().bind('<Configure>', lambda e: self._withdraw() if not self.disable else None, add="+")
self.attach.winfo_toplevel().bind("<Triple-Button-1>", lambda e: self._withdraw() if not self.disable else None, add="+")
self.attach.winfo_toplevel().bind("<Button-3>", lambda e: self._withdraw() if not self.disable else None, add="+")
self.attach.winfo_toplevel().bind("<Button-2>", lambda e: self._withdraw() if not self.disable else None, add="+")
self.attributes('-alpha', 0)
self.disable = False
self.fg_color = customtkinter.ThemeManager.theme["CTkFrame"]["fg_color"] if fg_color is None else fg_color
self.scroll_button_color = customtkinter.ThemeManager.theme["CTkScrollbar"]["button_color"] if scrollbar_button_color is None else scrollbar_button_color
self.scroll_hover_color = customtkinter.ThemeManager.theme["CTkScrollbar"]["button_hover_color"] if scrollbar_button_hover_color is None else scrollbar_button_hover_color
self.frame_border_color = customtkinter.ThemeManager.theme["CTkFrame"]["border_color"] if frame_border_color is None else frame_border_color
self.button_color = customtkinter.ThemeManager.theme["CTkFrame"]["top_fg_color"] if button_color is None else button_color
self.text_color = customtkinter.ThemeManager.theme["CTkLabel"]["text_color"] if text_color is None else text_color
if scrollbar is False:
self.scroll_button_color = self.fg_color
self.scroll_hover_color = self.fg_color
self.frame = customtkinter.CTkScrollableFrame(self, bg_color=self.transparent_color, fg_color=self.fg_color,
scrollbar_button_hover_color=self.scroll_hover_color,
corner_radius=self.corner, border_width=frame_border_width,
scrollbar_button_color=self.scroll_button_color,
border_color=self.frame_border_color)
self.frame._scrollbar.grid_configure(padx=3)
self.frame.pack(expand=True, fill="both")
self.dummy_entry = customtkinter.CTkEntry(self.frame, fg_color="transparent", border_width=0, height=1, width=1)
self.no_match = customtkinter.CTkLabel(self.frame, text="No Match")
self.height = height
self.height_new = height
self.width = width
self.command = command
self.fade = False
self.resize = resize
self.autocomplete = autocomplete
self.var_update = customtkinter.StringVar()
self.appear = False
if justify.lower()=="left":
self.justify = "w"
elif justify.lower()=="right":
self.justify = "e"
else:
self.justify = "c"
self.button_height = button_height
self.values = values
self.button_num = len(self.values)
self.image_values = None if len(image_values)!=len(self.values) else image_values
self.resizable(width=False, height=False)
self._init_buttons(**button_kwargs)
# Add binding for different ctk widgets
if double_click or self.attach.winfo_name().startswith("!ctkentry") or self.attach.winfo_name().startswith("!ctkcombobox"):
self.attach.bind('<Double-Button-1>', lambda e: self._iconify(), add="+")
else:
self.attach.bind('<Button-1>', lambda e: self._iconify(), add="+")
if self.attach.winfo_name().startswith("!ctkcombobox"):
self.attach._canvas.tag_bind("right_parts", "<Button-1>", lambda e: self._iconify())
self.attach._canvas.tag_bind("dropdown_arrow", "<Button-1>", lambda e: self._iconify())
if self.command is None:
self.command = self.attach.set
if self.attach.winfo_name().startswith("!ctkoptionmenu"):
self.attach._canvas.bind("<Button-1>", lambda e: self._iconify())
self.attach._text_label.bind("<Button-1>", lambda e: self._iconify())
if self.command is None:
self.command = self.attach.set
self.update_idletasks()
self.x = x
self.y = y
if self.autocomplete:
self.bind_autocomplete()
# self.deiconify()
self.withdraw()
self.attributes("-alpha", self.alpha)
def _withdraw(self):
if self.hide is False: self.withdraw()
self.hide = True
def _update(self, a, b, c):
self.live_update(self.attach._entry.get())
def bind_autocomplete(self, ):
def appear(x):
self.appear = True
if self.attach.winfo_name().startswith("!ctkcombobox"):
self.attach._entry.configure(textvariable=self.var_update)
self.attach._entry.bind("<Key>", appear)
self.attach.set(self.values[0])
self.var_update.trace_add('write', self._update)
if self.attach.winfo_name().startswith("!ctkentry"):
self.attach.configure(textvariable=self.var_update)
self.attach.bind("<Key>", appear)
self.var_update.trace_add('write', self._update)
def fade_out(self):
for i in range(100,0,-10):
if not self.winfo_exists():
break
self.attributes("-alpha", i/100)
self.update()
time.sleep(1/100)
def fade_in(self):
for i in range(0,100,10):
if not self.winfo_exists():
break
self.attributes("-alpha", i/100)
self.update()
time.sleep(1/100)
def _init_buttons(self, **button_kwargs):
self.i = 0
self.widgets = {}
for row in self.values:
self.widgets[self.i] = customtkinter.CTkButton(self.frame,
text=row,
height=self.button_height,
fg_color=self.button_color,
text_color=self.text_color,
image=self.image_values[i] if self.image_values is not None else None,
anchor=self.justify,
command=lambda k=row: self._attach_key_press(k), **button_kwargs)
self.widgets[self.i].pack(fill="x", pady=2, padx=(self.padding, 0))
self.i+=1
self.hide = False
def destroy_popup(self):
self.destroy()
self.disable = True
def place_dropdown(self):
self.x_pos = self.attach.winfo_rootx() if self.x is None else self.x + self.attach.winfo_rootx()
self.y_pos = self.attach.winfo_rooty() + self.attach.winfo_reqheight() + 5 if self.y is None else self.y + self.attach.winfo_rooty()
self.width_new = self.attach.winfo_width() if self.width is None else self.width
if self.resize:
if self.button_num==1:
self.height_new = self.button_height * self.button_num + 45
else:
self.height_new = self.button_height * self.button_num + 35
if self.height_new>self.height:
self.height_new = self.height
self.geometry('{}x{}+{}+{}'.format(self.width_new, self.height_new,
self.x_pos, self.y_pos))
self.fade_in()
self.attributes('-alpha', self.alpha)
def _iconify(self):
if self.disable: return
if self.hide:
self._deiconify()
self.place_dropdown()
if self.focus_something:
self.dummy_entry.pack()
self.dummy_entry.focus_set()
self.after(100, self.dummy_entry.pack_forget)
self.hide = False
self.focus_set()
self.focus()
else:
self.withdraw()
self.hide = True
def _attach_key_press(self, k):
self.fade = True
if self.command:
self.command(k)
self.fade = False
self.fade_out()
self.withdraw()
self.hide = True
def live_update(self, string=None):
if not self.appear: return
if self.disable: return
if self.fade: return
if string:
self._deiconify()
i=1
for key in self.widgets.keys():
s = self.widgets[key].cget("text")
if not s.startswith(string):
self.widgets[key].pack_forget()
else:
self.widgets[key].pack(fill="x", pady=2, padx=(self.padding, 0))
i+=1
if i==1:
self.no_match.pack(fill="x", pady=2, padx=(self.padding, 0))
else:
self.no_match.pack_forget()
self.button_num = i
self.place_dropdown()
else:
self.no_match.pack_forget()
self.button_num = len(self.values)
for key in self.widgets.keys():
self.widgets[key].destroy()
self._init_buttons()
self.place_dropdown()
self.frame._parent_canvas.yview_moveto(0.0)
self.appear = False
def insert(self, value, **kwargs):
self.widgets[self.i] = customtkinter.CTkButton(self.frame,
text=value,
height=self.button_height,
fg_color=self.button_color,
text_color=self.text_color,
anchor=self.justify,
command=lambda k=value: self._attach_key_press(k), **kwargs)
self.widgets[self.i].pack(fill="x", pady=2, padx=(self.padding, 0))
self.i+=1
self.values.append(value)
def _deiconify(self):
if len(self.values)>0:
self.deiconify()
def popup(self, x=None, y=None):
self.x = x
self.y = y
self.hide = True
self._iconify()
def configure(self, **kwargs):
if "height" in kwargs:
self.height = kwargs.pop("height")
self.height_new = self.height
if "alpha" in kwargs:
self.alpha = kwargs.pop("alpha")
if "width" in kwargs:
self.width = kwargs.pop("width")
if "fg_color" in kwargs:
self.frame.configure(fg_color=kwargs.pop("fg_color"))
if "values" in kwargs:
self.values = kwargs.pop("values")
self.image_values = None
for key in self.widgets.keys():
self.widgets[key].destroy()
self._init_buttons()
if "image_values" in kwargs:
self.image_values = kwargs.pop("image_values")
self.image_values = None if len(self.image_values)!=len(self.values) else self.image_values
if self.image_values is not None:
i=0
for key in self.widgets.keys():
self.widgets[key].configure(image=self.image_values[i])
i+=1
if "button_color" in kwargs:
for key in self.widgets.keys():
self.widgets[key].configure(fg_color=kwargs.pop("button_color"))
for key in self.widgets.keys():
self.widgets[key].configure(**kwargs)

View File

@@ -154,128 +154,134 @@ translation_lang["DeepL(auth)"] = {
"Chinese":"zh"
}
translation_lang["Google(web)"] = {
"japanese":"ja",
"english":"en",
"chinese":"zh",
"arabic":"ar",
"russian":"ru",
"french":"fr",
"german":"de",
"spanish":"es",
"portuguese":"pt",
"italian":"it",
"korean":"ko",
"greek":"el",
"dutch":"nl",
"hindi":"hi",
"turkish":"tr",
"malay":"ms",
"thai":"th",
"vietnamese":"vi",
"indonesian":"id",
"hebrew":"he",
"polish":"pl",
"mongolian":"mn",
"czech":"cs",
"hungarian":"hu",
"estonian":"et",
"bulgarian":"bg",
"danish":"da",
"finnish":"fi",
"romanian":"ro",
"swedish":"sv",
"slovenian":"sl",
"persian/farsi":"fa",
"bosnian":"bs",
"serbian":"sr",
"filipino":"tl",
"haitiancreole":"ht",
"catalan":"ca",
"croatian":"hr",
"latvian":"lv",
"lithuanian":"lt",
"urdu":"ur",
"ukrainian":"uk",
"welsh":"cy",
"swahili":"sw",
"samoan":"sm",
"slovak":"sk",
"afrikaans":"af",
"norwegian":"no",
"bengali":"bn",
"malagasy":"mg",
"maltese":"mt",
"gujarati":"gu",
"tamil":"ta",
"telugu":"te",
"punjabi":"pa",
"amharic":"am",
"azerbaijani":"az",
"belarusian":"be",
"cebuano":"ceb",
"esperanto":"eo",
"basque":"eu",
"irish":"ga"
"Japanese":"ja",
"English":"en",
"Chinese":"zh",
"Arabic":"ar",
"Russian":"ru",
"French":"fr",
"German":"de",
"Spanish":"es",
"Portuguese":"pt",
"Italian":"it",
"Korean":"ko",
"Greek":"el",
"Dutch":"nl",
"Hindi":"hi",
"Turkish":"tr",
"Malay":"ms",
"Thai":"th",
"Vietnamese":"vi",
"Indonesian":"id",
"Hebrew":"he",
"Polish":"pl",
"Mongolian":"mn",
"Czech":"cs",
"Hungarian":"hu",
"Estonian":"et",
"Bulgarian":"bg",
"Danish":"da",
"Finnish":"fi",
"Romanian":"ro",
"Swedish":"sv",
"Slovenian":"sl",
"Persian/Farsi":"fa",
"Bosnian":"bs",
"Serbian":"sr",
"Filipino":"tl",
"Haitiancreole":"ht",
"Catalan":"ca",
"Croatian":"hr",
"Latvian":"lv",
"Lithuanian":"lt",
"Urdu":"ur",
"Ukrainian":"uk",
"Welsh":"cy",
"Swahili":"sw",
"Samoan":"sm",
"Slovak":"sk",
"Afrikaans":"af",
"Norwegian":"no",
"Bengali":"bn",
"Malagasy":"mg",
"Maltese":"mt",
"Gujarati":"gu",
"Tamil":"ta",
"Telugu":"te",
"Punjabi":"pa",
"Amharic":"am",
"Azerbaijani":"az",
"Belarusian":"be",
"Cebuano":"ceb",
"Esperanto":"eo",
"Basque":"eu",
"Irish":"ga"
}
translation_lang["Bing(web)"] = {
"japanese":"ja",
"english":"en",
"chinese":"zh",
"arabic":"ar",
"russian":"ru",
"french":"fr",
"german":"de",
"spanish":"es",
"portuguese":"pt",
"italian":"it",
"korean":"ko",
"greek":"el",
"dutch":"nl",
"hindi":"hi",
"turkish":"tr",
"malay":"ms",
"thai":"th",
"vietnamese":"vi",
"indonesian":"id",
"hebrew":"he",
"polish":"pl",
"czech":"cs",
"hungarian":"hu",
"estonian":"et",
"bulgarian":"bg",
"danish":"da",
"finnish":"fi",
"romanian":"ro",
"swedish":"sv",
"slovenian":"sl",
"persian/farsi":"fa",
"bosnian":"bs",
"serbian":"sr",
"fijian":"fj",
"filipino":"tl",
"haitiancreole":"ht",
"catalan":"ca",
"croatian":"hr",
"latvian":"lv",
"lithuanian":"lt",
"urdu":"ur",
"ukrainian":"uk",
"welsh":"cy",
"tahiti":"ty",
"tongan":"to",
"swahili":"sw",
"samoan":"sm",
"slovak":"sk",
"afrikaans":"af",
"norwegian":"no",
"bengali":"bn",
"malagasy":"mg",
"maltese":"mt",
"queretaro otomi":"otq",
"klingon/tlhingan hol":"tlh",
"gujarati":"gu",
"tamil":"ta",
"telugu":"te",
"punjabi":"pa",
"irish":"ga"
"Japanese":"ja",
"English":"en",
"Chinese":"zh",
"Arabic":"ar",
"Russian":"ru",
"French":"fr",
"German":"de",
"Spanish":"es",
"Portuguese":"pt",
"Italian":"it",
"Korean":"ko",
"Greek":"el",
"Dutch":"nl",
"Hindi":"hi",
"Turkish":"tr",
"Malay":"ms",
"Thai":"th",
"Vietnamese":"vi",
"Indonesian":"id",
"Hebrew":"he",
"Polish":"pl",
"Czech":"cs",
"Hungarian":"hu",
"Estonian":"et",
"Bulgarian":"bg",
"Danish":"da",
"Finnish":"fi",
"Romanian":"ro",
"Swedish":"sv",
"Slovenian":"sl",
"Persian/Farsi":"fa",
"Bosnian":"bs",
"Serbian":"sr",
"Fijian":"fj",
"Filipino":"tl",
"Haitiancreole":"ht",
"Catalan":"ca",
"Croatian":"hr",
"Latvian":"lv",
"Lithuanian":"lt",
"Urdu":"ur",
"Ukrainian":"uk",
"Welsh":"cy",
"Tahiti":"ty",
"Tongan":"to",
"Swahili":"sw",
"Samoan":"sm",
"Slovak":"sk",
"Afrikaans":"af",
"Norwegian":"no",
"Bengali":"bn",
"Malagasy":"mg",
"Maltese":"mt",
"Queretaro otomi":"otq",
"Klingon/tlhingan Hol":"tlh",
"Gujarati":"gu",
"Tamil":"ta",
"Telugu":"te",
"Punjabi":"pa",
"Irish":"ga"
}
selectable_languages = {
"en": "English",
"ja": "日本語",
# 新しい言語とキーを追加する場合はここに追記してください
}

128
locales.yml Normal file
View File

@@ -0,0 +1,128 @@
en:
# main window
checkbox_translation: "Translation"
checkbox_transcription_send: "Voice2chatbox"
checkbox_transcription_receive: "Speaker2log"
checkbox_foreground: "Foreground"
# main tabview
main_tab_title_log: "Log"
main_tab_title_send: "Send"
main_tab_title_receive: "Receive"
main_tab_title_system: "System"
# configure window
# config tabview
config_tab_title_ui: "UI"
config_tab_title_translation: "Translation"
config_tab_title_transcription: "Transcription"
config_tab_title_parameter: "Parameter"
config_tab_title_others: "Others"
# tab UI
label_transparency: "Transparency"
label_appearance_theme: "Appearance Theme"
label_ui_scaling: "UI Scaling"
label_font_family: "Font Family"
label_ui_language: "UI Language"
# tab Translation
label_translation_translator: "Select Translator"
label_translation_input_language: "Send Language"
label_translation_output_language: "Receive Language"
# tab Transcription
label_input_mic_host: "Input Mic Host"
label_input_mic_device: "Input Mic Device"
label_input_mic_voice_language: "Input Mic Voice Language"
label_input_mic_energy_threshold: "Input Mic Energy Threshold"
checkbox_input_mic_threshold_check: "Check threshold point"
label_input_mic_dynamic_energy_threshold: "Input Mic Dynamic Energy Threshold"
label_input_mic_record_timeout: "Input Mic Record Timeout"
label_input_mic_phrase_timeout: "Input Mic Phrase Timeout"
label_input_mic_max_phrases: "Input Mic Max Phrases"
label_input_mic_word_filter: "Input Mic Word Filter"
label_input_speaker_device: "Input Speaker Device"
label_input_speaker_voice_language: "Input Speaker Voice Language"
label_input_speaker_energy_threshold: "Input Speaker Energy Threshold"
checkbox_input_speaker_threshold_check: "Check threshold point"
label_input_speaker_dynamic_energy_threshold: "Input Speaker Dynamic Energy Threshold"
label_input_speaker_record_timeout: "Input Speaker Record Timeout"
label_input_speaker_phrase_timeout: "Input Speaker Phrase Timeout"
label_input_speaker_max_phrases: "Input Speaker Max Phrases"
# tab Parameter
label_ip_address: "OSC IP address"
label_port: "OSC Port"
label_authkey: "DeepL Auth Key"
label_message_format: "Message Format"
# tab Others
label_checkbox_auto_clear_chatbox: "Auto clear chat box"
ja:
# main window
checkbox_translation: "翻訳"
checkbox_transcription_send: "マイク->チャットボックス"
checkbox_transcription_receive: "スピーカー->ログ"
checkbox_foreground: "最前面表示"
# main tabview
main_tab_title_log: "ログ"
main_tab_title_send: "送信"
main_tab_title_receive: "受信"
main_tab_title_system: "システム"
# configure window
# config tabview
config_tab_title_ui: "UI"
config_tab_title_translation: "翻訳方法"
config_tab_title_transcription: "音声認識"
config_tab_title_parameter: "パラメーター"
config_tab_title_others: "その他"
# tab UI
label_transparency: "透過度"
label_appearance_theme: "外観テーマを選択"
label_ui_scaling: "UIの拡大縮小"
label_font_family: "使用フォントの変更"
label_ui_language: "UI 言語"
# tab Translation
label_translation_translator: "翻訳エンジンの選択"
label_translation_input_language: "送信言語-->翻訳言語"
label_translation_output_language: "受信言語-->翻訳言語"
# tab Transcription
label_input_mic_host: "マイク入力ホスト"
label_input_mic_device: "マイク入力デバイス"
label_input_mic_voice_language: "マイクで話す言語"
label_input_mic_energy_threshold: "音声取得のしきい値"
checkbox_input_mic_threshold_check: "音声取得のしきい値の視覚化"
label_input_mic_dynamic_energy_threshold: "音声取得のしきい値の自動調整"
label_input_mic_record_timeout: "マイク音声の区切りの無音時間"
label_input_mic_phrase_timeout: "文字起こしする音声時間の上限"
label_input_mic_max_phrases: "保留する単語の上限(マイク)"
label_input_mic_word_filter: "ワードフィルタ"
label_input_speaker_device: "スピーカー(聞き取りたいデバイス)"
label_input_speaker_voice_language: "聞き取る音声の言語"
label_input_speaker_energy_threshold: "音声取得のしきい値"
checkbox_input_speaker_threshold_check: "音声取得のしきい値の視覚化"
label_input_speaker_dynamic_energy_threshold: "音声取得のしきい値の自動調整"
label_input_speaker_record_timeout: "スピーカー音声の区切りの無音時間"
label_input_speaker_phrase_timeout: "文字起こしする音声時間の上限"
label_input_speaker_max_phrases: "保留する単語の上限(スピーカー)"
# tab Parameter
# label_ip_address: ""
# label_port: ""
# label_authkey: ""
label_message_format: "送信するメッセージのフォーマット"
# tab Others
label_checkbox_auto_clear_chatbox: "送信後はチャットボックスを空にする"

View File

@@ -3,3 +3,5 @@ PyAudioWPatch
python-osc
customtkinter
deepl
flashtext
pyyaml

View File

@@ -1,13 +1,13 @@
import deepl
import deepl_translate
import translators as ts
import languages
from deepl import Translator as deepl_Translator
from deepl_translate import translate as deepl_web_Translator
from translators import translate_text as other_web_Translator
from languages import translators, translation_lang
# Translator
class Translator():
def __init__(self):
self.translator_status = {}
for translator in languages.translators:
for translator in translators:
self.translator_status[translator] = False
self.deepl_client = None
@@ -18,7 +18,7 @@ class Translator():
self.translator_status["DeepL(web)"] = True
result = True
elif translator_name == "DeepL(auth)":
self.deepl_client = deepl.Translator(authkey)
self.deepl_client = deepl_Translator(authkey)
self.deepl_client.translate_text(" ", target_lang="EN-US")
self.translator_status["DeepL(auth)"] = True
result = True
@@ -35,10 +35,10 @@ class Translator():
def translate(self, translator_name, source_language, target_language, message):
result = ""
try:
source_language=languages.translation_lang[translator_name][source_language]
target_language=languages.translation_lang[translator_name][target_language]
source_language=translation_lang[translator_name][source_language]
target_language=translation_lang[translator_name][target_language]
if translator_name == "DeepL(web)":
result = deepl_translate.translate(
result = deepl_web_Translator(
source_language=source_language,
target_language=target_language,
text=message
@@ -50,14 +50,14 @@ class Translator():
target_lang=target_language,
).text
elif translator_name == "Google(web)":
result = ts.translate_text(
result = other_web_Translator(
query_text=message,
translator="google",
from_language=source_language,
to_language=target_language,
)
elif translator_name == "Bing(web)":
result = ts.translate_text(
result = other_web_Translator(
query_text=message,
translator="bing",
from_language=source_language,

105
utils.py
View File

@@ -1,16 +1,18 @@
import json
import datetime
import threading
from json import load, dump
from os import path as os_path
import yaml
from datetime import datetime
from threading import Thread, Event
def save_json(path, key, value):
with open(path, "r") as fp:
json_data = json.load(fp)
json_data = load(fp)
json_data[key] = value
with open(path, "w") as fp:
json.dump(json_data, fp, indent=4)
dump(json_data, fp, indent=4)
def print_textbox(textbox, message, tags=None):
now = datetime.datetime.now()
now = datetime.now()
now = now.strftime('%H:%M:%S')
textbox.tag_config("ERROR", foreground="#FF0000")
@@ -25,11 +27,11 @@ def print_textbox(textbox, message, tags=None):
textbox.configure(state='disabled')
textbox.see("end")
class thread_fnc(threading.Thread):
class thread_fnc(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()
self._stop = Event()
def stop(self):
self._stop.set()
def stopped(self):
@@ -39,3 +41,90 @@ class thread_fnc(threading.Thread):
if self.stopped():
return
self.fnc(*self._args, **self._kwargs)
def get_localized_text(language):
file_path = os_path.join(os_path.dirname(__file__), "locales.yml")
with open(file_path, encoding="utf-8") as file:
languages_yaml_data = yaml.safe_load(file)
default_language = "en"
if language in languages_yaml_data:
localized_text = languages_yaml_data[language]
if default_language in languages_yaml_data:
default_text = languages_yaml_data[default_language]
merged_text = {**default_text, **localized_text}
return merged_text
else:
return localized_text
else:
return None
def get_key_by_value(dictionary, value):
for key, val in dictionary.items():
if val == value:
return key
return None
def widget_config_window_label_setter(self, language_yaml_data):
widget_names = [
# tab UI
"label_transparency",
"label_appearance_theme",
"label_ui_scaling",
"label_font_family",
"label_ui_language",
# tab Translation
"label_translation_translator",
"label_translation_input_language",
"label_translation_output_language",
# tab Transcription
"label_input_mic_host",
"label_input_mic_device",
"label_input_mic_voice_language",
"label_input_mic_energy_threshold",
"checkbox_input_mic_threshold_check",
"label_input_mic_dynamic_energy_threshold",
"label_input_mic_record_timeout",
"label_input_mic_phrase_timeout",
"label_input_mic_max_phrases",
"label_input_mic_word_filter",
"label_input_speaker_device",
"label_input_speaker_voice_language",
"label_input_speaker_energy_threshold",
"checkbox_input_speaker_threshold_check",
"label_input_speaker_dynamic_energy_threshold",
"label_input_speaker_record_timeout",
"label_input_speaker_phrase_timeout",
"label_input_speaker_max_phrases",
# tab Parameter
"label_ip_address",
"label_port",
"label_authkey",
"label_message_format",
# tab Others
"label_checkbox_auto_clear_chatbox"
]
for name in widget_names:
widget = getattr(self, name)
text_value = language_yaml_data.get(name)
if widget is not None and text_value is not None:
widget.configure(text=text_value + ":")
def widget_main_window_label_setter(self, language_yaml_data):
widget_names = [
"checkbox_translation",
"checkbox_transcription_send",
"checkbox_transcription_receive",
"checkbox_foreground",
]
for name in widget_names:
widget = getattr(self, name)
text_value = language_yaml_data.get(name)
if widget is not None and text_value is not None:
widget.configure(text=text_value)

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
import os
import customtkinter
from customtkinter import CTkToplevel, CTkTextbox, CTkFont
class ToplevelWindowInformation(customtkinter.CTkToplevel):
class ToplevelWindowInformation(CTkToplevel):
def __init__(self, parent, *args, **kwargs):
super().__init__(parent, *args, **kwargs)
self.parent = parent
@@ -13,12 +13,12 @@ class ToplevelWindowInformation(customtkinter.CTkToplevel):
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.textbox_information = CTkTextbox(
self,
font=customtkinter.CTkFont(family=self.parent.FONT_FAMILY)
font=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.2)
textbox_information_message = """VRCT(v1.3)
# 概要
VRChatで使用されるChatBoxをOSC経由でメッセージを送信するツールになります。
@@ -32,7 +32,7 @@ VRChatで使用されるChatBoxをOSC経由でメッセージを送信するツ
(任意)
1. DeepLのAPIを使用するためにアカウント登録し、認証キーを取得する
2. ギアアイコンのボタンでconfigウィンドウを開く
3. ParameterタブのDeepL Auth Keyに認証キーを記載し、フロッピーアイコンのボタンを押す
3. ParameterタブのDeepL Auth Keyに認証キーを記載
4. configウィンドウを閉じる
通常使用時
@@ -61,22 +61,29 @@ VRChatで使用されるChatBoxをOSC経由でメッセージを送信するツ
Appearance Theme: ウィンドウテーマを選択
UI Scaling: UIサイズを調整
Font Family: 表示フォントを選択
(New!) UI Language: UIの表示言語を選択
Translationタブ
Select Translator: 翻訳エンジンの変更
Send Language: 送信するメッセージに対して翻訳する言語[source, target]を選択
Receive Language: 受信したメッセージに対して翻訳する言語[source, target]を選択
Transcriptionタブ
(New!) Input Mic Host: マイクのホストAPIを選択
Input Mic Device: マイクを選択
Input Mic Voice Language: 入力する音声の言語
Input Mic Energy Threshold: 音声取得のしきい値
(New!) Check threshold point: Input Mic Energy Thresholdのしきい値を視覚化
Input Mic Dynamic Energy Threshold: 音声取得のしきい値の自動調整
Input Mic Record Timeout: 音声の区切りの無音時間
Input Mic Phase Timeout: 文字起こしする音声時間の上限
Input Mic Max Phrases: 保留する単語の上限
(New!) Input Mic Word Filter: MICの文字起こし時にWord Filterで設定した文字が入っていた場合にChatboxに表示しない (ex AAA,BBB,CCC)
Input Speaker Device: スピーカーを選択
Input Speaker Voice Language: 受信する音声の言語
Input Speaker Energy Threshold: 音声取得のしきい値
(New!) Check threshold point: (New!)Input Speaker Energy Thresholdのしきい値を視覚化
Input Speaker Dynamic Energy Threshold: 音声取得のしきい値の自動調整
Input Speaker Record Timeout: 音声の区切りの無音時間
Input Speaker Phase Timeout: 文字起こしする音声時間の上限
Input Speaker Max Phrases: 保留する単語の上限
Parameterタブ
OSC IP address: 変更不要
@@ -86,6 +93,8 @@ VRChatで使用されるChatBoxをOSC経由でメッセージを送信するツ
[message]がメッセージボックスに記入したメッセージに置換される
[translation]が翻訳されたメッセージに置換される
初期フォーマット:"[message]([translation])"
Othersタブ
(New!) Auto clear chat box: メッセージ送信後に書き込んだメッセージを空にする
設定の初期化
config.jsonを削除
@@ -118,6 +127,17 @@ https://twitter.com/misya_ai
- いくつかのバクを修正
- 翻訳/文字起こし言語の表記を略称からわかりやすい文字に変更
- 文字起こしの処理の軽量化
[2023-07-05: v1.2]
- 文字起こし精度の向上
[2023-07-21: v1.3]
- UIの表示言語を日本語/英語を選択できる機能を追加
- Energy Thresholdの視覚化機能を追加
- 文字起こしの誤認識対策のため、Word Filterを追加
- WASAPI以外のHostAPIでもマイクとして使用できるようにHostAPIを選択できる機能を追加
- メッセージ送信後に書き込んだメッセージを空にするか選択できる機能を追加
- バグ対策のため、translation/voice2chatbox/speaker2log/foregroundは起動時はOFFになるように変更
- バグ対策のため、スピーカーについて既定デバイス以外を選択した時にERRORが出るように変更
- 半角入力時に一部の文字が書き込めないバグを修正
# 注意事項
再配布とかはやめてね