From 5efa9c37d66352f9df15853d43b1766b06d25adb Mon Sep 17 00:00:00 2001 From: misyaguziya <53165965+misyaguziya@users.noreply.github.com> Date: Thu, 9 Oct 2025 13:11:59 +0900 Subject: [PATCH] Add documentation for modules and runtime instructions - Created detailed documentation for the device_manager, model, model_extra, osc, overlay, overlay_image, transcription, translation, transliteration, utils, watchdog, and websocket modules. - Added a comprehensive run events payloads document outlining the payloads sent during various run events in the controller. - Included runtime instructions and dependencies for setting up the project in a Windows environment. - Introduced a mypy configuration file to manage type checking and ignore errors in specific modules temporarily. --- src-python/docs/CHANGELOG.md | 27 + src-python/docs/CODING_RULES.md | 171 +++++ src-python/docs/README.md | 18 + src-python/docs/api.md | 704 +++++++++++++++++++++ src-python/docs/architecture.md | 21 + src-python/docs/diagrams.md | 51 ++ src-python/docs/modules/config.md | 203 ++++++ src-python/docs/modules/controller.md | 158 +++++ src-python/docs/modules/device_manager.md | 73 +++ src-python/docs/modules/model.md | 105 +++ src-python/docs/modules/model_extra.md | 60 ++ src-python/docs/modules/osc.md | 17 + src-python/docs/modules/overlay.md | 31 + src-python/docs/modules/overlay_image.md | 115 ++++ src-python/docs/modules/transcription.md | 51 ++ src-python/docs/modules/translation.md | 21 + src-python/docs/modules/transliteration.md | 17 + src-python/docs/modules/utils.md | 74 +++ src-python/docs/modules/watchdog.md | 12 + src-python/docs/modules/websocket.md | 18 + src-python/docs/run_events_payloads.md | 125 ++++ src-python/docs/runtime.md | 43 ++ src-python/mypy.ini | 32 + 23 files changed, 2147 insertions(+) create mode 100644 src-python/docs/CHANGELOG.md create mode 100644 src-python/docs/CODING_RULES.md create mode 100644 src-python/docs/README.md create mode 100644 src-python/docs/api.md create mode 100644 src-python/docs/architecture.md create mode 100644 src-python/docs/diagrams.md create mode 100644 src-python/docs/modules/config.md create mode 100644 src-python/docs/modules/controller.md create mode 100644 src-python/docs/modules/device_manager.md create mode 100644 src-python/docs/modules/model.md create mode 100644 src-python/docs/modules/model_extra.md create mode 100644 src-python/docs/modules/osc.md create mode 100644 src-python/docs/modules/overlay.md create mode 100644 src-python/docs/modules/overlay_image.md create mode 100644 src-python/docs/modules/transcription.md create mode 100644 src-python/docs/modules/translation.md create mode 100644 src-python/docs/modules/transliteration.md create mode 100644 src-python/docs/modules/utils.md create mode 100644 src-python/docs/modules/watchdog.md create mode 100644 src-python/docs/modules/websocket.md create mode 100644 src-python/docs/run_events_payloads.md create mode 100644 src-python/docs/runtime.md create mode 100644 src-python/mypy.ini diff --git a/src-python/docs/CHANGELOG.md b/src-python/docs/CHANGELOG.md new file mode 100644 index 00000000..723f3942 --- /dev/null +++ b/src-python/docs/CHANGELOG.md @@ -0,0 +1,27 @@ +# CHANGELOG + +## 2025-10-09 — 型チェック整備と安全性向上 + +- 修正: `controller.py` + - `Controller.chatMessage` の戻り値注釈を `dict` に明示(関数は JSON 系の応答オブジェクトを返します)。 + - `Controller.checkSoftwareUpdated` が実際に応答を返すように `return` を追加。 + +- 修正: `model.py` + - `startCheckMicEnergy` / `startCheckSpeakerEnergy` のコールバック引数を Optional に変更し、呼び出し前に `callable` チェックを追加。これにより None を渡しても安全に扱えるようになりました。 + - `convertMessageToTransliteration` の返り値を常に list に統一。hiragana/romaji が False の場合は空リストを返します。 + - `createOverlayImageLargeLog` 等の Overlay 作成関数で `target_language` を dict で受けた場合に内部で言語リストへ正規化する挙動を明確化。 + +- 目的: mypy の型チェックの警告/エラーを削減し、ランタイムでの None 呼び出しによるクラッシュを防止するための低リスクな変更です。 + +- 注記: + - 追加で `types-requests` をプロジェクト仮想環境にインストールし、mypy の外部型スタブ不足を解消しました。 + - 本チェンジは内部の型注釈とガードを中心としており、動作ロジックの大きな変更は行っていません。動作確認は mypy(型チェック)と ruff(lint)を通過したことをもって行っています。 + +## 1.0.0 (initial) +- 初回ドキュメント作成: ソースコードに基づく仕様書 / 詳細設計書を docs 配下に追加。 +- 対象: utils, model, controller, device_manager, config, translation, transcription, overlay, websocket, osc, transliteration, watchdog + +今後の作業候補: +- requirements.txt の自動生成とテストスイート追加 +- ドキュメントの API サンプル(リクエスト/レスポンス)追加 +- UML 図/シーケンス図の画像化 diff --git a/src-python/docs/CODING_RULES.md b/src-python/docs/CODING_RULES.md new file mode 100644 index 00000000..19a626b0 --- /dev/null +++ b/src-python/docs/CODING_RULES.md @@ -0,0 +1,171 @@ +# VRCT backend — コーディングルール + +目的: +- 可読性と保守性を保ちながら既存スタイルを尊重する。 +- 漸進的に型注釈を導入し、mypy と ruff のチェックに合わせる。 +- 自動化(CI / pre-commit)へ導出しやすくする。 + +注意: 既存の命名・構造(関数名・クラス名・変数名・run mapping のキー等)はコード上の互換性のためそのまま維持します。以下は新規実装やリファクタ時に従うべきルールです。 + +## 目次 +- 命名規則 +- モジュール・パッケージ構成 +- インポート +- 型注釈と mypy 方針 +- ドキュメンテーション / docstrings +- エラーハンドリングとロギング +- 非同期 / スレッド / キューの扱い +- テストと CI +- リファクタ・互換性の観点 + +--- + +## 命名規則 +- モジュール名: 小文字、アンダースコアで区切る(例: `overlay_utils.py`)。既存ファイルに従う。 +- パッケージ名: 小文字(`models`, `websocket` など)。 +- クラス名: CapWords (PascalCase)。既存クラス(`Controller`, `Model`, `Overlay`)に従う。 +- 関数・メソッド名: snake_case。 +- 変数名: snake_case。短い一時変数は `i`, `j`, `buf` 等の伝統的な省略形を可とするが、意味ある名前を優先する。 +- 定数: UPPER_SNAKE_CASE(`config.py` の定数に合わせる)。 +- run_mapping のキー: 現在は短い key(例: `transcription_mic`)を内部で使い `run_mapping` に `/run/...` を置いている。この慣習は維持する。Controller 内で `self.run_mapping[...]` を直接参照する実装は許容される。 + +例: `selected_translation_compute_device` は内部 key、`/run/selected_translation_compute_device` が外部イベント名である点を区別して使う。 + +## モジュール・パッケージ構成 +- 各サブ領域(ocr, overlay, transcription, translation, websocket 等)は `models/` 下に整理済みのため、同様の粒度で新機能は追加する。 +- パッケージは必ず `__init__.py` を置く(static analysis / mypy のため)。空の `__init__.py` でも可。これにより相対インポートが安定する。 + +## インポート +- 標準ライブラリ、サードパーティ、ローカルの順でインポートをまとめる。 +- ローカルモジュールを参照する場合は相対 import を使ってもよいが、プロジェクト全体を PYTHONPATH に入れてテスト/静的解析できるようにすること。 +- 例: +``` +import os +import json + +import numpy as np + +from . import overlay_utils +``` + +## 型注釈と mypy 方針 +- 戦略: Relax + incremental annotations(漸進的型付け)。以下を守る。 + - 新規コードは可能な限り型注釈を追加する(関数シグネチャ・返り値)。 + - 既存の大きな関数は段階的に注釈する。まずモジュール境界(public API)のシグネチャに注釈を入れる。内部の細かい変数は後回し。 + - CI では初期段階で mypy を `--ignore-missing-imports --allow-untyped-defs --allow-redefinition` のように緩めて実行する。段階的に `--check-untyped-defs` を有効化していく。 + - 型の `Any` を多用しない。どうしても必要な場合は `# type: ignore[assignment]` を付けて理由をコメントに残す。 + +## docstrings / コメント +- 重要な public 関数・メソッドとクラスに短い docstring を追加する(目的・引数・返り値の要約)。Google/Numpy スタイルのどちらかに統一する必要はないが、プロジェクト内で混乱しないよう短く統一すること。 +- 実装トリッキーな箇所には `# NOTE:` や `# FIXME:` コメントを残し、必要なら issue を紐付ける(例: `# NOTE: keep in sync with mainloop.run_mapping`)。 + +## エラーハンドリングとロギング +- 例外をキャッチするときは有用なコンテキストをログに残す(`errorLogging()` のようなユーティリティを使う)。 +- broad except: を使う場合は最低限 `errorLogging()` を呼び、必要なら `raise` して上位へ伝播する。 + +## 非同期 / スレッド / キューの扱い +- スレッドは `threading.Thread` を使っている箇所があるため、スレッド間通信は `queue.Queue` ベースで実装すること。 +- スレッドを生成する関数は `start_` プレフィックス(例: `start_transcription_thread`)のように命名すると分かりやすい。 + +## テスト・CI +- まずは軽量な CI ワークフローを入れる: + - ruff check + - mypy (relaxed) + - 自動テスト(将来的に pytest を追加) +- pre-commit フックの導入を推奨: ruff auto-fix と isort (import 整理) を採用できる。 + +## リファクタ・互換性 +- 既存 public API(stdin/stdout の endpoint 仕様や `run_mapping` のキー、Controller のメソッド名)は後方互換を優先する。変更が必要な場合は CHANGELOG に明示する。 + +## 小さなコーディング規約チェックリスト(PR テンプレ) +- 新しい public メソッドには docstring を付けたか +- 既存命名規則に従っているか(snake_case / PascalCase / UPPER_SNAKE_CASE) +- 型アノテーションをシグネチャに追加したか(可能な限り) +- 直接 stdout に JSON を print する箇所は `printResponse` 等ユーティリティ経由か確認する + +--- + +このドキュメントは現状のスタイルを尊重して最小限の規則を与えることを目的としています。次のステップを希望する場合: +- CI に ruff/mypy を組み込む PR を作成 +- pre-commit 用の設定ファイル(`.pre-commit-config.yaml`)を追加して自動整形を導入 +- 型注釈・テストのためのタスク分割(優先順位をつけた TODO) + +要望があれば、これをベースに `.pre-commit-config.yaml`、`pyproject.toml` の ruff 設定、あるいは CI ワークフローの雛形(GitHub Actions)を作成します。 + +## Copilot と共同作業するための具体例とテンプレート +以下は Copilot に推奨プロンプトを投げやすく、また PR 作成時に便利なテンプレート類です。コピー&ペーストして使用してください。 + +### 関数テンプレート(型注釈 + docstring) +```python +from typing import Any, Dict, Optional + +def example_handler(endpoint: str, data: Any) -> Dict[str, Any]: + """Handle an example endpoint. + + Args: + endpoint: incoming endpoint string (e.g. '/get/data/version') + data: request payload (None for many GETs) + + Returns: + A dict suitable for printResponse(status, endpoint, result) + """ + # implementation... + result = {"status": 200, "endpoint": endpoint, "result": data} + return result +``` + +### Controller の run 発行パターン(推奨) +Controller 内で run を呼ぶときは `self.run(self.run_mapping["key"], payload)` の形を維持してください。Copilot に尋ねるときは「この run key に対応する payload の形は?」と聞くとペイロード例を生成しやすいです。 + +### Docstring 例(Google スタイル) +```python +def set_selected_tab_no(tab_no: int) -> Dict[str, Any]: + """Set the current tab. + + Args: + tab_no: index of tab to select + + Returns: + A response dict with status and new tab number + """ + ... +``` + +### PR チェックリスト(拡張版) +- コーディング規則に従っているか +- 新しい public API の docstring があるか +- 型注釈を最小限追加しているか(特に関数シグネチャ) +- ruff check が通るか +- mypy(relaxed)で重大な型エラーが出ていないか +- docs (必要箇所) を更新したか(API 変更があれば) + +### 推奨 `.pre-commit-config.yaml`(例) +```yaml +repos: + - repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.14.0 + hooks: + - id: ruff + args: ["--fix"] + - repo: https://github.com/PyCQA/isort + rev: 5.12.0 + hooks: + - id: isort + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.18.2 + hooks: + - id: mypy + args: ["--ignore-missing-imports", "--allow-untyped-defs", "--allow-redefinition"] +``` + +### 推奨 ruff 設定(pyproject.toml への最小設定例) +```toml +[tool.ruff] +line-length = 88 +extend-ignore = ["E203"] +select = ["E", "F", "W", "C90"] +``` + +--- + +更新が必要なら私が `.pre-commit-config.yaml`、`pyproject.toml`、および CI ワークフロー (GitHub Actions) の雛形を作成してコミットまでできます。どれを優先しますか? diff --git a/src-python/docs/README.md b/src-python/docs/README.md new file mode 100644 index 00000000..be03a5d0 --- /dev/null +++ b/src-python/docs/README.md @@ -0,0 +1,18 @@ +# VRCT — ドキュメント + +このドキュメントセットは、VRCT プロジェクト(`src-python`)に含まれる実装の仕様書 / 設計書 / 詳細設計書です。 + +目的 +- ソースコード構造、モジュール間データフロー、API エンドポイント、設定、実行手順、トラブルシュートを網羅して開発・運用の参照を容易にする。 + +対象 +- `utils.py`, `model.py`, `controller.py`, `mainloop.py`, `device_manager.py`, `config.py` および `models/` 以下の全モジュール。 + +ドキュメント構成(主要ファイル) +- `architecture.md` — アーキテクチャ概観 +- `modules/` — 各モジュールごとの詳細設計(個別ファイル) +- `api.md` — 外部/内部向け API エンドポイント マッピング(`mainloop.py` の `mapping` / `run_mapping` に準拠) +- `runtime.md` — 実行/セットアップ手順、依存関係 +- `diagrams.md` — システム図(Mermaid とテキスト両方) +- `CODING_RULES.md` — プロジェクト固有のコーディング規約(命名・型方針・lint/mypy 方針 等) +- `CHANGELOG.md` — 変更履歴 \ No newline at end of file diff --git a/src-python/docs/api.md b/src-python/docs/api.md new file mode 100644 index 00000000..373e29d4 --- /dev/null +++ b/src-python/docs/api.md @@ -0,0 +1,704 @@ +--- + + + +## API エンドポイント仕様 + +概要 +- このドキュメントは `mainloop.py` の `mapping` と `run_mapping` に定義された全エンドポイントを列挙します。 +- すべてのリクエストは標準入力経由で JSON を一行送る形で受信され、標準出力へ JSON 応答を出力します。 + +共通リクエスト形式 +- JSON オブジェクトを 1 行で標準入力に流します。 +- フィールド: + - `endpoint`: エンドポイント文字列 (例: `/get/data/version`) + - `data`: 任意(多くの GET 系は null、SET 系は新しい値やオブジェクト) + +例 +```json +{"endpoint":"/get/data/version","data":null} +``` + +共通レスポンス形式 +- mainloop は各リクエストの処理結果を次の形式で標準出力に出します(内部 util の `printResponse` を経由): + +成功例: +```json +{"status":200,"endpoint":"/get/data/version","result":"3.2.2"} +``` + +エラー例: +```json +{"status":400,"endpoint":"/set/data/osc_ip_address","result":{"message":"Invalid IP address","data":"127.0.0.1"}} +``` + +ロック状態と再試行 +- `mapping` にある各ハンドラは `"status": True|False` を持ちます。 + - False の場合、`handleRequest` は 423 (Locked endpoint) を返し、メインのハンドラはその要求をキューに戻して待機します(遅延再実行のため)。 + +run イベント +- `controller` は UI 更新などの非同期通知を行うために `run(status, endpoint, payload)` を呼び出します。これらは `run_mapping` にマップされ、外部 UI には `/run/...` 形式のエンドポイントで配信されます。 + +以下は `controller.py` から抽出した run イベントと、実際に送られるペイロードの具体例です。UI 側はこれらの JSON 形状を期待することで正しく動作します。 + +`/run/connected_network` (200) + - payload: true | false + +`/run/enable_ai_models` (200) + - payload: true | false + +`/run/mic_host_list` (200) + - payload: ["Host 1", "Host 2"] + +`/run/mic_device_list` (200) + - payload: ["Microphone (Realtek)", "Headset Microphone"] + +`/run/speaker_device_list` (200) + - payload: ["Speakers (Realtek)", "Headset"] + +`/run/initialization_complete` (200) + - payload: dict mapping endpoint -> current value (constructed from init_mapping) + - 例: {"/get/data/version":"3.2.2","/get/data/selected_tab_no":0} + +`/run/selected_mic_device` (200) + - payload: {"host": , "device": } + +`/run/selected_speaker_device` (200) + - payload: string (device name) + +`/run/error_device` (400) + - payload: {"message":"No mic device detected","data": null} + +`/run/check_mic_volume` (200) + - payload: numeric energy value (float) + +`/run/check_speaker_volume` (200) + - payload: numeric energy value (float) + +`/run/download_progress_ctranslate2_weight` (200) + - payload: {"weight_type":"m2m100_418m","progress":0.42} + +`/run/downloaded_ctranslate2_weight` (200) + - payload: "m2m100_418m" + +`/run/error_ctranslate2_weight` (400) + - payload: {"message":"CTranslate2 weight download error","data": null} + +`/run/download_progress_whisper_weight` (200) + - payload: {"weight_type":"base","progress":0.78} + +`/run/downloaded_whisper_weight` (200) + - payload: "base" + +`/run/error_whisper_weight` (400) + - payload: {"message":"Whisper weight download error","data": null} + +`/run/word_filter` (200) + - payload: {"message":"Detected by word filter: "} + +`/run/error_translation_engine` (400) + - payload: {"message":"Translation engine limit error","data": null} + +`/run/error_translation_mic_vram_overflow` (400) + - payload: {"message":"VRAM out of memory during translation of mic","data":""} + +`/run/error_translation_speaker_vram_overflow` (400) + - payload: {"message":"VRAM out of memory during translation of speaker","data":""} + +`/run/error_translation_chat_vram_overflow` (400) + - payload: {"message":"VRAM out of memory during translation of chat","data":""} + +`/run/enable_translation` (200/400) + - payload: on OOM: {"message":"Translation disabled due to VRAM overflow","data": false} + +`/run/transcription_send_mic_message` (200) + - payload: + { + "original": {"message": "Hello", "transliteration": []}, + "translations": [ {"message":"こんにちは","transliteration":[]}, ... ] + } + +`/run/transcription_receive_speaker_message` (200) + - payload: same shape as `/run/transcription_send_mic_message` + +`/run/software_update_info` (200) + - payload: e.g. {"has_update": true, "latest_version": "3.3.0"} + +`/run/selected_translation_compute_type` (200) + - payload: string ("auto"|"cpu"|"cuda:0") + +`/run/selected_transcription_compute_type` (200) + - payload: string + +`/run/selected_translation_compute_device` (200) + - payload: device descriptor (e.g. {"name":"cuda:0","type":"gpu"}) + +`/run/selected_translation_engines` (200) + - payload: config.SELECTED_TRANSLATION_ENGINES (list/dict per tab) + +`/run/translation_engines` (200) + - payload: ["CTranslate2"] + +`/run/initialization_progress` (200) + - payload: integer (1..4) + +`/run/enable_osc_query` (200) + - payload: {"data": true|false, "disabled_functions": ["vrc_mic_mute_sync"]} + + +エンドポイント一覧(mapping にある全エンドポイント) + +注: 各行の説明では、`method` 的な概念はありません。すべてのエンドポイントは JSON リクエストで同様に呼び出します。`data` の期待値は説明に記載しています。 + +1) メイン操作 + +- /set/enable/translation — data: null — 翻訳を有効にします。 + - 成功応答例: + ```json + {"status":200, "endpoint":"/set/enable/translation", "result": true} + ``` + - 失敗例(VRAM OOM を検出して無効化されたケースは run イベントで通知されます): + ```json + {"status":400, "endpoint":"/set/enable/translation", "result":{"message":"Translation disabled due to VRAM overflow","data":false}} + ``` + +- /set/disable/translation — data: null — 翻訳を無効にします。 + - 成功応答例: + ```json + {"status":200, "endpoint":"/set/disable/translation", "result": false} + ``` + +- /set/enable/transcription_send — data: null — マイク転写(送信)を有効化します。 + - 実行はスレッドで開始される場合がある。成功例: + ```json + {"status":200, "endpoint":"/set/enable/transcription_send", "result": true} + ``` + +- /set/disable/transcription_send — data: null — 停止要求。成功例: + ```json + {"status":200, "endpoint":"/set/disable/transcription_send", "result": false} + ``` + +- /set/enable/transcription_receive — data: null — スピーカー側の転写を有効化します。 +- /set/disable/transcription_receive — data: null — 無効化します。 + +- /set/enable/foreground — data: null — フォアグラウンド表示を有効化します。 + - 成功例: {"status":200, "endpoint":"/set/enable/foreground", "result": true} + +- /get/data/selected_tab_no — data: null — 現在のタブ番号を返します。 + - 例: {"status":200, "endpoint":"/get/data/selected_tab_no", "result": 0} + +- /get/data/main_window_sidebar_compact_mode — data: null — サイドバーのコンパクト表示の現在値を返します。 + - 例: {"status":200, "endpoint":"/get/data/main_window_sidebar_compact_mode","result": false} + + +- /set/data/selected_tab_no — data: int — タブ番号を設定します。 + - リクエスト例: {"endpoint":"/set/data/selected_tab_no","data":1} + - 成功応答例: {"status":200, "endpoint":"/set/data/selected_tab_no","result":1} + +- /get/data/translation_engines — data: null — 利用可能な翻訳エンジン一覧を返します。 + - 例: {"status":200, "endpoint":"/get/data/translation_engines","result":["CTranslate2"]} + +- /get/data/selectable_language_list — data: null — 選択可能な言語一覧(言語コード, country 等を含むデータ構造) + - 例: {"status":200, "endpoint":"/get/data/selectable_language_list","result":[{"language":"English","country":"US"},{"language":"Japanese","country":"JP"}]} + +- /get/data/transcription_engines — data: null — 利用可能な転写エンジン一覧 + - 例: {"status":200, "endpoint":"/get/data/transcription_engines","result":["Google","Whisper"]} + + +- /run/send_message_box — data: {"id": <任意>, "message": "..."} + - 内部で `Controller.chatMessage` を呼び出します。戻りは変換済メッセージ構造体。 + - リクエスト例: + ```json + {"endpoint":"/run/send_message_box","data":{"id":123,"message":"Hello"}} + ``` + - 成功応答例: + ```json + {"status":200,"endpoint":"/run/send_message_box","result":{"id":123,"original":{"message":"Hello","transliteration":[]},"translations":[{"message":"","transliteration":[]}]}} + ``` + +- /run/typing_message_box — data: null — OSC でタイピング状態を伝える場合に使用。成功例: {"status":200,...} +- /run/stop_typing_message_box — data: null — 停止。 + +- /run/send_text_overlay — data: object — オーバーレイに表示するテキストを更新します。例: {"text":"Hello","lang":"English"} + - 成功応答は送信した data をそのまま返すことが多い。 + +- /run/swap_your_language_and_target_language — data: null — 選択中の入出力言語を入れ替えます。成功例: {"status":200, ...} + + +/run/update_software — data: null — 非同期でアップデート処理を開始します。成功応答: {"status":200, "result": true} +/run/update_cuda_software — data: null — CUDA アップデートを開始します。 + + +/set/enable/transcription_receive — data: null — スピーカー側の転写(受信)を有効化 +/set/disable/transcription_receive — data: null — 無効化 + + +/set/enable/foreground — data: null — フォアグラウンド表示を有効化 +/set/disable/foreground — data: null — 無効化 + +- /get/data/selected_tab_no — data: null — 現在のタブ番号を返す +- /set/data/selected_tab_no — data: int — タブ番号を設定 + +- /get/data/translation_engines — data: null — 使える翻訳エンジン一覧を返す + +- /get/data/selected_translation_engines — data: null — 各タブで選択されている翻訳エンジン(タブ別辞書) + - 例: {"status":200, "endpoint":"/get/data/selected_translation_engines","result":{"0":["CTranslate2"],"1":["CTranslate2"]}} + +- /get/data/selected_your_languages — data: null — 各タブの入力言語設定 + - 例: {"status":200, "endpoint":"/get/data/selected_your_languages","result":{"0":{"language":"English","enable":true}}} + +- /get/data/selected_target_languages — data: null — 各タブの出力言語設定 + - 例: {"status":200, "endpoint":"/get/data/selected_target_languages","result":{"0":{"1":{"language":"Japanese","enable":true}}}} + +- /get/data/selected_transcription_engine — data: null — 現在選択されている転写エンジン + - 例: {"status":200, "endpoint":"/get/data/selected_transcription_engine","result":"Whisper"} + +- /run/send_message_box — data: {"id":..., "message": "..."} — チャット送信を実行(chatMessage を内部呼び出し) +- /run/typing_message_box — data: null — タイピング開始通知(OSC 経由で送信される場合あり) +- /run/stop_typing_message_box — data: null — タイピング停止 + +- /run/send_text_overlay — data: {text settings...} — オーバーレイ用のテキスト表示を更新 + +- /run/swap_your_language_and_target_language — data: null — 入出力言語を入れ替え + +- /run/update_software — data: null — ソフト更新処理をスレッドで開始 +- /run/update_cuda_software — data: null — CUDA 関連更新を開始 + +2) 表示・外観設定 +- /get/data/version — data: null — アプリ版を返す +- /get/data/transparency — data: null — 透過率 +- /set/data/transparency — data: int — 透過率を設定 +- /get/data/ui_scaling — data: null — UI スケール +- /set/data/ui_scaling — data: int +- /get/data/textbox_ui_scaling, /set/data/textbox_ui_scaling +- /get/data/message_box_ratio, /set/data/message_box_ratio +- /get/data/send_message_button_type, /set/data/send_message_button_type +- /get/data/show_resend_button, /set/enable/show_resend_button, /set/disable/show_resend_button +- /get/data/font_family, /set/data/font_family +- /get/data/ui_language, /set/data/ui_language +- /get/data/main_window_geometry, /set/data/main_window_geometry + +3) 計算デバイス関連 +- /get/data/compute_mode — data: null — compute mode +- /get/data/translation_compute_device_list — data: null — 選択可能な翻訳デバイス一覧 +- /get/data/selected_translation_compute_device — data: null +- /set/data/selected_translation_compute_device — data: device descriptor — 選択 +- /get/data/transcription_compute_device_list — same as translation +- /get/data/selected_transcription_compute_device, /set/data/selected_transcription_compute_device + +4) 翻訳設定 +- /get/data/selectable_ctranslate2_weight_type_dict — data: null — 利用可能な ctranslate2 重みの辞書 +- /get/data/ctranslate2_weight_type, /set/data/ctranslate2_weight_type +- /get/data/selected_translation_compute_type, /set/data/selected_translation_compute_type +- /run/download_ctranslate2_weight — data: "weight_type" — 指定した重みをダウンロード(非同期可) +- /get/data/deepl_auth_key — data: null — DeepL API キー(存在すれば返却、セキュリティ上の注意あり) +- /set/data/deepl_auth_key — data: "" — DeepL キーを設定(キー検証あり) +- /delete/data/deepl_auth_key — data: null — DeepL キーを削除 + +- /set/data/selected_translation_engines — data: dict/list — 各タブの翻訳エンジン選択を設定します。 + - 例: {"endpoint":"/set/data/selected_translation_engines","data":{"0":["CTranslate2"]}} + +- /set/data/selected_transcription_engine — data: string — 現在の転写エンジンを設定します。 + - 例: {"endpoint":"/set/data/selected_transcription_engine","data":"Whisper"} + +- /set/enable/main_window_sidebar_compact_mode — data: null — サイドバーをコンパクト表示に設定 + - 例: {"status":200,"endpoint":"/set/enable/main_window_sidebar_compact_mode","result": true} + +- /set/disable/main_window_sidebar_compact_mode — data: null — サイドバーのコンパクト表示を解除 + - 例: {"status":200,"endpoint":"/set/disable/main_window_sidebar_compact_mode","result": false} +- /get/data/convert_message_to_romaji, /set/enable/convert_message_to_romaji, /set/disable/convert_message_to_romaji +- /get/data/convert_message_to_hiragana, /set/enable/convert_message_to_hiragana, /set/disable/convert_message_to_hiragana + +5) トランスクリプション / デバイス +- /get/data/mic_host_list, /get/data/mic_device_list, /get/data/speaker_device_list +- /get/data/auto_mic_select, /set/enable/auto_mic_select, /set/disable/auto_mic_select +- /get/data/selected_mic_host, /set/data/selected_mic_host +- /get/data/selected_mic_device, /set/data/selected_mic_device +- /get/data/mic_threshold, /set/data/mic_threshold +- /get/data/mic_automatic_threshold, /set/enable/mic_automatic_threshold, /set/disable/mic_automatic_threshold +- /get/data/mic_record_timeout, /set/data/mic_record_timeout +- /get/data/mic_phrase_timeout, /set/data/mic_phrase_timeout +- /get/data/mic_max_phrases, /set/data/mic_max_phrases +- /get/data/hotkeys, /set/data/hotkeys +- /get/data/plugins_status, /set/data/plugins_status +- /get/data/mic_avg_logprob, /set/data/mic_avg_logprob +- /get/data/mic_no_speech_prob, /set/data/mic_no_speech_prob +- /set/enable/check_mic_threshold, /set/disable/check_mic_threshold +- /get/data/mic_word_filter, /set/data/mic_word_filter + +6) スピーカー側設定 +- /get/data/auto_speaker_select, /set/enable/auto_speaker_select, /set/disable/auto_speaker_select +- /get/data/selected_speaker_device, /set/data/selected_speaker_device +- /get/data/speaker_threshold, /set/data/speaker_threshold +- /get/data/speaker_automatic_threshold, /set/enable/speaker_automatic_threshold, /set/disable/speaker_automatic_threshold +- /get/data/speaker_record_timeout, /set/data/speaker_record_timeout +- /get/data/speaker_phrase_timeout, /set/data/speaker_phrase_timeout +- /get/data/speaker_max_phrases, /set/data/speaker_max_phrases +- /get/data/speaker_avg_logprob, /set/data/speaker_avg_logprob +- /get/data/speaker_no_speech_prob, /set/data/speaker_no_speech_prob +- /set/enable/check_speaker_threshold, /set/disable/check_speaker_threshold + +7) Whisper / トランスクリプション重み +- /get/data/selectable_whisper_weight_type_dict +- /get/data/whisper_weight_type, /set/data/whisper_weight_type +- /get/data/selected_transcription_compute_type, /set/data/selected_transcription_compute_type +- /run/download_whisper_weight — data: "weight_type" + +8) VR / オーバーレイ +- /get/data/overlay_small_log, /set/enable/overlay_small_log, /set/disable/overlay_small_log +- /get/data/overlay_small_log_settings, /set/data/overlay_small_log_settings +- /get/data/overlay_large_log, /set/enable/overlay_large_log, /set/disable/overlay_large_log +- /get/data/overlay_large_log_settings, /set/data/overlay_large_log_settings +- /get/data/overlay_show_only_translated_messages, /set/enable/overlay_show_only_translated_messages, /set/disable/overlay_show_only_translated_messages + +9) その他設定 +- /get/data/send_message_format_parts, /set/data/send_message_format_parts +- /get/data/received_message_format_parts, /set/data/received_message_format_parts +- /get/data/auto_clear_message_box, /set/enable/auto_clear_message_box, /set/disable/auto_clear_message_box +- /get/data/send_only_translated_messages, /set/enable/send_only_translated_messages, /set/disable/send_only_translated_messages +- /get/data/logger_feature, /set/enable/logger_feature, /set/disable/logger_feature +- /run/open_filepath_logs +- /get/data/vrc_mic_mute_sync, /set/enable/vrc_mic_mute_sync, /set/disable/vrc_mic_mute_sync +- /get/data/send_message_to_vrc, /set/enable/send_message_to_vrc, /set/disable/send_message_to_vrc +- /get/data/send_received_message_to_vrc, /set/enable/send_received_message_to_vrc, /set/disable/send_received_message_to_vrc + +10) WebSocket +- /get/data/websocket_host, /set/data/websocket_host +- /get/data/websocket_port, /set/data/websocket_port +- /get/data/websocket_server, /set/enable/websocket_server, /set/disable/websocket_server + +11) OSC / 高度設定 +- /get/data/osc_ip_address, /set/data/osc_ip_address +- /get/data/osc_port, /set/data/osc_port +- /get/data/notification_vrc_sfx, /set/enable/notification_vrc_sfx, /set/disable/notification_vrc_sfx +- /run/open_filepath_config_file +- /run/feed_watchdog + +挙動メモ / 注意点 +- `data` は受信時に `encodeBase64` が適用される場合があります(バイナリや特殊文字対策)。 +- いくつかのエンドポイントは内部的にバックグラウンドスレッドを立ち上げます(ダウンロード・更新処理・transliteration 等)。 +- 翻訳・転写関連は VRAM OOM を検知すると自動的に関連機能を無効化し、UI に 400 系の run イベントを送信します。API 消費者はこれらの run イベントを監視する必要があります。 + +次の作業 +- `docs/modules/controller.md` に記載した Controller のメソッド詳細と紐付けて、各エンドポイントごとに具体的な request/response のサンプル(body の構造)を追加します。 +### API / メッセージマッピング(詳細) + +このアプリは stdin/stdout を通じた 1 行 JSON メッセージで制御します。内部では `mainloop.py` の `mapping` が受信 endpoint を Controller のメソッドに結び付け、`run_mapping` が非同期通知のエンドポイントを定義します。 + +受信メッセージ(stdin) +```json +{ "endpoint": "/set/data/selected_tab_no", "data": 0 } +``` + +送信メッセージ(stdout) +- 成功: printResponse が次を出力します。 +```json +{ "status": 200, "endpoint": "/get/data/version", "result": "3.2.2" } +``` +- エラー: +```json +{ "status": 400, "endpoint": "/set/data/osc_ip_address", "result": {"message":"Invalid IP address","data":"127.0.0.1"} } +``` + +動作原則 +- `/get/data/*` : Controller の getter を呼び、設定やリストを返す。 +- `/set/data/*` : Controller の setter を呼び、設定を変更して新値を返す。 +- `/run/*` : 非同期アクションや UI ボタンが実行する処理(ダウンロード、更新、送信など)。 +- `mapping` の `"status": False` はロック(423 を返し、要求はキューに戻され再試行される)。 + +表記ルール +- Controller メソッドは `Controller.` の形式で明記。 +- `run events` は Controller が UI に通知する `run_mapping` の `/run/...` エンドポイント名を列挙します。 + +以下は `mainloop.py` の `mapping` に基づいた、主要エンドポイントの詳細(カテゴリ順)。 + +1) メイン操作(チャット/翻訳/転写) + +- Endpoint: `/set/enable/translation` + - Controller: `Controller.setEnableTranslation` + - data: null + - success: {status:200, result: true} + - error example: {status:400, result:{message:"Translation disabled due to VRAM overflow", data: False}} + - run events: `/run/enable_translation` を発行して UI に状態を通知する。 + +- Endpoint: `/set/disable/translation` + - Controller: `Controller.setDisableTranslation` + - data: null + - success: {status:200, result: false} + - run events: `/run/enable_translation` + +- Endpoint: `/set/enable/transcription_send` + - Controller: `Controller.setEnableTranscriptionSend` + - data: null + - success: {status:200, result: true} + - side-effect: `Controller.startThreadingTranscriptionSendMessage` を呼びバックグラウンドで音声転写を開始する。 + - run events: `/run/enable_transcription_send` + +- Endpoint: `/set/disable/transcription_send` + - Controller: `Controller.setDisableTranscriptionSend` + - data: null + - success: {status:200, result: false} + +- Endpoint: `/run/send_message_box` + - Controller: `Controller.sendMessageBox` -> 内部で `Controller.chatMessage` + - data: {"id": <任意>, "message": "..."} + - success example: {status:200, result: {"id":123, "original":{...}, "translations":[...]}} + - run events: 転送先言語や翻訳結果があれば `/run/transcription_send_mic_message` などが発行される。 + +- Endpoint: `/run/send_text_overlay` + - Controller: `Controller.sendTextOverlay` + - data: object (例: {"text":"Hello","lang":"English"}) + - success: echo back the data + - side-effect: オーバーレイ更新(small/large に応じた出力) + +2) 表示 / 外観設定 +- Endpoint: `/get/data/version` + - Controller: `Controller.getVersion` + - data: null + - success: {status:200, result: config.VERSION} + +- Endpoint: `/get/data/transparency` / `/set/data/transparency` + - Controller: `Controller.getTransparency` / `Controller.setTransparency` + - data for set: integer (0-255 等、設定側で検証) + - success example: {status:200, result: } + +(UI スケーリング、textbox スケーリング、font_family, ui_language 等の /get と /set は同様のパターン: Controller の getXXX / setXXX を呼ぶ) + +3) 計算デバイス関連 +- Endpoint: `/get/data/translation_compute_device_list` -> `Controller.getComputeDeviceList` + - data: null + - result: list of device descriptors (構造は `config.SELECTABLE_COMPUTE_DEVICE_LIST` に従う) + +- Endpoint: `/set/data/selected_translation_compute_device` + - Controller: `Controller.setSelectedTranslationComputeDevice` + - data: device descriptor (例: {"name":"cuda:0","type":"gpu"}) + - side-effects: `model.setChangedTranslatorParameters(True)` が呼ばれ、実行時にモデル再ロードが必要な場合がある。 + - success: {status:200, result: selected_device} + +4) 翻訳/重み管理 +- Endpoint: `/get/data/selectable_ctranslate2_weight_type_dict` + - Controller: `Controller.getSelectableCtranslate2WeightTypeDict` + - result: dict mapping weight_type -> bool + +- Endpoint: `/run/download_ctranslate2_weight` + - Controller: `Controller.downloadCtranslate2Weight` + - data: "weight_type" (例: "m2m100_418m") + - behavior: 非同期フラグでスレッド起動可能。進捗は run events `/run/download_progress_ctranslate2_weight` を発行。完了時に `/run/downloaded_ctranslate2_weight`。 + +- Endpoint: `/set/data/deepl_auth_key` + - Controller: `Controller.setDeeplAuthKey` + - data: string (API key) + - behavior: 内部で `model.authenticationTranslatorDeepLAuthKey` を実行して検証。失敗時は 400 を返す。 + +5) トランスクリプション / デバイス +- Endpoint: `/get/data/mic_host_list` -> `Controller.getMicHostList` + - data: null + - result: dict/list of hosts + +- Endpoint: `/set/data/selected_mic_host` -> `Controller.setSelectedMicHost` + - data: host identifier (string) + - side-effects: デフォルトデバイスを `model.getMicDefaultDevice()` で選択し、エネルギーチェックや転写スレッドの再起動が発生する場合がある。 + +- Endpoint: `/set/data/mic_threshold` -> `Controller.setMicThreshold` + - data: integer + - validation: 0 <= value <= config.MAX_MIC_THRESHOLD + - success: {status:200, result: new_value} error: 400 with message and old value + +6) スピーカー関連(受信) +- Endpoint: `/set/data/selected_speaker_device` -> `Controller.setSelectedSpeakerDevice` + - data: device descriptor + - side-effects: スピーカー転写スレッド(ENABLE_CHECK_ENERGY_RECEIVE)を再起動する可能性あり + +7) Whisper / トランスクリプション重み +- Endpoint: `/run/download_whisper_weight` + - Controller: `Controller.downloadWhisperWeight` + - data: "weight_type" + - run events: `/run/download_progress_whisper_weight`, `/run/downloaded_whisper_weight` + +8) オーバーレイ / VR +- Endpoint: `/set/enable/overlay_small_log` -> `Controller.setEnableOverlaySmallLog` + - side-effect: `model.startOverlay()` を呼び、`model.updateOverlaySmallLog` で描画が更新される + +9) WebSocket / OSC / Watchdog +- Endpoint: `/set/data/websocket_host` -> `Controller.setWebSocketHost` + - validation: IP 形式チェック (`isValidIpAddress`) + - if WebSocket server running: attempts to restart server on new host/port (checks availability via `isAvailableWebSocketServer`) + +- Endpoint: `/set/data/osc_ip_address` -> `Controller.setOscIpAddress` + - validation: IP 形式。失敗時は 400 を返す。 + +- Endpoint: `/run/feed_watchdog` -> `Controller.feedWatchdog` + - Controller: `Controller.feedWatchdog` ➜ `model.feedWatchdog()` + +共通的な失敗モード(クライアント実装者向けメモ) +- 無効なパラメータ: 400 と {message,data} を返す。 +- ロック: 423 (Locked endpoint) — UI 側はリトライまたはキュー内での再試行を待つ。 +- 内部エラー: 500 とエラーメッセージ(詳細はログ)を返す。 +- VRAM OOM / モデルエラー: Controller は `model.detectVRAMError` を使い、必要に応じて機能無効化と run イベントで通知する。 + +付録: すぐ使える呼び出し例 +- バージョン取得 +```json +{ "endpoint": "/get/data/version", "data": null } +``` + +- タブ切替 +```json +{ "endpoint": "/set/data/selected_tab_no", "data": 1 } +``` + +- メッセージ送信(チャット) +```json +{ "endpoint": "/run/send_message_box", "data": {"id": 555, "message": "Hello world"} } +``` + +次の作業 +- ① `docs/modules/controller.md` の各メソッドとこの `docs/api.md` を突き合わせ、未記載の `run_mapping` イベントのペイロード例を追加します。 +- ② 軽い品質ゲート(README と runtime 注意の草案作成)を実行します。 + +## エンドポイント別 JSON スキーマ(補完) + +このセクションでは `mainloop.py` の `mapping` に定義された全エンドポイントをパターンごとに整理し、クライアントが送信すべき `request` と期待される `response` の JSON スキーマを明示します。多数のエンドポイントは共通パターンに従うため、パターン定義と代表例でほとんどのケースをカバーしています。 + +共通ルール +- リクエストは必ず 1 行 JSON: {"endpoint": "", "data": }。 +- レスポンスは {"status": , "endpoint": "", "result": } の形式(内部の `printResponse` により出力)。 + +1) /get/data/* パターン(読み取り) +- request.data: null +- response.result: 直ちに返せる JSON 値(数値/文字列/配列/辞書) +- schema(JSON Schema 風の簡易表記): + + request: + { + "endpoint": "/get/data/", + "data": null + } + + response: + { + "status": 200, + "endpoint": "/get/data/", + "result": + } + + 代表例: + - `/get/data/version` → result: string + {"status":200,"endpoint":"/get/data/version","result":"3.2.2"} + - `/get/data/mic_device_list` → result: ["Device 1", "Device 2"] + +2) /set/data/* パターン(書き込み) +- request.data: セッタが期待する型(下に代表的な型を列挙) +- response.result: 新しい値または検証済の値(成功時) +- error: バリデーション失敗時は status 400 と {message,data} + + 共通 request/response: + + request: + { + "endpoint": "/set/data/", + "data": + } + + response (success): + { + "status":200, + "endpoint":"/set/data/", + "result": + } + + response (validation error): + { + "status":400, + "endpoint":"/set/data/", + "result": {"message": "", "data": } + } + + 代表的リクエスト型一覧(多くはこの型いずれか): + - int: `/set/data/selected_tab_no`, `/set/data/transparency`, `/set/data/mic_threshold` など + - string: `/set/data/selected_mic_host`, `/set/data/selected_speaker_device`, `/set/data/deepl_auth_key` など + - dict/object: `/set/data/selected_your_languages`, `/set/data/selected_target_languages`, `/set/data/send_message_format_parts` など + - list: `/set/data/mic_word_filter` など + +3) フラグ切替(enable / disable) + +- 概要: 機能の有効化/無効化を行うエンドポイント群は、実装で定義された具体的なエンドポイント名(例: `/set/enable/translation`, `/set/disable/translation`, `/set/enable/foreground` など)で提供されています。本ドキュメントでは umbrella 的な汎用トークン(`/set/enable` や `/set/disable` 単体)は記載せず、実際に実装で定義されている concrete エンドポイントのみを列挙しています。 + +- 振る舞いの要点: + - リクエストの `data` は通常 `null` です。 + - 成功応答は多くの場合 boolean を返します(例: `{ "status":200, "endpoint":"/set/enable/foreground", "result": true }`)。 + - 条件により有効化/無効化ができない場合は 400 を返し、`{ "message": "...", "data": }` の形で詳細が返されます。 + +具体的なフラグ切替エンドポイントはドキュメント本文の各該当箇所で個別に列挙しています(例: `/set/enable/translation`, `/set/disable/translation`, `/set/enable/transcription_send`, `/set/disable/transcription_send`, `/set/enable/main_window_sidebar_compact_mode`, など)。 + +4) /run/*(アクション・実行系) +- request.data: アクションに依存(例: `/run/send_message_box` は {id, message}) +- response.result: 多くは action の結果(True/False, object)を返す +- 非同期で UI 更新を行う場合は `Controller.run(...)` により `/run/...` 形式の通知が stdout に出力される + + 代表例: + - `/run/send_message_box` + request.data: {"id": , "message": ""} + response.result: { + "id": , + "original": {"message": "", "transliteration": [] }, + "translations": [ {"message":"", "transliteration":[...]}, ... ] + } + + - `/run/download_ctranslate2_weight` + request.data: "" (string) + response.result: true + progress: `/run/download_progress_ctranslate2_weight` -> {"weight_type":"...","progress":0.0..1.0} + complete: `/run/downloaded_ctranslate2_weight` -> "" + +5) WebSocket / OSC / Watchdog 関連 +- `/set/data/websocket_host` : request.data:string(host) → response: {status:200, result: host} または 400 (not available) +- `/set/data/osc_ip_address` : request.data:string(ip) → validation via `isValidIpAddress` → 400 on invalid +- `/run/feed_watchdog`: request.data:null → response: {status:200,result:true} + +6) エラー応答の標準形 +- Validation / domain error : status 400, result: {"message": "<説明>", "data": } +- Locked endpoint: status 423, result: "Locked endpoint"(mainloop が再試行のためキューに戻す) +- Internal error: status 500, result: "" + +7) run events(UI 更新通知)- 参考(主要イベントのみ再掲) +- `/run/connected_network` : bool +- `/run/enable_ai_models` : bool +- `/run/initialization_progress` : int (1..4) + - `/run/transcription_send_mic_message` / `/run/transcription_receive_speaker_message` : オブジェクト(original/translations, see above) + +追加の run イベント(ランタイム検証で未記載と判定されたため追記): + +- `/run/enable_transcription_receive` : bool + - 説明: スピーカー側転写(transcription receive)の有効/無効を UI に通知します。 + +- `/run/transcription_send_mic_message` : object + - payload: 同 `/run/transcription_send_mic_message` の構造(original + translations) + - 説明: マイク側で転写結果が生成され、UI に送信するための通知です。 + +- `/run/transcription_receive_speaker_message` : object + - payload: 同 `/run/transcription_receive_speaker_message` の構造 + - 説明: スピーカー側で転写結果が生成されたときに発行されます。 + +- `/run/error_transcription_mic_vram_overflow` : object (400) + - payload: {"message": "VRAM out of memory during mic transcription", "data": ""} + - 説明: マイク転写中に VRAM OOM が発生した際に通知します。 + +- `/run/error_transcription_speaker_vram_overflow` : object (400) + - payload: {"message": "VRAM out of memory during speaker transcription", "data": ""} + - 説明: スピーカー転写中に VRAM OOM が発生した際に通知します。 + +補遺: 全エンドポイント一覧と期待型の速見表 +- `/get/data/*` : data=null -> result: primitive|array|object +- `/set/data/*` : data: 型指定 (int|string|dict|list) -> result: new value or validation error +- `/set/enable/*` `/set/disable/*` : data=null -> result: bool +- `/run/*` : data: action-specific -> result: action result object / bool + +ファイルの更新履歴 +- このドキュメントは `mainloop.py` の `mapping` と `controller.py` の `run_mapping` を参照して作成しました。将来的にエンドポイントを追加した場合は同じ箇所を参照して本ドキュメントを更新してください。 + +---- + +完了: エンドポイント別スキーマの補完を行いました。次は軽い品質ゲート(lint/typecheck)の実行を提案します。 + diff --git a/src-python/docs/architecture.md b/src-python/docs/architecture.md new file mode 100644 index 00000000..8fcf20e9 --- /dev/null +++ b/src-python/docs/architecture.md @@ -0,0 +1,21 @@ +# アーキテクチャ概観 + +VRCT(src-python)は、ローカル音声キャプチャ・音声認識・翻訳・VR 表示・OSC/ WebSocket 連携を統合するアプリケーションです。主な責務は次の通り。 + +- device_manager: オーディオ入出力デバイスの発見、監視、コールバック通知。 +- transcription (models/transcription/*): マイク/スピーカーからの音声取得、認識(Google/Whisper)、議事録管理。 +- translation (models/translation/*): 翻訳エンジン(DeepL/API、CTranslate2、Google など)管理と実行。 +- overlay (models/overlay/*): VR オーバーレイの画像生成と OpenVR を使った描画管理。 +- osc (models/osc/osc.py): VRChat 等との OSC(および OSCQuery)でのやり取り。 +- websocket (models/websocket/*): 外部クライアント向け WebSocket ブロードキャスト。 +- model.py: 高レベルなファサード。各機能のインスタンス化とランタイム操作。 +- controller.py: UI/外部メッセージを受け、config を更新・機能を起動するコマンド実行層。 +- mainloop.py: stdin 経由のコマンド受付ループとマッピング定義。GUI からの操作を受ける想定。 +- utils.py: ロギング、ネットワークチェック、デバイス/計算デバイスタイプ判定などのユーティリティ。 +- config.py: シングルトン設定ストア。アプリ起動中に共有して使うすべての設定値。 + +設計上のポイント: +- シングルトン/ファサード: `model` と `config` はシングルトンでグローバルに参照される。これにより UI 層(Controller)と低レイヤ(models/*)の橋渡しを行う。 +- 非同期処理: デバイス監視、音声録音・認識、WebSocket サーバー、Overlay のループはそれぞれ別スレッド/非同期ループで実行される。 +- フォールバック: 翻訳はまず選択されたエンジンを使い、失敗時に CTranslate2 にフォールバックする仕組みがある。 +- VRAM エラー検出: Whisper / CTranslate2 等で VRAM 不足が起きた場合、特殊なエラー検出を行い翻訳/音声機能を無効化して回復を試みる。 diff --git a/src-python/docs/diagrams.md b/src-python/docs/diagrams.md new file mode 100644 index 00000000..71b42975 --- /dev/null +++ b/src-python/docs/diagrams.md @@ -0,0 +1,51 @@ +# システム図 + +以下はシステム構成の概要(Mermaid シーケンス図とテキスト版の両方)です。Mermaid がサポートされているビューアでは下のシーケンス図が描画されます。 + +```mermaid +sequenceDiagram + participant GUI as GUI (stdin/stdout) + participant Main as mainloop + participant Controller as Controller + participant Model as Model + participant Recorder as Recorder + participant Transcriber as Transcriber + participant Translator as Translator + participant Overlay as Overlay + participant OSC as OSC + participant WS as WebSocket + + GUI->>Main: send JSON endpoint + Main->>Controller: dispatch + Controller->>Model: startMicTranscript(callback) + Recorder->>Transcriber: audio data + Transcriber->>Controller: result (text, language) + Controller->>Translator: getInputTranslate(text) + Translator-->>Controller: translations + Controller->>Overlay: updateOverlay(translation) + Controller->>OSC: sendMessage(osc_message) + Controller->>WS: websocketSendMessage(event) + Controller-->>GUI: run(status, endpoint, result) +``` + +## テキスト版(簡易) + +Main process (`mainloop.py`) + - stdin -> JSON コマンド -> Main.receiver -> queue + - Main.handler -> Controller (コマンド実行) + - run(status, endpoint, result) -> stdout (GUI に通知) + +Controller + - config (読み書き) + - model (起動/停止/アクション) + +Model サブシステム + - device_manager (デバイス列挙/監視) + - transcription (recorder -> transcriber) + - translation (Translator) + - overlay (OverlayImage -> Overlay) + - osc (OSCHandler) + - websocket (WebSocketServer) + +データフロー(代表): 録音 -> audio_queue -> AudioTranscriber -> Controller.micMessage -> Translator -> (OSC / Overlay / WebSocket / ログ) + diff --git a/src-python/docs/modules/config.md b/src-python/docs/modules/config.md new file mode 100644 index 00000000..d33e7b42 --- /dev/null +++ b/src-python/docs/modules/config.md @@ -0,0 +1,203 @@ +# config.py クラス仕様書 + +目的: アプリケーションの全設定を集中管理するシングルトン `config`(クラス名: `Config`、インスタンス: `config`)。 + +特徴: +- JSON シリアライズ対象のプロパティには `@json_serializable('KEY_NAME')` デコレータが付いており、`load_config()` / `saveConfig()` によって `config.json` に永続化されます。 +- プロパティは「読み取り専用 (Read Only)」と「読み書き (Read/Write)」に分類されます。読み書き可能なプロパティはバリデーション処理とともに setter が用意されています。 +- 設定は内部的に `_config_data` に保持され、`saveConfig()` はデバウンス(2秒)でファイルへ書き込みます。即時書き込みオプションも可能です(saveConfig(..., immediate_save=True))。 + +## 生成とライフサイクル +- `Config()` はシングルトン(__new__ で単一インスタンスを生成)。 +- `init_config()` でデフォルト値を初期化し、その後 `load_config()` が `config.json` を読み込んで既存値を適用します。 + +## 主要プロパティ一覧(型・デフォルト・説明) + +注: 下は `config.py` の初期化ロジックに基づく抜粋です。`json_serializable` が付与されたキーは `config.json` に書き出されます。 + +- Read only + - `VERSION` (str) = "3.2.2" + - `PATH_LOCAL` (str) = フォロー実行ファイルのディレクトリか、ソースの __file__ のディレクトリ + - `PATH_CONFIG` (str) = PATH_LOCAL/config.json + - `PATH_LOGS` (str) = PATH_LOCAL/logs + - `GITHUB_URL`, `UPDATER_URL`, `BOOTH_URL`, `DOCUMENTS_URL`, `DEEPL_AUTH_KEY_PAGE_URL` (str) + - `MAX_MIC_THRESHOLD` (int) = 2000 + - `MAX_SPEAKER_THRESHOLD` (int) = 4000 + - `WATCHDOG_TIMEOUT` (int) = 60 + - `WATCHDOG_INTERVAL` (int) = 20 + - `SELECTABLE_*` 系: 各種選択肢のリスト/イテレータ(モデルの重みや言語、UI 言語等)。 + +- Read/Write(主な項目) + - `SEND_MESSAGE_FORMAT_PARTS` (dict) = デフォルトで message/translation/translation_first 等を含むフォーマット定義。json_serializable キー: 'SEND_MESSAGE_FORMAT_PARTS' + - `RECEIVED_MESSAGE_FORMAT_PARTS` (dict) + - `ENABLE_TRANSLATION` (bool) = False + - `ENABLE_TRANSCRIPTION_SEND` (bool) = False + - `ENABLE_TRANSCRIPTION_RECEIVE` (bool) = False + - `ENABLE_FOREGROUND` (bool) = False + - `ENABLE_CHECK_ENERGY_SEND` (bool) = False + - `ENABLE_CHECK_ENERGY_RECEIVE` (bool) = False + - `SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_DICT` (dict) = {: False, ...} + - `SELECTABLE_WHISPER_WEIGHT_TYPE_DICT` (dict) + - `SELECTABLE_TRANSLATION_ENGINE_STATUS` (dict) + - `SELECTABLE_TRANSCRIPTION_ENGINE_STATUS` (dict) + - `SELECTED_TAB_NO` (str) = "1" (json_serializable: 'SELECTED_TAB_NO') + - `SELECTED_TRANSLATION_ENGINES` (dict) = tab毎に選択 ('CTranslate2' 等) + - `SELECTED_YOUR_LANGUAGES`, `SELECTED_TARGET_LANGUAGES` (dict) = 翻訳元/先の選択と有効フラグ + - `SELECTED_TRANSCRIPTION_ENGINE` (str) = 'Google' + - `CONVERT_MESSAGE_TO_ROMAJI` / `CONVERT_MESSAGE_TO_HIRAGANA` (bool) + - UI 設定: `TRANSPARENCY` (int), `UI_SCALING` (int), `TEXTBOX_UI_SCALING` (int), `MESSAGE_BOX_RATIO` (int) + - `SEND_MESSAGE_BUTTON_TYPE` (str) = 'show'(候補は SEND_MESSAGE_BUTTON_TYPE_LIST) + - `SHOW_RESEND_BUTTON` (bool) + - `FONT_FAMILY` (str) = 'Yu Gothic UI' + - `UI_LANGUAGE` (str) = 'en'(候補は SELECTABLE_UI_LANGUAGE_LIST) + - `MAIN_WINDOW_GEOMETRY` (dict) = {x_pos, y_pos, width, height} + - マイク/スピーカー関係: `AUTO_MIC_SELECT`, `SELECTED_MIC_HOST`, `SELECTED_MIC_DEVICE`, `MIC_THRESHOLD`, `MIC_AUTOMATIC_THRESHOLD`, `MIC_RECORD_TIMEOUT`, `MIC_PHRASE_TIMEOUT`, `MIC_MAX_PHRASES`, `MIC_WORD_FILTER`, `HOTKEYS` 等 + - `PLUGINS_STATUS` (list) + - マイク転写確度閾値: `MIC_AVG_LOGPROB`, `MIC_NO_SPEECH_PROB` + - スピーカー関連(同様の項目): `AUTO_SPEAKER_SELECT`, `SELECTED_SPEAKER_DEVICE`, `SPEAKER_THRESHOLD`, ... + - `OSC_IP_ADDRESS` (str) = '127.0.0.1' + - `OSC_PORT` (int) = 9000 + - `AUTH_KEYS` (dict) = {'DeepL_API': None} + - `USE_EXCLUDE_WORDS` (bool) = True + - 計算デバイス選択: `SELECTED_TRANSLATION_COMPUTE_DEVICE` / `SELECTED_TRANSCRIPTION_COMPUTE_DEVICE`(`getComputeDeviceList()` に基づくデバイス辞書) + - 重み/計算タイプ: `CTRANSLATE2_WEIGHT_TYPE`, `WHISPER_WEIGHT_TYPE`, `SELECTED_TRANSLATION_COMPUTE_TYPE`, `SELECTED_TRANSCRIPTION_COMPUTE_TYPE` + - オーバーレイ設定: `OVERLAY_SMALL_LOG`, `OVERLAY_SMALL_LOG_SETTINGS`, `OVERLAY_LARGE_LOG`, `OVERLAY_LARGE_LOG_SETTINGS`, `OVERLAY_SHOW_ONLY_TRANSLATED_MESSAGES` 等 + - VRC/ログ/WebSocket: `SEND_MESSAGE_TO_VRC`, `SEND_RECEIVED_MESSAGE_TO_VRC`, `LOGGER_FEATURE`, `VRC_MIC_MUTE_SYNC`, `NOTIFICATION_VRC_SFX`, `WEBSOCKET_SERVER`, `WEBSOCKET_HOST`, `WEBSOCKET_PORT` + +# config.py — 完全上書きドキュメント + +目的: アプリケーションの全設定を集中管理するシングルトン `config`(クラス名: `Config`、インスタンス: `config`)。 + +特徴: +- JSON シリアライズ対象のプロパティには `@json_serializable('KEY_NAME')` デコレータが付いており、`load_config()` / `saveConfig()` によって `config.json` に永続化されます。 +- プロパティは「読み取り専用 (Read Only)」と「読み書き (Read/Write)」に分類されます。読み書き可能なプロパティはバリデーション処理とともに setter が用意されています。 +- 設定は内部的に `_config_data` に保持され、`saveConfig()` はデバウンス(2秒)でファイルへ書き込みます。即時書き込みオプションも可能です(saveConfig(..., immediate_save=True))。 + +## 生成とライフサイクル +- `Config()` はシングルトン(__new__ で単一インスタンスを生成)。 +- `init_config()` でデフォルト値を初期化し、その後 `load_config()` が `config.json` を読み込んで既存値を適用します。 + +## 主要プロパティ一覧(型・デフォルト・説明) + +注: 下は `config.py` の初期化ロジックに基づく抜粋です。`json_serializable` が付与されたキーは `config.json` に書き出されます。 + +- Read only + - `VERSION` (str) = "3.2.2" + - `PATH_LOCAL` (str) = フォロー実行ファイルのディレクトリか、ソースの __file__ のディレクトリ + - `PATH_CONFIG` (str) = PATH_LOCAL/config.json + - `PATH_LOGS` (str) = PATH_LOCAL/logs + - `GITHUB_URL`, `UPDATER_URL`, `BOOTH_URL`, `DOCUMENTS_URL`, `DEEPL_AUTH_KEY_PAGE_URL` (str) + - `MAX_MIC_THRESHOLD` (int) = 2000 + - `MAX_SPEAKER_THRESHOLD` (int) = 4000 + - `WATCHDOG_TIMEOUT` (int) = 60 + - `WATCHDOG_INTERVAL` (int) = 20 + - `SELECTABLE_*` 系: 各種選択肢のリスト/イテレータ(モデルの重みや言語、UI 言語等)。 + +- Read/Write(主な項目) + - `SEND_MESSAGE_FORMAT_PARTS` (dict) = デフォルトで message/translation/translation_first 等を含むフォーマット定義。json_serializable キー: 'SEND_MESSAGE_FORMAT_PARTS' + - `RECEIVED_MESSAGE_FORMAT_PARTS` (dict) + - `ENABLE_TRANSLATION` (bool) = False + - `ENABLE_TRANSCRIPTION_SEND` (bool) = False + - `ENABLE_TRANSCRIPTION_RECEIVE` (bool) = False + - `ENABLE_FOREGROUND` (bool) = False + - `ENABLE_CHECK_ENERGY_SEND` (bool) = False + - `ENABLE_CHECK_ENERGY_RECEIVE` (bool) = False + - `SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_DICT` (dict) = {: False, ...} + - `SELECTABLE_WHISPER_WEIGHT_TYPE_DICT` (dict) + - `SELECTABLE_TRANSLATION_ENGINE_STATUS` (dict) + - `SELECTABLE_TRANSCRIPTION_ENGINE_STATUS` (dict) + - `SELECTED_TAB_NO` (str) = "1" (json_serializable: 'SELECTED_TAB_NO') + - `SELECTED_TRANSLATION_ENGINES` (dict) = tab毎に選択 ('CTranslate2' 等) + - `SELECTED_YOUR_LANGUAGES`, `SELECTED_TARGET_LANGUAGES` (dict) = 翻訳元/先の選択と有効フラグ + - `SELECTED_TRANSCRIPTION_ENGINE` (str) = 'Google' + - `CONVERT_MESSAGE_TO_ROMAJI` / `CONVERT_MESSAGE_TO_HIRAGANA` (bool) + - UI 設定: `TRANSPARENCY` (int), `UI_SCALING` (int), `TEXTBOX_UI_SCALING` (int), `MESSAGE_BOX_RATIO` (int) + - `SEND_MESSAGE_BUTTON_TYPE` (str) = 'show'(候補は SEND_MESSAGE_BUTTON_TYPE_LIST) + - `SHOW_RESEND_BUTTON` (bool) + - `FONT_FAMILY` (str) = 'Yu Gothic UI' + - `UI_LANGUAGE` (str) = 'en'(候補は SELECTABLE_UI_LANGUAGE_LIST) + - `MAIN_WINDOW_GEOMETRY` (dict) = {x_pos, y_pos, width, height} + - マイク/スピーカー関係: `AUTO_MIC_SELECT`, `SELECTED_MIC_HOST`, `SELECTED_MIC_DEVICE`, `MIC_THRESHOLD`, `MIC_AUTOMATIC_THRESHOLD`, `MIC_RECORD_TIMEOUT`, `MIC_PHRASE_TIMEOUT`, `MIC_MAX_PHRASES`, `MIC_WORD_FILTER`, `HOTKEYS` 等 + - `PLUGINS_STATUS` (list) + - マイク転写確度閾値: `MIC_AVG_LOGPROB`, `MIC_NO_SPEECH_PROB` + - スピーカー関連(同様の項目): `AUTO_SPEAKER_SELECT`, `SELECTED_SPEAKER_DEVICE`, `SPEAKER_THRESHOLD`, ... + - `OSC_IP_ADDRESS` (str) = '127.0.0.1' + - `OSC_PORT` (int) = 9000 + - `AUTH_KEYS` (dict) = {'DeepL_API': None} + - `USE_EXCLUDE_WORDS` (bool) = True + - 計算デバイス選択: `SELECTED_TRANSLATION_COMPUTE_DEVICE` / `SELECTED_TRANSCRIPTION_COMPUTE_DEVICE`(`getComputeDeviceList()` に基づくデバイス辞書) + - 重み/計算タイプ: `CTRANSLATE2_WEIGHT_TYPE`, `WHISPER_WEIGHT_TYPE`, `SELECTED_TRANSLATION_COMPUTE_TYPE`, `SELECTED_TRANSCRIPTION_COMPUTE_TYPE` + - オーバーレイ設定: `OVERLAY_SMALL_LOG`, `OVERLAY_SMALL_LOG_SETTINGS`, `OVERLAY_LARGE_LOG`, `OVERLAY_LARGE_LOG_SETTINGS`, `OVERLAY_SHOW_ONLY_TRANSLATED_MESSAGES` 等 + - VRC/ログ/WebSocket: `SEND_MESSAGE_TO_VRC`, `SEND_RECEIVED_MESSAGE_TO_VRC`, `LOGGER_FEATURE`, `VRC_MIC_MUTE_SYNC`, `NOTIFICATION_VRC_SFX`, `WEBSOCKET_SERVER`, `WEBSOCKET_HOST`, `WEBSOCKET_PORT` + +## セッタのバリデーション +- 多くの setter は型チェックと候補値チェック(リストや辞書のキー整合性)を行います。例: + - `SELECTED_MIC_DEVICE` は `device_manager.getMicDevices()` の一覧に存在する名前であること。 + - `SELECTED_TRANSLATION_COMPUTE_TYPE` は `SELECTED_TRANSLATION_COMPUTE_DEVICE['compute_types']` に含まれる文字列であること。 + - UI 関連の集合は `SELECTABLE_UI_LANGUAGE_LIST` などの一覧に従う。 + +## 永続化の詳細 +- `load_config()` は `config.json` が存在し、かつ中身がある場合に読み込みを試み、ファイル中のキーを `setattr(self, key, value)` して既存の setter を利用して適用します。 +- 読み込み後、`json_serializable` 指定された全キーを `_config_data` に書き戻し、ファイルを上書き(常に書く)。 + +## 使い方の例 + +以下は `config` を使った典型的なコード例です。 + +```python +from config import config + +# 値の参照 +print('App version:', config.VERSION) +print('Current UI language:', config.UI_LANGUAGE) + +# 値の更新(setter を通す) +config.UI_LANGUAGE = 'ja' +config.SEND_MESSAGE_TO_VRC = False + +# 複雑な dict を設定する例(メッセージフォーマットを上書き) +config.SEND_MESSAGE_FORMAT_PARTS = { + 'message': {'prefix': '[YOU] ', 'suffix': ''}, + 'separator': '\n', + 'translation': {'prefix': '[TR] ', 'separator': '\n', 'suffix': ''}, + 'translation_first': True, +} + +# 即時保存したい場合(即座に config.json を上書き) +config.saveConfig('CUSTOM_SAVE', {'foo': 'bar'}, immediate_save=True) +``` + +## エッジケース / 注意点 +- `load_config()` はファイル値を setter 経由で当てはめるため、ファイルに古いキーや予期しない型があると setter によって無視されることがあります(例: 言語キーが不正の場合)。 +- `saveConfig()` はデバウンスされるため、高頻度の設定変更では複数の変更がまとめて書き込まれます。即時書き込みが必要な操作(重要な鍵の更新など)は `immediate_save=True` を使ってください。 +- `SELECTABLE_*` 系や `*_DICT` 系は初期化時に外部モジュール(翻訳リソース、whisper_models、device_manager 等)から生成されます。これらが利用できない環境ではデフォルトが空になる可能性があります。 + +## 推奨改善点(将来的なドキュメント/実装) +- 設定スキーマを JSON Schema で定義し、load 時の検証を明確化すると安全性が向上します。 +- 設定変更イベントを発火する仕組み(observer パターン)を導入すると、Controller/Model 側の再初期化処理をより明確に実装できます。 + +--- + +このファイルは `config.py` の実装に基づいて自動生成的に作成されたドキュメント(overwrite)です。実装の微細な差分は `config.py` を参照してください。 + +## 詳細設計 + +目的: アプリケーションの全設定を保持するシングルトン `config`。 + +ポイント: +- JSON シリアライズ可能な設定値には `@json_serializable` デコレータが付与され、save 操作でファイルへ書き出される。 +- 多数のプロパティが定義され、読み取り専用 (Read Only) と 読み書き (Read/Write) が混在する。 +- 設定項目の例: + - ENABLE_TRANSLATION, ENABLE_TRANSCRIPTION_SEND, ENABLE_TRANSCRIPTION_RECEIVE + - SELECTED_MIC_HOST, SELECTED_MIC_DEVICE, SELECTED_SPEAKER_DEVICE + - SELECTED_TRANSLATION_ENGINES, SELECTED_YOUR_LANGUAGES, SELECTED_TARGET_LANGUAGES + - PATH_LOCAL, PATH_LOGS, VERSION, GITHUB_URL, UPDATER_URL + - SELECTABLE_CTRANSLATE2_WEIGHT_TYPE_DICT / SELECTABLE_WHISPER_WEIGHT_TYPE_DICT + - COMPUTE 関連: SELECTABLE_COMPUTE_DEVICE_LIST, SELECTED_TRANSLATION_COMPUTE_DEVICE, SELECTED_TRANSCRIPTION_COMPUTE_DEVICE + +設計上の契約: +- 全ての get/set は辞書形で status/result を返す Controller の呼び出しに合わせて変換される。 +- 外部から設定を変更した際は必要に応じて Model/Controller による再初期化処理を呼ぶ。 + +検討事項: +- 現状は設定変更が即時反映されるが、一部操作は再初期化(モデルロード、デバイス再取得)を要求するため Controller 側で連携している。 diff --git a/src-python/docs/modules/controller.md b/src-python/docs/modules/controller.md new file mode 100644 index 00000000..f2ae57a6 --- /dev/null +++ b/src-python/docs/modules/controller.md @@ -0,0 +1,158 @@ +## Controller クラス仕様書 + +概要 +- `Controller` はアプリケーションのコントロール層(Facade)で、`model` と `device_manager`、および外部 UI / mainloop とを仲介します。 +- UI からのコマンドを受け取り、`model` の開始/停止、設定の変更、ダウンロードの開始、各種フラグの切り替え、進捗通知(`run` コールバック経由)を行います。 +- 多くのメソッドは JSON 系の応答オブジェクトを返します: {"status": int, "result": Any}。副作用で `self.run(status, run_mapping[key], payload)` を呼び出して UI に通知します。 + +初期化とランタイムフック +- __init__() -> None + - フィールド: `init_mapping: dict`, `run_mapping: dict`, `run: Callable`, `device_access_status: bool` + - `setInitMapping(init_mapping: dict)` / `setRunMapping(run_mapping: dict)` / `setRun(run: Callable)` で mainloop からマッピング・コールバックを注入されることを想定。 + +コールバック通知用メソッド(UI への通知) +- connectedNetwork() / disconnectedNetwork() -> None +- enableAiModels() / disableAiModels() -> None +- updateMicHostList() / updateMicDeviceList() / updateSpeakerDeviceList() -> None +- updateConfigSettings() -> None + - これらは `self.run(status, run_mapping[key], payload)` を使って UI にイベントを送ります。 + +ダウンロード用ヘルパークラス +- class DownloadCTranslate2(run_mapping: dict, weight_type: str, run: Callable) + - progressBar(progress: float) -> None + - downloaded() -> None +- class DownloadWhisper(run_mapping: dict, weight_type: str, run: Callable) + - progressBar(progress: float) -> None + - downloaded() -> None + +音声・翻訳イベントハンドラ +- micMessage(result: dict) -> None + - 引数: result: {"text": str|False, "language": str} + - 挙動: ワードフィルタ、繰り返し検出、翻訳(`model.getInputTranslate`)、音声送信(OSC)・オーバーレイ更新・WebSocket ブロードキャスト等を行う。 + - エラー: 翻訳中に VRAM OOM が起きた場合は model.detectVRAMError を使って検出し、翻訳機能を無効化して UI に 400 を通知。 + +- speakerMessage(result: dict) -> None + - 引数: result: {"text": str|False, "language": str} + - micMessage と同様だが、受信(speaker)側のロジックやオーバーレイの扱いが異なる。 + +- chatMessage(data: dict) -> dict + - 引数: {"id": Any, "message": str} + - 戻り値: {"status": int, "result": {"id":..., "original":..., "translations":[...]}} + - 挙動詳細: + - 翻訳処理は `model.getInputTranslate` を呼び出します。翻訳処理中に VRAM 関連の例外が発生した場合、`model.detectVRAMError` によって検出し、翻訳機能を自動で無効化します。 + - VRAM エラー検出時は Controller は UI に対して 400 系の run イベントを発行する(例: `error_translation_chat_vram_overflow`, `enable_translation` で無効化通知)。 + - エラー発生時の戻り値: 翻訳を行わずに基本情報を含む 200 応答を返すコードパスがあり、クライアント側でのハンドリングを想定しています。 + +設定取得/変更系メソッド(代表例) +- getVersion() -> {"status":200, "result": config.VERSION} +- getComputeMode() / getComputeDeviceList() / getSelectedTranslationComputeDevice() -> dict +- setSelectedTranslationComputeDevice(device: str) -> {"status":200, "result": device} +- getSelectableCtranslate2WeightTypeDict() -> dict +- setEnableTranslation() / setDisableTranslation() -> dict + - setEnableTranslation はモデルロード時に VRAM エラーを検知するロジックを内包している。 + - 多くの setXXX / getXXX メソッドは config を直接操作して即時反映する。 + +自動デバイス選択 +- applyAutoMicSelect() / applyAutoSpeakerSelect() + - `device_manager` にコールバックを登録して自動選択を有効化する。 + +トランスクリプション制御(スレッドで実行) +- startTranscriptionSendMessage() / stopTranscriptionSendMessage() / startThreadingTranscriptionSendMessage() / stopThreadingTranscriptionSendMessage() +- startTranscriptionReceiveMessage() / stopTranscriptionReceiveMessage() / startThreadingTranscriptionReceiveMessage() / stopThreadingTranscriptionReceiveMessage() + - 実際の処理は `model.startMicTranscript` / `model.startSpeakerTranscript` に委譲される。VRAM エラーは検出して UI に通知し、自動的に停止する処理あり。 + +閾値・チェック系 +- startCheckMicEnergy() / stopCheckMicEnergy() / startThreadingCheckMicEnergy() / stopThreadingCheckMicEnergy() +- startCheckSpeakerEnergy() / stopCheckSpeakerEnergy() / startThreadingCheckSpeakerEnergy() / stopThreadingCheckSpeakerEnergy() + +ダウンロード開始(非同期/同期) +- downloadCtranslate2Weight(data: str, asynchronous: bool=True) -> dict +- downloadWhisperWeight(data: str, asynchronous: bool=True) -> dict + - 非同期なら別スレッドでダウンロードを行い progressBar コールバックを経由して UI に進捗を返す。 + +Watchdog / WebSocket / OSC 周り +- startWatchdog() / feedWatchdog() / stopWatchdog() +- getWebSocketHost() / setWebSocketHost(data) -> dict +- setEnableWebSocketServer() / setDisableWebSocketServer() +- setOscIpAddress(data) / setOscPort(data) + - ネットワーク周りの設定は検証ロジック(IP アドレス検証、サーバー利用可否のチェック)を含む。 + +ユーティリティ関数 +- messageFormatter(format_type: str, translation: list, message: str) -> str + - OSC に送る文面のフォーマットを生成(設定に基づく)。 +- replaceExclamationsWithRandom(text) -> (str, dict) +- restoreText(escaped_text, escape_dict) -> str +- removeExclamations(text) -> str + +重要な戻り値規約 +- 成功: {"status": 200, "result": ...} +- 失敗: {"status": 400, "result": {"message": str, "data": Any}} +- 多くのメソッドは UI への通知として `self.run(status, run_mapping[key], payload)` を行う。 + +エッジケース / エラー処理 +- VRAM OOM 検出: モデル例外が上がると model.detectVRAMError(e) を呼び出し、VRAM エラーが検出された場合は関連機能を自動で無効化して UI に 400 を通知する。 +- デバイスアクセスの競合: `device_access_status` による簡易ロックで、デバイス操作中は待機する。 +- ネットワーク依存: DeepL 等の外部翻訳 API 利用可否は `model.authenticationTranslatorDeepLAuthKey` で検査し、無効時は選択肢を更新する。 + +呼び出し例(Python から直接) +```python +from controller import Controller +ctrl = Controller() +# run コールバックの例: (status:int, event_name:str, payload:any) +def ui_run(status, event, payload): + print(status, event, payload) + +ctrl.setRun(ui_run) +resp = ctrl.setEnableTranslation() +print(resp) # {'status':200, 'result': True} + +data = {"id": 123, "message": "Hello"} +resp = ctrl.chatMessage(data) +print(resp) +``` + +シーケンス図(簡易: マイク入力 -> 翻訳 -> UI 通知) +```mermaid +sequenceDiagram + participant UI + participant Mainloop + participant Controller + participant Model + + UI->>Mainloop: ユーザ操作 (send message) + Mainloop->>Controller: chatMessage(data) + Controller->>Model: getInputTranslate(message) + Model-->>Controller: translation + Controller->>Model: oscSendMessage(...) + Controller->>UI: run(200, run_mapping['transcription_send_mic_message'], payload) +``` + +次の作業 +- `docs/api.md` を `mainloop.py` のマッピングに基づいて拡張し、各エンドポイントの request/response 例を追加してください。 + +参考: 実装詳細は `src-python/controller.py` を参照してください(メソッドごとに細かな条件分岐や run_mapping キーが存在します)。 +# controller.py — 詳細設計 + +目的: UI(または外部プロセス)からの操作を受け、`config` と `model` を操作して副作用を生じさせるコマンド層。 + +主要クラス/関数: +- class Controller + - 属性: + - init_mapping: アプリ起動時の読み出し用マッピング(/get/data/*) + - run_mapping: イベント通知先のエンドポイントマップ(run 関数で使用) + - run: run(status, endpoint, result) を格納 + + - 主要メソッド: + - setEnableTranslation / setDisableTranslation: 翻訳機能の切替(モデル切替や VRAM エラー回復処理を含む) + - start/stop transcription/energy checks: Model の startMicTranscript 等を呼ぶ + - downloadCtranslate2Weight / downloadWhisperWeight: ダウンロードを非同期で開始し進捗を run 経由で通知 + - micMessage / speakerMessage / chatMessage: 認識結果を受け、翻訳/OSC/Overlay/WebSocket/ログ記録を行う主要ハンドラ + - messageFormatter: OSC 用メッセージ整形 + - 多数の get/set 系関数: config の各種設定を読み書きし status/result を返す + +エラー/例外: +- VRAM 関連は特に注意し、検出時は該当機能を無効化してユーザーへ通知する。 + +API マッピング: +- `mainloop.py` の `mapping` と連携しており、多くの `/get/data/*` `/set/data/*` `/run/*` が Controller のメソッドにマッピングされる(詳細は docs/api.md を参照)。 + diff --git a/src-python/docs/modules/device_manager.md b/src-python/docs/modules/device_manager.md new file mode 100644 index 00000000..f681b2d9 --- /dev/null +++ b/src-python/docs/modules/device_manager.md @@ -0,0 +1,73 @@ +# device_manager.py — デバイス検出と監視(overwrite) + +目的: システムのマイク/スピーカー(主に Windows の WASAPI)を列挙し、変更を監視してコールバックで通知する `DeviceManager` シングルトンを提供します。 + +主要コンポーネント: +- class Client(MMNotificationClient) + - オーディオデバイスのシステムイベント(追加/削除/デフォルト変更)を受け取り、監視ループの再起動をトリガーします。 + +- class DeviceManager + - シングルトンインスタンス: `device_manager` + - 主要プロパティ: + - `mic_devices` (dict): {host_name: [device_info, ...]} + - `default_mic_device` (dict): {'host': {...}, 'device': {...}} + - `speaker_devices` (list): [device_info, ...] + - `default_speaker_device` (dict) + - 各種 prev_/update_flag_: 差分検出用 + - callback 関連プロパティ: `callback_default_mic_device`, `callback_mic_device_list`, など多数 + + - 主要メソッド (抜粋): + - `update()` -> None: PyAudio を利用してホスト毎の入力デバイスとループバック(スピーカー)を列挙し内部状態を更新します。 + - `checkUpdate()` -> bool: 前回値との差分を計算して変更フラグを返します。 + - `monitoring()` -> None: pycaw/MMNotificationClient を使った長時間監視ループ。変化を検出すると各コールバックを呼び出す。 + - `startMonitoring()` / `stopMonitoring()` + - `getMicDevices()` / `getDefaultMicDevice()` / `getSpeakerDevices()` / `getDefaultSpeakerDevice()` + - `forceUpdateAndSetMicDevices()` / `forceUpdateAndSetSpeakerDevices()` + +コールバックAPI(例): +- `setCallbackMicDeviceList(callback)` — マイクデバイスリスト変更時に呼ばれる +- `setCallbackDefaultMicDevice(callback)` — デフォルトマイク変更時に呼ばれる +- `setCallbackProcessBeforeUpdateMicDevices(callback)` / `setCallbackProcessAfterUpdateMicDevices(callback)` — 更新前後のフック + +例: + +```python +from device_manager import device_manager + +def on_default_mic(host_name, device_name): + print('Default mic changed:', host_name, device_name) + +device_manager.setCallbackDefaultMicDevice(on_default_mic) +device_manager.forceUpdateAndSetMicDevices() +``` + +注意点: +- Windows 固有のモジュール(PyAudio paWASAPI, pycaw)に依存します。クロスプラットフォーム対応が必要な場合は別実装が必要です。 +- 監視スレッドは永続的に動作するため、アプリケーション終了時は `stopMonitoring()` を呼んで安全に停止してください。 + +## 詳細設計 + +目的: ローカルの入力(マイク)と出力(ループバックから抽出されたスピーカー)デバイスを列挙し、変更を監視してコールバックで通知する。Windows の WASAPI 等に依存。 + +主要クラス/関数: +- class Client(MMNotificationClient) + - Audio デバイスの変更イベントを受けると `loop = False` にして監視ループを再起動させる設計。 + +- class DeviceManager + - シングルトン: `device_manager = DeviceManager()` + - 主要属性: + - mic_devices: {host: [device_info...]} + - default_mic_device: {host, device} + - speaker_devices: [device_info...] + - default_speaker_device: {device} + - 各種 prev_*, update_flag_*: 差分検出のために保持 + - コールバック属性: callback_default_mic_device, callback_host_list など + - 主要メソッド: + - update(): PyAudio を使ってホストごとにデバイス列挙。Loopback デバイスを speaker_devices に集める。 + - monitoring(): MMNotificationClient と組み合わせてデバイスの変化を検出し、コールバックを発行 + - set/clear Callback 系: UI や Controller が登録して自動選択や再起動をトリガーできる + - forceUpdateAndSetMicDevices / forceUpdateAndSetSpeakerDevices: 即時更新とコールバック通知 + +注意点: +- Windows 固有の処理(paWASAPI, pycaw)に依存する。 +- デバイス取得はリソースに依存するので try/except で例外を吸収し errorLogging() を呼ぶ。 diff --git a/src-python/docs/modules/model.md b/src-python/docs/modules/model.md new file mode 100644 index 00000000..3cb331ad --- /dev/null +++ b/src-python/docs/modules/model.md @@ -0,0 +1,105 @@ +# model.py — クラスと主要メソッド +目的: アプリケーションの中核オーケストレータ。翻訳器 (Translator)、オーバーレイ、トランスクリプタ、OSC、WebSocket、Watchdog などのインスタンスを保持し、これらの起動/停止/操作を担います。`model` は `Model` のシングルトンインスタンスです。 + +主要クラスとシグネチャ: +- class threadFnc(Thread) + - __init__(self, fnc, end_fnc=None, daemon=True, *args, **kwargs) + - stop(self) -> None + - pause(self) -> None + - resume(self) -> None + +- class Model + - __new__(cls) -> Model + - init(self) -> None + - checkTranslatorCTranslate2ModelWeight(self, weight_type: str) -> bool + - changeTranslatorCTranslate2Model(self) -> None + - downloadCTranslate2ModelWeight(self, weight_type, callback=None, end_callback=None) -> Any + - isLoadedCTranslate2Model(self) -> bool + - getListLanguageAndCountry(self) -> list + - getTranslate(self, translator_name, source_language, target_language, target_country, message) -> tuple + - getInputTranslate(self, message, source_language=None) -> (list, list) + - getOutputTranslate(self, message, source_language=None) -> (list, list) + - startMicTranscript(self, fnc) -> None + - stopMicTranscript(self) -> None + - startSpeakerTranscript(self, fnc: Optional[Callable[[dict], None]] = None) -> None + - stopSpeakerTranscript(self) -> None + - startWebSocketServer(self, host, port) -> None + - stopWebSocketServer(self) -> None + - websocketSendMessage(self, message_dict: dict) -> bool + + 変更点(2025-10-09): + + - startCheckMicEnergy(self, fnc: Optional[Callable[[float], None]] = None) -> None + - 説明: 進捗/エネルギー表示用のコールバックを受け取ります。fnc が None の場合は内部で no-op を使い、呼び出し前に callable チェックを行います。これにより呼び出し側が None を渡しても安全になりました。 + + - startCheckSpeakerEnergy(self, fnc: Optional[Callable[[float], None]] = None) -> None + - 説明: 同上(fnc を Optional として受け取り、呼び出し時に callable を確認します)。内部では Queue を作成して録音データを受け取り、定期的にコールバックを呼びます。 + + - convertMessageToTransliteration(self, message: str, hiragana: bool = True, romaji: bool = True) -> list + - 説明: 以前は単一の文字列や別形を返す箇所がありましたが、現在は常にリスト(トークン単位の dict を要素とする list)を返します。hiragana/romaji の両方が False の場合は空リストを返します。 + + - createOverlayImageLargeLog(self, message_type: str, message: Optional[str], your_language: Optional[str], translation: list, target_language: Optional[dict] = None) -> object + - 説明: `target_language` は辞書形式で渡される場合があり、内部で言語リストに正規化されます(enabled な言語のみ抽出)。`message` / `your_language` は Optional となり、`None` を渡して翻訳のみのログを作ることが可能です。 + +使用例(簡易): + +```python +from model import model + +# 翻訳を呼び出す +translation, success = model.getTranslate('CTranslate2', 'Japanese', 'English', 'United States', 'こんにちは') +print(translation, success) + +# マイク文字起こしの開始(コールバックで結果を受け取る) +def on_mic_transcript(result): + print('mic transcript:', result) + +model.startMicTranscript(on_mic_transcript) + +# WebSocket サーバー起動 +model.startWebSocketServer('127.0.0.1', 2231) + +``` + +注意点: +- `Model` は多くの外部リソース(GPU、ファイル、ネットワーク)に依存するため、各操作は例外処理で保護されています。 +- 大きなモデルのロードで VRAM OOM を検出する `detectVRAMError` を備え、Controller 側でのフォールバック処理に使われます。 + +## 詳細設計 + +目的: 各モデル(翻訳/転写/Overlay/Watchdog/OSC/WebSocket 等)のインスタンスを保持し、高レベルの操作を提供するファサード。 + +主要クラス/変数: +- class threadFnc(Thread) + - 説明: ループする関数をバックグラウンドで呼ぶヘルパ。pause/stop/end callback をサポート。 + +- class Model + - シングルトン: ファイル末で `model = Model()` として公開。 + - 主な属性: + - translator (Translator) + - overlay (Overlay) + - overlay_image (OverlayImage) + - mic_audio_queue, mic_audio_recorder, mic_transcriber + - speaker_audio_queue, speaker_audio_recorder, speaker_transcriber + - watchdog (Watchdog) + - osc_handler (OSCHandler) + - websocket_server (WebSocketServer) + - 主なメソッド: + - start/stop logger, overlay, watchdog + - startMicTranscript / stopMicTranscript: 録音、transcriber の起動とキュー処理 + - startSpeakerTranscript / stopSpeakerTranscript + - startCheckMicEnergy / stopCheckMicEnergy + - startCheckSpeakerEnergy / stopCheckSpeakerEnergy + - getTranslate / getInputTranslate / getOutputTranslate: Translator を利用する高レベル関数 + - createOverlayImage* / updateOverlay* : OverlayImage と Overlay を結合して VR 表示を作成 + - startWebSocketServer / stopWebSocketServer / websocketSendMessage + +エラー処理: +- 音声認識や翻訳で VRAM エラーが発生した場合、detectVRAMError() で特殊な例外内容を検査し、Controller 経由で翻訳機能を OFF にする処理がある。 + +非同期/リソース: +- Recorder/Transcriber/Overlay/Watchdog/WebSocket はそれぞれ別スレッドで動作する。Model はそれらの開始/停止を管理する。 + +依存: +- models/translation, models/transcription, models/overlay, models/osc, models/websocket + diff --git a/src-python/docs/modules/model_extra.md b/src-python/docs/modules/model_extra.md new file mode 100644 index 00000000..5d3af186 --- /dev/null +++ b/src-python/docs/modules/model_extra.md @@ -0,0 +1,60 @@ +# model.py — クラス一覧と使用例 + +以下は `model.py` で提供される主要クラスのシグネチャ概要と、簡単な呼び出し例です。 + +## クラス / 主要シグネチャ + +- class threadFnc(Thread) + - __init__(self, fnc: Callable, interval: float = 0.1, end_callback: Callable | None = None) + - start(self) -> None + - pause(self) -> None + - resume(self) -> None + - stop(self) -> None + +- class Model + - startLogger(self) -> None + - stopLogger(self) -> None + - startOverlay(self) -> None + - shutdownOverlay(self) -> None + - startMicTranscript(self, callback: Callable[[dict], None]) -> None + - stopMicTranscript(self) -> None + - startSpeakerTranscript(self, callback: Callable[[dict], None]) -> None + - stopSpeakerTranscript(self) -> None + - startCheckMicEnergy(self, progress_callback: Callable[[int], None]) -> None + - stopCheckMicEnergy(self) -> None + - startCheckSpeakerEnergy(self, progress_callback: Callable[[int], None]) -> None + - stopCheckSpeakerEnergy(self) -> None + - startWebSocketServer(self, host: str, port: int) -> None + - stopWebSocketServer(self) -> None + - websocketSendMessage(self, message: dict) -> None + - getListMicHost(self) -> dict + - getListMicDevice(self) -> list + - getListSpeakerDevice(self) -> list + - getInputTranslate(self, text: str, source_language: str = None) -> tuple[list[str], list[bool]] + - getOutputTranslate(self, text: str, source_language: str = None) -> tuple[list[str], list[bool]] + - detectVRAMError(self, exception: Exception) -> tuple[bool, str] + +## サンプル(呼び出し例) + +以下は Model の簡単な呼び出し例です。 + +```python +from model import model + +# マイク転写のコールバック例 +def on_mic_result(result: dict): + # result の想定形: {"text": str|False, "language": str} + text = result.get("text") + language = result.get("language") + print('mic:', text, language) + +# マイク転写を開始(別スレッドで動く) +model.startMicTranscript(on_mic_result) + +# 一度だけ翻訳を呼ぶ +translation, success = model.getInputTranslate('Hello', source_language='English') +print('translation:', translation, 'success:', success) + +# WebSocket 経由で外部クライアントへイベント送信 +model.websocketSendMessage({'type': 'INFO', 'message': 'VRCT ready'}) +``` diff --git a/src-python/docs/modules/osc.md b/src-python/docs/modules/osc.md new file mode 100644 index 00000000..7ad5454d --- /dev/null +++ b/src-python/docs/modules/osc.md @@ -0,0 +1,17 @@ +# models/osc — 詳細設計 + +目的: VRChat 等と OSC / OSCQuery 経由で値の取得やチャット送信を行う。 + +主要クラス/関数: +- class OSCHandler + - sendMessage(message: str, notification: bool=True): OSC で chatbox/input を送信 + - sendTyping(flag: bool): chatbox/typing を送信 + - receiveOscParameters(): OSCQuery を立て、指定したフィルタに対してローカルでサーバを実装してイベントを受ける + - getOSCParameterValue(address: str): OSCQuery を通じて現在値を問い合わせる(use tinyoscquery) + +注意点: +- `is_osc_query_enabled` が True のときに OSCQuery を使う(127.0.0.1 や localhost の場合に True) +- 受信ハンドラは dispatcher にマップしてコールバックを呼ぶ。 +- ネットワーク環境や OSCQuery の可否により動作が変わるため例外処理が多く入っている。 + +依存: python-osc, tinyoscquery diff --git a/src-python/docs/modules/overlay.md b/src-python/docs/modules/overlay.md new file mode 100644 index 00000000..18b65321 --- /dev/null +++ b/src-python/docs/modules/overlay.md @@ -0,0 +1,31 @@ +# overlay.py — OpenVR オーバーレイ管理 + +目的: OpenVR を使ったオーバーレイ表示(複数サイズ: small/large)を管理する `Overlay` クラスを提供します。 + +主要メソッド: +- __init__(self, settings_dict) +- init(self) -> None +- startOverlay(self) -> None +- shutdownOverlay(self) -> None +- reStartOverlay(self) -> None +- updateImage(self, img: PIL.Image.Image, size: str) -> None +- updateOpacity(self, opacity: float, size: str, with_fade: bool = False) -> None +- updateUiScaling(self, ui_scaling: float, size: str) -> None +- updatePosition(self, x_pos, y_pos, z_pos, x_rotation, y_rotation, z_rotation, tracker, size) -> None +- mainloop(self) -> None # アニメーション / フェード評価ループ + +使用上の注意: +- OpenVR (SteamVR) が稼働していることが前提です。`checkSteamvrRunning()` で `vrmonitor.exe` の存在チェックを行います。 +- 例外が発生した場合は `errorLogging()` を呼んでスタックトレースを残します。 + +## モジュール構成(補足) + +- overlay.py — OpenVR を使ったオーバーレイ管理。Overlay クラスは複数サイズ(small/large)を扱い、位置/回転/透明度/フェードを制御する。 +- overlay_image.py — PIL を使ってオーバーレイに表示する画像を生成(テキストボックス、ログレイアウト、フォント管理)。 +- overlay_utils.py — 行列演算や座標変換ユーティリティ。 + +注意点: +- OpenVR(SteamVR)に依存。SteamVR が動作していることが前提。 +- フォントファイルは repo の fonts フォルダか、ランタイム内パスを探索して読み込む。 +- 生成画像は RGBA バイト列に変換され `overlay.setOverlayRaw` で渡される。 + diff --git a/src-python/docs/modules/overlay_image.md b/src-python/docs/modules/overlay_image.md new file mode 100644 index 00000000..04735623 --- /dev/null +++ b/src-python/docs/modules/overlay_image.md @@ -0,0 +1,115 @@ +# overlay_image.py — 画像生成ユーティリティ +目的: `models.overlay.overlay_image.OverlayImage` の実装に基づき、オーバーレイ用のテキストボックス/ログ画像を PIL (Pillow) で生成するための仕様書です。 + +このドキュメントは実装に合わせて書かれており、主要な公開メソッドの振る舞い、引数、返り値、例外、使用例、注意点を含みます。 + +概要 +------ +- 提供クラス: `OverlayImage` +- 役割: 文字列(元文/翻訳)やメッセージタイプ(send/receive) を受け取り、Small/Large 向けの RGBA PIL.Image を生成する。 +- 依存: Pillow (PIL)、フォントファイル群(`fonts/` ディレクトリまたは環境配下) + +主要機能 +-------- +- テキストをラップして画像化する(行折り返しを含む) +- 複数テキストブロック(原文+複数の翻訳)を縦に連結して一つの画像にする +- 背景(角丸矩形)を合成して最終的な RGBA 画像を返す +- Small と Large で UI 設定(幅、高さ、フォントサイズ等)を切り替え +- フォント探索: 実行環境の `fonts/` 配下または相対パスからフォントを探し、見つからない場合は FileNotFoundError を投げる + +公開 API(要約) +----------------- +- class OverlayImage(root_path: str | None = None) + - コンストラクタ引数 + - root_path: フォント等のリソースのベースディレクトリ。None の場合は実装に合わせて repo の `fonts/` を候補パスとして探索する。 + +- OverlayImage.createOverlayImageSmallLog(message: str, your_language: str, translation: list | None = None, target_language: list | None = None) -> PIL.Image.Image + - 説明: Small ログ向け(横長・1行〜複数行)にテキストブロックを作成して結合し、角丸背景と合成して RGBA 画像を返す。 + - 引数 + - message: 表示する原文テキスト(None を許容しない想定) + - your_language: 原文の言語キー(フォントマッピングに使用) + - translation: 翻訳テキストのリスト(省略可) + - target_language: 翻訳それぞれに対応する言語キーのリスト(省略可) + - 戻り値: PIL.Image.Image (RGBA) + - 例外: フォントが見つからない場合は FileNotFoundError を投げる可能性あり + +- OverlayImage.createOverlayImageLargeLog(message_type: str, message: str | None = None, your_language: str | None = None, translation: list | None = None, target_language: list | None = None) -> PIL.Image.Image + - 説明: Large ログ(複数行 + ヘッダ(Send/Receive)や時刻)向けに、複数ブロックを作成して縦結合し、背景を合成して返す。 + - 引数 + - message_type: 'send' または 'receive'(UI 向けアンカー/色指定に使用) + - message: 表示する原文テキスト(None 可。この場合翻訳のみを表示することもある) + - your_language: 原文の言語キー(フォント選定に使用) + - translation: 翻訳テキストのリスト(省略可) + - target_language: 翻訳それぞれに対応する言語キーのリスト(省略可) + - 戻り値: PIL.Image.Image (RGBA) + - 例外: フォント未発見などで FileNotFoundError を投げる可能性あり + +内部で使われる補助メソッド(要旨) +--------------------------------- +- concatenateImagesVertically(img1, img2, margin=0) -> Image +- addImageMargin(image, top, right, bottom, left, color) -> Image +- createTextboxSmallLog(...) -> Image +- createTextImageLargeLog(...) -> Image +- createTextboxLargeLog(...) -> Image +- getUiSizeSmallLog(), getUiColorSmallLog(), getUiSizeLargeLog(), getUiColorLargeLog() + +フォントとローカライズ +----------------------- +- 実装は `LANGUAGES` マッピングを持ち、言語キーからフォントファイル名を決定します(例: "Japanese" -> "NotoSansJP-Regular.ttf")。 +- フォントは `root_path` を基準に探索します。実行環境によりフォントファイルの場所が異なるため、実装は複数パスを順に試します。フォントが見つからない場合は FileNotFoundError を発生させる設計です。 + +描画と折り返しロジック(実装に基づく注意点) +-------------------------------------------- +- テキスト幅を計算し、基準幅に収まるように文字数ベースで分割して折り返す単純なロジックを採用しています。厳密な単語単位折り返しではなく、文字数ベースの分割になります。 +- Small/Large でフォントサイズや余白、角丸半径などを分けており、複数行のテキストブロックを縦結合することで最終画像を作ります。 + +使用例 +------ +Small ログ画像を作る例: + +```python +from models.overlay.overlay_image import OverlayImage + +overlay = OverlayImage() +img = overlay.createOverlayImageSmallLog( + message='こんにちは、世界!', + your_language='Japanese', + translation=['Hello, world!'], + target_language=['English'] +) +img.save('overlay_small.png') +``` + +Large ログ(複数メッセージ履歴)を作る例: + +```python +from models.overlay.overlay_image import OverlayImage +from datetime import datetime + +overlay = OverlayImage() +img = overlay.createOverlayImageLargeLog( + message_type='send', + message='Hello from VRCT', + your_language='English', + translation=['こんにちは'], + target_language=['Japanese'] +) +img.save('overlay_large.png') +``` + +実装上の注意と推奨事項 +----------------------- +- 実行環境にフォントが存在することを確認してください(`fonts/` に主要フォントを置くのが簡単です)。 +- Pillow (PIL) のバージョンに依存する描画 API を使っています。Pillow は v8〜最新程度で問題ありません。 +- 長いテキストの折り返しは単純な文字幅分割ロジックです。より自然な折り返し(単語単位・ルビ考慮等)が必要なら実装拡張を推奨します。 +- 生成画像は RGBA(透過)です。Overlay 側の API(`overlay.setOverlayRaw` 相当)へ渡して使う前提です。 + +復元メモ +-------- +このファイルは実装ファイル `models/overlay/overlay_image.py` を参照して復元しました。実装を変更した場合は本ドキュメントも同期して更新してください。 + +関連ファイル +------------- +- 実装: `models/overlay/overlay_image.py` +- ヘルパ: `models/overlay/overlay_utils.py` +- フォント: `fonts/` ディレクトリ diff --git a/src-python/docs/modules/transcription.md b/src-python/docs/modules/transcription.md new file mode 100644 index 00000000..1efa5ef3 --- /dev/null +++ b/src-python/docs/modules/transcription.md @@ -0,0 +1,51 @@ +# transcription — 文字起こしモジュール +概要: マイク/スピーカー音声の録音と Whisper/Google などのエンジンを使った文字起こしを提供するモジュール群です。主なクラスは録音用の Recorder と `AudioTranscriber` です。 + +主要クラス/シグネチャ: +- SelectedMicEnergyAndAudioRecorder(device, energy_threshold, dynamic_energy_threshold, phrase_time_limit) +- SelectedSpeakerEnergyAndAudioRecorder(...) +- SelectedMicEnergyRecorder(device) +- SelectedSpeakerEnergyRecorder(device) +- AudioTranscriber(speaker: bool, source, phrase_timeout: int, max_phrases: int, transcription_engine: str, root: str, whisper_weight_type: str, device: str, device_index: int, compute_type: str) + - transcribeAudioQueue(queue, languages:list, countries:list, avg_logprob: float, no_speech_prob: float) -> bool + - getTranscript() -> dict + +使用例: + +```python +from models.transcription.transcription_recorder import SelectedMicEnergyAndAudioRecorder +from models.transcription.transcription_transcriber import AudioTranscriber + +# 録音 +rec = SelectedMicEnergyAndAudioRecorder(device, energy_threshold=300, dynamic_energy_threshold=False, phrase_time_limit=3) +queue = Queue() +rec.recordIntoQueue(queue, None) + +# 文字起こし +transcriber = AudioTranscriber(speaker=False, source=rec.source, phrase_timeout=3, max_phrases=10, transcription_engine='Google', root='.', whisper_weight_type='base', device='cpu', device_index=0, compute_type='auto') +transcriber.transcribeAudioQueue(queue, ['Japanese'], ['Japan'], -0.8, 0.6) +print(transcriber.getTranscript()) +``` + +注意点: +- Whisper のモデルロードは VRAM を消費します。`Model.detectVRAMError` のような検知と回復策が必要です。 +- 録音は OS のデバイス依存のため `device_manager` でのデバイス取得と組み合わせて利用してください。 + +# models/transcription — 詳細設計 + +構成ファイル: +- transcription_recorder.py — 各デバイス向け Recorder クラス群(Base, SelectedMic*, SelectedSpeaker*)。speech_recognition をラップし、Audio / Energy をキューへ出す。 +- transcription_transcriber.py — AudioTranscriber: Google Speech API または faster-whisper を使った音声→テキスト変換の実行ロジック。複数言語に対する最良候補選択と confidence に基づく選出。 +- transcription_whisper.py — Whisper(faster-whisper)重みのダウンロードとモデル生成のユーティリティ。 + +主要契約: +- Recorder は recordIntoQueue(audio_queue, energy_queue) を提供し、バックグラウンドで音声データをキューに流す。 +- AudioTranscriber.transcribeAudioQueue(audio_queue, languages, countries, avg_logprob, no_speech_prob) -> bool + - audio_queue から音声を取り出し認識を試みる。結果は getTranscript() で取得する。常に True/False を返して呼び出し側がループ継続を制御。 + +VRAM エラー対策: +- Whisper のモデルロードで GPU メモリ不足が発生すると、ValueError("VRAM_OUT_OF_MEMORY", message) を投げる実装。Controller で捕捉して機能停止/通知する。 + +外部依存: +- speech_recognition, faster_whisper, pydub, numpy, torch + diff --git a/src-python/docs/modules/translation.md b/src-python/docs/modules/translation.md new file mode 100644 index 00000000..e247a394 --- /dev/null +++ b/src-python/docs/modules/translation.md @@ -0,0 +1,21 @@ +# models/translation — 詳細設計 + +構成ファイル: +- translation_translator.py — Translator クラス。DeepL/API、Google、Bing、Papago、CTranslate2 を統一インターフェースで扱う。 +- translation_utils.py — 重みファイルのダウンロード・検証ロジック(CTranslate2 用)。 +- translation_languages.py — 各エンジンの対応言語マップ。 + +Translator の契約: +- translate(translator_name, source_language, target_language, target_country, message) -> str|False + - 成功時は文字列、失敗または一時的エラーは False を返す。 +- changeCTranslate2Model(path, model_type, device, device_index, compute_type) + - CTranslate2 の Translator オブジェクトと Tokenizer を初期化する。 + +フォールバック: +- Controller/Model 層で翻訳が失敗した場合に CTranslate2 にフォールバックする実装がある。 + +外部依存: +- ctranslate2, transformers, deepl(オプション)、translators(任意) + +安全性: +- 翻訳 API キー(DeepL)は Translator.authenticationDeepLAuthKey で検証して保持。 diff --git a/src-python/docs/modules/transliteration.md b/src-python/docs/modules/transliteration.md new file mode 100644 index 00000000..577d055d --- /dev/null +++ b/src-python/docs/modules/transliteration.md @@ -0,0 +1,17 @@ +# models/transliteration — 詳細設計 + +目的: 日本語テキストの仮名読みを解析し、ひらがな/ローマ字(Hepburn)に変換する。 + +主要クラス/関数: +- class Transliterator + - analyze(text: str, use_macron: bool=False) -> List[dict] + - 入力: テキスト + - 出力: トークンのリスト。各要素は { orig, kana, hira, hepburn } + - split_kanji_okurigana(surface, reading_kana): 漢字+送り仮名を分割して kana を割り当てるロジックを持つ(詳細設計あり) + +実装上のポイント: +- SudachiPy を使い形態素解析して読みを得る。 +- Katakana を Hiragana に変換し、katakana_to_hepburn モジュールでローマ字化を行う。 +- 文脈ルールを `transliteration_context_rules.apply_context_rules` で適用できる設計(ルールエンジン)。 + +依存: sudachipy diff --git a/src-python/docs/modules/utils.md b/src-python/docs/modules/utils.md new file mode 100644 index 00000000..7a7b2289 --- /dev/null +++ b/src-python/docs/modules/utils.md @@ -0,0 +1,74 @@ +# utils.py — 関数一覧と使用例 +目的: 共通ユーティリティ(ログ、JSON 出力、ネットワーク/ポート検査、デバイス/計算タイプ列挙、バリデーション等)を提供します。 + +主要関数とシグネチャ: +- validateDictStructure(data: dict, structure: dict) -> bool +- isConnectedNetwork(url: str = "http://www.google.com", timeout: int = 3) -> bool +- isAvailableWebSocketServer(host: str, port: int) -> bool +- isValidIpAddress(ip_address: str) -> bool +- getComputeDeviceList() -> dict +- getBestComputeType(device: str, device_index: int) -> str +- encodeBase64(data: str) -> dict +- removeLog() -> None +- setupLogger(name, log_file, level=logging.INFO) -> logging.Logger +- printLog(log: str, data: Any = None) -> None +- printResponse(status: int, endpoint: str, result: Any = None) -> None +- errorLogging() -> None + +使用例: + +```python +from utils import printResponse, getComputeDeviceList, validateDictStructure + +# JSON 形式で mainloop に応答を返す +printResponse(200, '/get/data/version', {'version': '3.2.2'}) + +# 利用可能な計算デバイス一覧を取得 +devices = getComputeDeviceList() +print(devices) + +# 辞書構造のバリデーション +data = {'a': 1, 'b': {'c': 'x'}} +structure = {'a': int, 'b': {'c': str}} +ok = validateDictStructure(data, structure) +print('valid:', ok) +``` + +注意点: +- `printResponse` は stdout に JSON を出力しつつログファイルにも書き込みます。大きなオブジェクトは json.dumps で失敗する可能性があるため、例外処理が含まれています。 + +# utils.py — 詳細設計 + +目的: 小さなユーティリティ関数群。ロギング、ネットワーク検査、型検証、計算デバイス列挙など。 + +主要関数/変数: +- validateDictStructure(data: dict, structure: dict) -> bool + - 説明: 辞書が期待される構造(キーセットと値の型/入れ子)に完全一致するか検証する。 + - 入力: data(検証対象), structure(期待構造: 値が型または入れ子 dict) + - 出力: bool + - 例外: 型不一致や欠落時は False を返す(例外は投げない)。 + +- isConnectedNetwork(url="http://www.google.com", timeout=3) -> bool + - 説明: 指定 URL に HTTP GET して接続可否を判定。requests を使用。 + +- isAvailableWebSocketServer(host: str, port: int) -> bool + - 説明: 指定ポートへ bind できるかを試し、使用中かを判別する(True=利用可能)。 + +- isValidIpAddress(ip_address: str) -> bool + - 説明: ipaddress.ip_address で検証。 + +- getComputeDeviceList() -> dict + - 説明: CPU と CUDA(利用可能なら)を列挙し、各デバイスでサポートされる compute types を取得する。 + - 依存: torch, ctranslate2.get_supported_compute_types + +- getBestComputeType(device: str, device_index: int) -> str + - 説明: デバイス名に基づき優先 compute_type を選び、利用可能なものを返す。デフォルトは "float32"。 + +- setupLogger(name, log_file, level=logging.INFO) -> Logger + - 説明: RotatingFileHandler を使って UTF-8 ログを作る。10MB ローテーション。 + +- printLog / printResponse / errorLogging + - 説明: mainloop と通信するために標準出力へ JSON を flush するユーティリティ。内部で file ログへも書く。 + +注意点: +- ネットワーク検査やファイル生成で例外が発生した場合、errorLogging() を呼んでトレースを error.log に保存する。 diff --git a/src-python/docs/modules/watchdog.md b/src-python/docs/modules/watchdog.md new file mode 100644 index 00000000..26a822bd --- /dev/null +++ b/src-python/docs/modules/watchdog.md @@ -0,0 +1,12 @@ +# models/watchdog — 詳細設計 + +目的: 外部(Process 管理側)へ定期的に "生存" を知らせるために使う軽量ウォッチドッグ。 + +設計: +- class Watchdog(timeout:int=60, interval:int=20) + - feed(): 最終フィード時刻を更新 + - setCallback(callback): タイムアウト時に呼ぶコールバックを登録 + - start(): 現状は単純で、呼び出し側がループ中に start() を呼ぶかたち。実装は簡易(将来的にスレッド化推奨) + +注意: +- 現行実装は非常にシンプルで、長時間のブロッキングやスレッド運用の見直しが必要になり得る。 diff --git a/src-python/docs/modules/websocket.md b/src-python/docs/modules/websocket.md new file mode 100644 index 00000000..936d61de --- /dev/null +++ b/src-python/docs/modules/websocket.md @@ -0,0 +1,18 @@ +# models/websocket — 詳細設計 + +目的: 外部クライアント(例えば第三者のアプリ)へ翻訳済みテキストやイベントをブロードキャストする軽量 WebSocket サーバー。 + +API: +- class WebSocketServer(host='127.0.0.1', port=8765) + - start(): 別スレッドで asyncio ループを生成しサーバを起動。 + - stop(): サーバ停止、全クライアント切断。 + - set_message_handler(handler): クライアントからのメッセージ受信時のコールバックを登録。handler(server, websocket, message) + - send(message): 非同期キューに積んで全クライアントへ送信(スレッドセーフ)。 + - broadcast(message): asyncio を経由して即時ブロードキャスト。 + +実装上の工夫: +- サーバ本体は別スレッドで asyncio イベントループを run_forever している。 +- 送信用に内部キュー `_send_queue` を持ち、_send_loop で順次送信する。これにより GUI 等から安全に send() を呼べる。 + +依存: websockets(asyncio) + diff --git a/src-python/docs/run_events_payloads.md b/src-python/docs/run_events_payloads.md new file mode 100644 index 00000000..a30f7b8d --- /dev/null +++ b/src-python/docs/run_events_payloads.md @@ -0,0 +1,125 @@ +# Run events payloads + +このファイルは `controller.py` 内で `self.run(status, run_mapping["key"], payload)` として発行される全ての run イベントの鍵と、実際に渡されるペイロードの具体例を列挙します。 + +--- + +## 抽出済み run イベント一覧(正規化済み) + +以下は controller.py の self.run 呼び出しを解析して抽出した run イベントです。名称は `mainloop.py` の `run_mapping` に合わせて正規化しています。 + +- connected_network (200) + - payload: true | false + +- enable_ai_models (200) + - payload: true | false + +- mic_host_list (200) + - payload: list[str] + +- mic_device_list (200) + - payload: list[str] + +- speaker_device_list (200) + - payload: list[str] + +- initialization_complete (200) + - payload: dict mapping endpoint -> current value (constructed from init_mapping) + +- selected_mic_device (200) + - payload: {"host": , "device": } + +- selected_speaker_device (200) + - payload: string (device name) + +- error_device (400) + - payload: {"message": , "data": null} + +- check_mic_volume (200) + - payload: numeric energy value (float) + +- check_speaker_volume (200) + - payload: numeric energy value (float) + +- download_progress_ctranslate2_weight (200) + - payload: {"weight_type": , "progress": } + +- downloaded_ctranslate2_weight (200) + - payload: + +- error_ctranslate2_weight (400) + - payload: {"message":"CTranslate2 weight download error","data": null} + +- download_progress_whisper_weight (200) + - payload: {"weight_type": , "progress": } + +- downloaded_whisper_weight (200) + - payload: + +- error_whisper_weight (400) + - payload: {"message":"Whisper weight download error","data": null} + +- word_filter (200) + - payload: {"message": "Detected by word filter: "} + +- error_translation_engine (400) + - payload: {"message":"Translation engine limit error","data": null} + +- error_translation_mic_vram_overflow (400) + - payload: {"message":"VRAM out of memory during translation of mic","data": } + +- error_translation_speaker_vram_overflow (400) + - payload: {"message":"VRAM out of memory during translation of speaker","data": } + +- error_translation_chat_vram_overflow (400) + - payload: {"message":"VRAM out of memory during translation of chat","data": } + +- enable_translation (400 or 200) + - payload example on OOM: {"message":"Translation disabled due to VRAM overflow","data": false} + +- transcription_send_mic_message (200) + - payload: { + "original": {"message": , "transliteration": }, + "translations": [ {"message": , "transliteration": }, ... ] + } + +- transcription_receive_speaker_message (200) + - payload: same shape as transcription_send_mic_message + +- software_update_info (200) + - payload: dict (e.g. {"has_update": true, "latest_version": "3.3.0"}) + +- selected_translation_compute_type (200) + - payload: string e.g. "auto" | "cpu" | "cuda:0" + +- selected_transcription_compute_type (200) + - payload: string + +- selected_translation_compute_device (200) + - payload: device descriptor (object) — `config.SELECTED_TRANSLATION_COMPUTE_DEVICE` の現在値。 + +- selected_translation_engines (200) + - payload: config.SELECTED_TRANSLATION_ENGINES (list/dict per tab) + +- translation_engines (200) + - payload: list of selectable engines (e.g. ["CTranslate2"]) + +- initialization_progress (200) + - payload: integer stage (used values in code: 1..4) + +- enable_osc_query (200) + - payload: {"data": true|false, "disabled_functions": [...]} + +- enable_transcription_receive (200) + - payload: boolean (true when transcription receive enabled) + +- error_transcription_mic_vram_overflow (400) + - payload: {"message":"VRAM out of memory during mic transcription","data": } + +- error_transcription_speaker_vram_overflow (400) + - payload: {"message":"VRAM out of memory during speaker transcription","data": } + +--- + +注: 上記は controller.py の self.run 呼び出しを解析して作成した "実際に送られる" ペイロード例です。UI 側はこれらの形を期待してコーディングしてください。状況によっては model 層からの戻り値の具象型が変化するため、実装では型チェック/存在チェックを行ってください。 + diff --git a/src-python/docs/runtime.md b/src-python/docs/runtime.md new file mode 100644 index 00000000..f633e9f4 --- /dev/null +++ b/src-python/docs/runtime.md @@ -0,0 +1,43 @@ +# 実行手順と依存関係 + +対象 OS: Windows を想定(device_manager は WASAPI / pycaw を使う)。 + +必須依存(概略): +- Python 3.10+ 推奨 +- pip パッケージ: + - torch + - ctranslate2 + - transformers + - requests + - pyaudiowpatch + - pycaw + - speech_recognition + - pydub + - websockets + - python-osc + - tinyoscquery + - sudachipy + - pillow + - flashtext + - faster_whisper (オプション: Whisper をローカルで使う場合) + - deepl / translators(外部翻訳を使う場合) + +実行手順 (開発環境): +1. 仮想環境を作成し有効化 +2. 必要パッケージをインストール + - requirements.txt を用意する場合はそこからインストール +3. `src-python` をワークディレクトリにして `python mainloop.py` を実行 + +注意点: +- Whisper / CTranslate2 の重みは初回にダウンロードする必要がある。Controller の downloadCtranslate2Weight / downloadWhisperWeight エンドポイントからトリガできる。 +- OpenVR (SteamVR) を使う Overlay は SteamVR が動作している環境でのみ動作。 +- Windows 固有: device_manager が pyaudiowpatch と pycaw に依存。Linux/Mac での互換性は保証されない。 + +ログ: +- process.log (標準動作ログ) +- error.log (トレースバック) +- models 用のロガーは `model.startLogger()` により PATH_LOGS 配下に日付付きファイルを作成する。 + +デバッグ: +- `utils.printLog` と `utils.printResponse` が stdout に JSON を出すため、GUI 側はそれをパースして UI 更新を行う。 +- WebSocket を有効にすると外部クライアントに JSON をブロードキャストできる。 diff --git a/src-python/mypy.ini b/src-python/mypy.ini new file mode 100644 index 00000000..d9f53b5e --- /dev/null +++ b/src-python/mypy.ini @@ -0,0 +1,32 @@ +[mypy] +# Temporarily ignore missing type stubs for third-party libraries to focus on +# type errors inside the project. We'll tighten this later. +ignore_missing_imports = True +python_version = 3.11 +show_error_codes = True + +# Per-module ignores can be added later for specific noisy modules. + +[mypy-tests.*] +ignore_errors = True + +# Temporarily ignore entire implementation areas that produce many non-actionable +# mypy errors (third-party untyped libs or large unannotated modules). We'll +# progressively remove these ignores as we annotate the codebase. +[mypy-models.transliteration.*] +ignore_errors = True + +[mypy-models.overlay.*] +ignore_errors = True + +[mypy-models.osc.*] +ignore_errors = True + +[mypy-models.transcription.*] +ignore_errors = True + +[mypy-models.translation.*] +ignore_errors = True + +[mypy-device_manager] +ignore_errors = True