MultiVoice

公開日 2026年9月29日

Minecraftでのリアルタイム音声翻訳

私のサーバーには、スペイン語、英語、ポルトガル語、日本語を話す人が集まっています。ボイスチャットは機能していましたが、言語の壁により会話の半分は失われていました。そこで、あなたが言ったことを聞き取り、テキスト化して各自の言語に翻訳し、頭の上の吹き出しに表示するシステムを構築しました。

すべて自分のマシンで稼働しており、有料のAPIは使用していません。そして、最も重い処理を行っているのは2010年のプロセッサを搭載したVPSです。

概要

  • 機能: Plasmo Voiceの近接ボイスを文字起こしし、各プレイヤーのMinecraftの言語に翻訳します。テキストは話している人の頭上の吹き出しに表示されます。
  • 仕組み: Minecraftサーバーは音声のコピーをPythonの別サービス(音声認識にVosk、翻訳にCTranslate2上のOPUS-MT)に送信し、新しいプロトコルパケットを通じてテキストを返します。
  • 実際のレイテンシ: 話している間に暫定的なテキストが表示されます。最終的な文は話し終わってから約0.7秒後に届き、翻訳はその約0.2秒後に届きます。
  • コスト: API費用は0ユーロ。8 vCPUのVPSで、高品質なモデルを使用しても同時に8〜10人の発話を処理できます。
  • うまくいかなかったこと: 小さなオープンモデルを使用した日本語への翻訳は使い物になりませんでした。また、スラングの用語集がないと、翻訳機が「creeper(クリーパー)」を放送禁止用語に変換してしまいます。

1つの文の経路

話す ──▶ Minecraftサーバー ──▶ 音声サービス (別マシン)
                 │                        │  Opus → 16 kHz → Vosk
                 │                        │  最終テキスト → 翻訳
                 ◀────── テキスト ────────┘
                 │
                 └──▶ 各聞き手の言語で、頭上に吹き出しを表示

設計を決定づけた3つの要素:

1. 音声はクライアントではなくサーバーでコピーされる。 Minecraftサーバーはすでに全員の音声を受信しており、復号化するキーを持っています。そのため、音声サービスとの間に単一の接続を開き、デコードせずにOpus音声をそのまま送信します。非圧縮オーディオの送信にかかる256 kbpsではなく、話している人1人あたり約30 kbpsで済みます。

2. 音声認識はJVMの外部で実行される。 これは複数のコアを消費するため、ゲームのカクつきの原因となるMinecraftのメインスレッドからCPUを奪いたくありません。さらに、サーバーに触れることなく、サービスの再起動やエンジンの変更が可能です。

3. 誰も読まない場合は文字起こししない。 字幕を有効にしている人が範囲内にいる場合にのみ、音声が送信されます。これが最大の節約になります。ほとんどの時間、システムはリソースを消費しません。

音声認識:選ぶ前に測定する

VPSは2010年のXeon X5675:8 vCPU、AVXなし、GPUなし。これにより最初からWhisperは除外されたため、真のストリーミングであり、話している間に暫定テキストを出力するVosk (Kaldi) を採用しました。

Modのコードを1行も書く前に、N人のプレイヤーが同時に話す状況をリアルタイムでシミュレートし、処理の遅延を測定するベンチマークを作成しました。スペイン語の大規模モデルでの結果は以下の通りです。

同時ストリーム数使用コア数p95遅延判定
20.5571 msOK
82.6299 msOK
103.91129 msOK
126.90794 ms遅延発生

ここで学んだ2つのこと:

  • ストリームあたりのコストは負荷とともに増加する。 12ストリームは3.6コアを要求するはずが、6.9コアを要求しました。プロバイダーがCPUを奪っていた(vmstatのstealは0)わけではなく、15年前のプロセッサの遅いメモリが原因でした。
  • 複数のプロセスよりも、スレッドを使用した単一のプロセスの方がパフォーマンスが高い。 メモリ内のモデルの同じコピー(14 GBではなく2.3 GB)を全員で共有するためです。

また、大規模モデルのチューニングも試しました。最終的なニューラル並べ替え(rnnlm)を削除すると、品質をほとんど落とさずにコストを27%削減できました。デコーダーの検索パラメータを調整しても何も変わりませんでした。コストは音響ニューラルネットワークにあり、AVXがなければ調整で解決することはできません。

翻訳:安上がりでも機能する

翻訳には、int8量子化でCTranslate2に変換されたOPUS-MT(Helsinki-NLPのモデル)を使用しています。言語ペアあたり79 MBのモデルであり、CTranslate2はSSE4.1のみを要求するため、古いXeonでも動作します。

メトリクス (VPS上)es→enen→es
文あたりの時間 (p50 / p95)197 / 245 ms191 / 222 ms
1秒あたりの文数, 2スレッド9.810.9
1秒あたりの文数, 4スレッド17.518.4

これはプロジェクトにおける嬉しい驚きでした。モデルが小さいため、古いプロセッサでも現代のPCとほぼ同じ速さで翻訳できたのです。

モデル以上に重要な3つの設計の詳細:

  • 聞き手ごとではなく、言語ごとに一度だけ翻訳する。 10人が聞いていて、その中に3つの異なる言語がある場合、翻訳は10回ではなく3回になります。
  • 最終的な文のみを翻訳する。 単語ごとに修正される暫定的なテキストを翻訳すると、コストが倍増し、字幕がちらつくことになります。
  • 直近の文のキャッシュ。 ゲームでは「行くよ」「待って」「今行く」などが何度も繰り返されます。

グリッドに収まらない言語

ペアごとのモデルで複数の言語をカバーすることには、明らかな問題があります。N個の言語では、N×(N−1)個のモデルが必要になります。4言語なら12個、20言語なら380個です。

古典的な解決策は**英語を介したブリッジ(ピボット)**です。直接のモデルがない場合、英語に翻訳し、英語から目的の言語に翻訳します。私のルーターは最初に直接モデルを試し、必要な場合のみ連結します。2倍の時間がかかり(約0.5秒ではなく約1.1秒)、忠実度は少し落ちますが、何百ものモデルを維持することは避けられます。

ここで2つの興味深いケースが発生しました。

日本語:認識はするが、翻訳はしない

日本語からの出力はうまくいきました。「危ないクリーパーが後ろにいる」は “¡Cuidado! Creper está detrás de mí”(注意!後ろにクリーパーがいるぞ)のように出力されました。

しかし、日本語への入力は惨憺たるものでした。Helsinkiの唯一の無料の英語→日本語モデルは聖書のテキストで訓練されており、「watch out there is a creeper behind you」はハゲタカが集まるという内容に変換されてしまいました。全く使い物になりません。

決定事項:日本人プレイヤーが話すと、全員が翻訳されたテキストを読みますが、彼自身は他の人の文を元の言語で読みます。翻訳ルートがない場合、システムは単に元のテキストを送信します。設定は何も必要ありません。

ポルトガル語:2つの方言、1つのモデル

ポルトガル語については、方言タグを受け入れる現代的なモデル(opus-mt-tc-big-en-pt)が存在します。ブラジル向けには>>pob<<、欧州向けには>>por<<です。モデルの語彙の中にこれらのタグを受け入れるかどうかを自動検出することで統合しました。

正直な結果:タグはテキストをほとんど変えません。 欧州ポルトガル語を要求しても、「autocarro」や「comboio」の代わりに「ônibus」や「trem」(ブラジルポルトガル語)が返ってきました。より良いモデルが登場した時のためにメカニズムは構築したままですが、現状では約束を果たせていません。

クライアントは、それが重要となる言語についてのみ地域を保持します。es_mxとes_esはどちらも「es」ですが、pt_brとpt_ptはそのまま送信されます。そして、これら2つの方言間では翻訳は行われません。同じように読めるからです。

そしてスラングがすべてを壊す

プロジェクトで最悪の翻訳は、英語を介したポルトガル語→スペイン語でした。

“olha que tens um creeper atrás de ti” → “Mira, tienes una ramera detrás de ti”(見ろ、お前の後ろに売春婦がいるぞ)

また、「spawn」→「engendro(怪物)」、「nether」→「parte inferior(下部)」など。一般的な自動翻訳はゲームの語彙を知らず、英語を介するブリッジが破壊を倍増させます。解決策は、絶対に翻訳されない保護された用語の用語集です。これは私のリストの次の課題であり、これらのシステムが文脈の中で最もよく使われる単語でまさに失敗することを示す完璧な例です。

メニューなしの言語設定

私が満足しているディテール:言語セレクターがありません。 クライアントはMinecraftで設定されている言語を送信し、サーバーはそれに基づいて、あなたが何語で話し、何語に翻訳すべきかを想定します。あなたがスペイン語でプレイし、他の人が英語でプレイしている場合、オプションに一切触れることなく、それぞれが自分の言語でお互いの言葉を読み取ることができます。

私が追加した唯一のオプションは、「吹き出しを表示する」と「私の声の文字起こしを許可する」です。なぜなら、人々の声を文字起こしすることは個人データの処理であり、それは想定するものではなく尋ねるべきことだからです。デフォルトではディスクには何も保存されません。

もっと予算があればやること

言語ペアによる設計には明確な限界があります。約6言語を超えると、言語ごとのモデルではなく、単一の多言語モデル(Whisperなど)を搭載したGPUを使用するのが合理的です。20のモデルと30 GBのRAMから、2つのモデルへと移行し、同時にすべての言語で品質を向上させることができます。コードにはすでに分離されたエンジンインターフェースがあるため、作り直すのではなくクラスを追加するだけで済みます。

それまでの間、システムは約束通りの働きをしています。人々は互いに理解し合えています。

学んだこと(収穫)

  1. 構築する前に測定する。 Modに触れる前に作成したベンチマークによって、モデル、同時接続人数、そしてモデルのどの部分を無効にするかが決定されました。
  2. ゼロレイテンシは存在しない。そしてそれを伝えるべきである。 誰も言い終わる前に単語を文字起こしすることはできません。現実的な目標は「瞬時に感じる」ことであり、それは話している間の暫定テキストによって達成されます。
  3. 壊すのではなく、ダウングレードする。 音声サービスがダウンしても、ボイスチャットはそのまま機能し、字幕が出なくなるだけです。CPUが不足している場合は、文がスキップされます。翻訳ができない場合は、原文が表示されます。
  4. 興味深い失敗は境界領域にある。 聖書で訓練されたモデル、何も機能しない方言タグ、そして侮辱的な言葉に変換された「creeper」などです。