2026/08/17ver6.6_EN_JP版上げ忘れてたっぱ

22:09

著者: かっぱ

カテゴリー: ゲーム, パソコン・インターネット, IT技術ネタ?

タグ:OBS字幕, 字幕翻訳, リアルタイム字幕, 自動字幕, Whisper, Whisper turbo, Python, Subtitle Translator, Subtitle Browser, OBS Studio, 音声認識, 音声翻訳, 日本語字幕, 英語字幕, AI翻訳, Cloudflare Workers AI, VAD, ハルシネーション対策, PC音声キャプチャ, かっぱ配信

The English version of the file is also included in the compressed archive, but since the changes this time are in Japanese, please just translate them as best you can and read through them.

(I didn’t have time.)😅so sorry!

ばんわっぱ(o’Θ’o)ノ)) かっぱだっぱ♪ 翻訳の精度確認には20分ぐらい適当に喋り続けるのがいいって昔からいうっぱので
適当に台本無しでYoutubeにタスクマネージャーとPython版の動作ログウィンドウとOBS字幕をつけた状態で動かして録画したのをあげておいたっぱ

んで、その時動かしてたのが6.6版だっぱ♪ ⋯ (/θ・\)チラッ あ、上げてあったやつver古いっぱね 今おいておくっぱね(´>Θ<`)ゝ))エヘッ 変更点だけ今回はあげておくので つかう前の説明とか見ておきたいなら1つ前の記事を参考にしてくれっぱ(雑  または、圧縮ファイルにあるReadMeは必ず読むようにのd(-Θ-。)ネッ・・ てか、読まないと使えないと思うっぱ⋯

2026/08/17 かっぱ配信の「かっぱ語(日本語)→(英語)」マイク音声から日本語と英語字幕作りの方法についてっぱ(´・Θ・`) - YouTube https://www.youtube.com/watch?v=YI_RnQdf4JE


修正履歴 (CHANGELOG)

Python版(OBS Subtitle Translator)とブラウザ版(Subtitle Browser)の全修正履歴。


v6.6 (0816) — Python版

修正①: Whisper turboモデルにハルシネーション抑制パラメータを追加 ★★★

日付: 2026-08-16

問題: 高精度モデル(whisper-large-v3-turbo)使用時、無音やノイズに対してもっともらしい日本語・ポルトガル語・ヘブライ語等のテキストが生成される(ハルシネーション)。従来はコード側のフィルタ(短文/繰り返し/言語固有)でのみ対処していたが、Whisper API自体にハルシネーション抑制パラメータが存在することがCloudflare公式ドキュメントで確認できた。

修正ファイル:

  • obs_subtitle_translator_0816_v6_6.py(日本語フル版)
  • obs_subtitle_translator_0816_v6_6_en.py(英語フル版)
  • obs_subtitle_translator_0816_v6_6_slim.py(日本語slim版)
  • obs_subtitle_translator_0816_v6_6_en_slim.py(英語slim版)

修正内容: turboモデルのREST APIリクエストJSONに以下のパラメータを追加:

# 追加したパラメータ(turboモデルのみ、4ファイルすべてに適用)
turbo_body["vad_filter"] = True                          # サーバー側VAD(既存)
turbo_body["condition_on_previous_text"] = False         # ハルシネーションループ防止
turbo_body["compression_ratio_threshold"] = 2.0          # デフォルト2.4より厳しく
turbo_body["hallucination_silence_threshold"] = 1.0      # 1秒以上の無音をスキップ
turbo_body["no_speech_threshold"] = 0.6                  # 無音区間スキップ閾値(明示)

パラメータ詳細:

パラメータ デフォルト 設定値 効果
condition_on_previous_text True False 前のセグメントのテキストを次のセグメントのコンテキストに使わない。ハルシネーションループ(同じ誤認識が繰り返される)を防止
compression_ratio_threshold 2.4 2.0 テキストの圧縮比が閾値を超えると破棄。デフォルトより低く設定し、繰り返しテキストをより厳しく弾く
hallucination_silence_threshold なし 1.0 1秒以上の無音区間をスキップしてハルシネーションを防止
vad_filter False True サーバー側VADで無音区間を除外(Python版には既存、ブラウザ版にも追加)
no_speech_threshold 0.6 0.6 無音確率が閾値を超えるセグメントをスキップ(デフォルトだが明示)

影響:

  • モデル側でハルシネーションを抑制するため、コード側フィルタの負担軽減
  • Neuron消費は増加しない(パラメータ追加のみ)
  • 標準モデル(@cf/openai/whisper)にはこれらのパラメータは公式には未対応のため、turboモデルのみに適用
  • ブラウザ版(Subtitle Browser v9.5)の whisper.ts にも同じパラメータを追加済み

参考: Cloudflare Whisper large-v3-turbo ドキュメント


v6.5 (0810) — Python版

修正①: check_vad で voice_mask 空配列時のNaN発生を防止 ★★★

問題: check_vad() 関数内で voice_mask = (freqs >= 200) & (freqs <= 3400) の結果が全False(200-3400Hzに周波数ビンが存在しない極端に短いサンプル)の場合、np.abs(fft[voice_mask]).mean()NaN を返す。NaN < vad_thresholdFalse となり、VADを通過してしまいノイズがWhisperに送られていた。

修正: np.any(voice_mask) で空配列を事前チェックし、空の場合はVAD不合格として False を返す。

# 修正前
voice_mask = (freqs >= 200) & (freqs <= 3400)
voice_power = np.abs(fft[voice_mask]).mean()

# 修正後
voice_mask = (freqs >= 200) & (freqs <= 3400)
if not np.any(voice_mask):
    return False
voice_power = np.abs(fft[voice_mask]).mean()
  • 極端に短いサンプルや低サンプルレート環境でNaNによるVADすり抜けを防止
  • 正常な音声には影響なし(通常のサンプルレートでは200-3400Hzに必ずビンが存在する)

修正②: 短文フィルタをjaモードのみに限定 ★★★

問題: detect_hallucination() の短文スキップフィルタ(3文字以下・1単語以下・かな/漢字なし)が whisper_language に関わらず常に有効だった。英語モード(whisper_language == "en")でも "Yes"(3字), "OK"(2字), "Hi"(2字), "No"(2字) などの正当な短い英語発話がハルシネーションとして破棄されていた。

修正: whisper_language == "ja" の場合のみ短文フィルタを適用する。

# 修正前
if stripped.length <= 3 and stripped.split(/\s+/).length <= 1:
    return True  # 常に有効

# 修正後
if whisper_language == "ja" and stripped.length <= 3 and stripped.split(/\s+/).length <= 1:
    return True  # jaモードのみ
  • 英語モードで正当な短い発話が破棄される問題を解消
  • 日本語モードでは従来通り短文フィルタが有効

修正③: translate() でlist型レスポンスに対応 ★★☆

問題: Workers AI翻訳APIがlist型(配列)でレスポンスを返す場合があり、従来コードはdict型のみ対応していたため、list型レスポンス時に KeyError が発生していた。

修正: レスポンスがlist型の場合、最初の要素から翻訳結果を抽出する。

# 修正後
if isinstance(first, list):
    translated = str(first[0]).strip() if first else ""
elif isinstance(first, dict):
    translated = str(first.get("translation", "")).strip()
else:
    translated = str(first).strip() if first else ""

修正④: コメント削除・README移管 ★★☆

修正: pyファイルから詳細なコメントを削除し、説明文をREADMEに移管。ファイルサイズ約15%削減、説明文の重複を解消。

修正⑤: 429エラー(Neuron上限到達)時のgraceful shutdown ★★★

問題: Workers AIの無料枠(10,000 Neurons/日)に到達すると429エラーが返されるが、従来コードは無限リトライを続けていた。コンソールが429エラーで埋め尽くされる問題があった。

修正: 429エラー検出時に _quota_exhausted フラグを設定し、InputStream・ワーカースレッド・メインループが連携して安全に停止する。ユーザーに分かりやすいメッセージを表示(Workers Paidプランへの案内付き)。


v6.4 (0809) — Python版

  • Cache Key を SHA-256 ハッシュ化(日本語テキストがHTTPヘッダーに直接入る問題を修正)
  • 漢字のみテキストをautoモードで捨てない([JA?] タグ付きで ja_to_en 処理)
  • Ring Buffer の maxlen をブロック数で計算(メモリ浪費を修正)
  • Token Usage 取得を型安全化(isinstance チェック + or {} 追加)
  • 翻訳入力のNGフィルタをバイパス(表示用はフィルタ済み、翻訳用は生テキストを渡して文脈破壊を防止)
  • 翻訳system promptにジャンルヒントを注入(直訳癖・ゲーム用語の誤訳を軽減、起動ログに 翻訳ジャンルヒント: あり/なし 表示を追加)

v6.3 (0805) — Python版

  • 言語固有ハルシネーションフィルタを whisper_language == "ja" 時のみ有効化(auto/enモードで多言語対応)
  • イタリア語ハルシネーション検出を追加
  • カスタムキャッシュキー(cf-aig-cache-key)追加 — 翻訳方向+ソーステキストのみをキーにしてヒット率向上
  • [Cache MISS] ログ追加([Cache Hit] と併せてキャッシュ動作確認用)
  • Neuron消費内訳・推定実行時間表をREADMEに追加
  • デバイス番号警告、かっぱ配信説明、多言語ユースケースをREADMEに追加

v6.2 (0805) — Python版

  • 翻訳API Prompt Caching(x-session-affinity)追加
  • VAD_PRECHECK_THRESHOLD 50.0→3.0 に調整(ハルシネーション削減)
  • VAD_THRESHOLD_MIC 50→100 に調整(微弱ノイズ排除)
  • かっぱ配信プロンプト強化(っぱ変換フレーズ追加)
  • Whisperモデル選択機能追加(標準/高精度)
  • whisper-large-v3-turbo対応(base64 JSON形式・temperature/prompt非対応)
  • 翻訳APIトークン使用量表示追加

v9.5 (0816) — ブラウザ版 (Subtitle Browser)

バグ修正

バグ1: onSpeechEnd内のrun変数が未定義

ファイル: browser/app.js

問題: onSpeechEnd コールバック内で run 変数を参照していたが、正しくは isRunningrun は未定義のため、if (run) が常に false となり、Processing…状態からListening…に戻らない。

修正:

// 修正前
if (run) setStatus("Listening...", "#00aaff");

// 修正後
if (isRunning) setStatus("Listening...", "#00aaff");

バグ2: filters.tsの言語フィルタがautoでも効くまま

ファイル: src/filters.ts

問題: 言語固有フィルタ(ハングル・キリル・ギリシャ等)の条件が whisperLanguage === "ja" || whisperLanguage === "auto" になっており、autoモードでも言語固有フィルタが有効になっていた。autoモードでは英語→日本語翻訳のために英語入力を許可すべきなのに、英語がハルシネーションとして弾かれていた。

修正: ja モードのみに限定。

バグ3: filters.tsのisTranslationRefusalが古い版

ファイル: src/filters.ts

問題: 翻訳拒否検出のキーワードリストが古く、Python v6.5にあった「this is a japanese」「please give me the japanese」等のキーワードが欠落していた。

修正: Python v6.5移植版のフルキーワードに復元。

バグ4: index.tsのcached変数が未使用

ファイル: src/index.ts

問題: translate() から cached を受け取っているが、translate.ts は常に cached: false を返すため無害だが、TranscribeResponsecached フィールドに渡されていた。

修正: 無害だがそのまま残存(将来的にキャッシュ実装時に使用予定)。

バグ5: Env型のインポート元が間違っていた

ファイル: src/types.ts, src/index.ts, src/whisper.ts, src/translate.ts

問題: Env インターフェースが index.ts で定義されていたが、whisper.tstranslate.tsimport type { Env } from "./types" していた。実行時は影響しない(esbuildが型を削除するため)が、型定義の不整合。

修正: Envtypes.ts に移動し、index.tsimport type { Env } from "./types" に変更。

バグ6: en_to_ja_passthrough_jaが言語フィルタに含まれていなかった

ファイル: src/index.ts

問題: en_to_ja_passthrough_ja モードが言語フィルタの条件 (dir === "en_to_ja" || dir === "non_ja_to_ja") に含まれておらず、日本語がスルーされず翻訳に回されていた。

修正: 条件に en_to_ja_passthrough_ja を追加。

機能修正

PC音声キャプチャ修正(getDisplayMedia)

ファイル: browser/app.js

問題: PC音声が拾えない原因が3つ重なっていた:

  1. 複数AudioContextの分離: getPCAudio()mixStreams() がそれぞれ別の new AudioContext() を作成し、getDisplayMediaのストリームを別AudioContext経由でVADに渡していた。ブラウザによってはデータが流れない。
  2. ビデオトラック即時stop: getDisplayMedia 取得後すぐに stream.getVideoTracks().forEach(t => t.stop()) すると、一部ブラウザでオーディオトラックも停止する。
  3. 不要なモノラル変換: VAD内部が16kHzモノラルでリサンプリングするので、入力がステレオでも問題ない。モノラル変換のためにAudioContextを経由しているのが悪影響。

修正:

  1. sharedAudioCtx でAudioContextを統一(複数作成しない)
  2. ビデオトラックを即座にstopせず enabled = false で維持、VAD開始後にstop
  3. getPCAudio() は生ストリームをそのまま返す(AudioContext経由のモノラル変換を廃止)

mixモードのエコー対策

ファイル: browser/app.js

問題: mixモード(マイク+PC音声)で echoCancellation: false だと、PC音声がスピーカーから出てマイクに戻り、二重認識・ハウリングが発生する。

修正: mixモードのみ echoCancellation: true, noiseSuppression: true, autoGainControl: true に変更。mic単体・pc単体モードは従来通りfalse。

AudioContext.resume() 追加

ファイル: browser/app.js

問題: AudioContextがsuspended状態のまま動かないことがある(Autoplay Policy)。

修正: start() ボタン押下時に sharedAudioCtx.resume() を呼ぶ。

フレーズ繰り返し検出の閾値緩和

ファイル: src/filters.ts

問題: 「おじん、おじん。おじん。おじん。おじん。」(20文字)がフレーズ繰り返しチェックをすり抜けていた。s.length > 20 が厳密比較なので、ちょうど20文字だと引っかからない。

修正:

修正前 修正後 理由
s.length > 20 s.length >= 12 20文字ちょうどだと引っかからない
parts.length >= 4 parts.length >= 3 「おじん、おじん。おじん。」は3回でも弾くべき
sameCount >= 4 sameCount >= 3 同上

Whisper language + prompt渡しの復活

ファイル: src/whisper.ts

問題: Workerログで prompt= が出ていなかった。最新のwhisper.ts(languageとpromptを渡す版)がデプロイされていなかった。

修正: 標準モデルに language + prompt、turboモデルに language + initial_prompt を渡す。

ハルシネーション抑制パラメータ追加(turboモデル)

ファイル: src/whisper.ts

修正: turboモデルに以下のパラメータを追加(Python版 v6.6と同じ):

const input: Record<string, unknown> = {
  audio: audioBase64,
  condition_on_previous_text: false,
  compression_ratio_threshold: 2.0,
  hallucination_silence_threshold: 1.0,
  vad_filter: true,
  no_speech_threshold: 0.6,
};

ドキュメント更新

  • README.md: ハルシネーション対策セクション更新(モデル側パラメータ + コード側フィルタの二重対策を明記)
  • blog-post.md: ハルシネーション抑制パラメータ追加を記録、メリット・デメリット・既知の不具合セクション更新
  • README_v6_5.md: v6.6修正セクション追加、バージョン履歴にv6.6追加
  • README_v6_5_en.md: 同上(英語版)

今後の予定

ブラウザ版 (Subtitle Browser)

優先度 項目 内容
WebSocket移行 OBSページの1.5秒ポーリング遅延を解消
Llama JSON出力 整形+翻訳を1回のLLM呼び出しでJSON返す(Neuron節約)
BroadcastChannel追加 同一PCタブ間を即時配信
GPT-4o Transcribe検討 temperature: 0 で決定的出力可能(data URI形式への変換が必要)
GLM-4.7-flash翻訳検討 日本語精度向上の可能性(Neuron消費3-4倍)

Python版

優先度 項目 内容
GPT-4o Transcribe検討 同上(REST API経由)

モデル選定の考え方

無料枠(10,000 Neurons/日)で運用する場合の制約

  • STT: Whisper標準(41.14 Neurons/分)+ Whisper turbo(46.63 Neurons/分)の2択が最適

    • Deepgram系は10-20倍高い(472-836 Neurons/分)→ 無料枠では21分/日しか使えない
    • GPT-4o Transcribe / Grok STT / AssemblyAIはThird-party(AI Gateway課金の可能性、ライセンスが各社ToSに縛られる)
    • Whisper系はMIT Licenseで商用利用自由
  • 翻訳LLM: Llama 3.1 8B Fast(~2,500 Neurons/M入力トークン)が最適

    • Llama Community License(月間7億ユーザー未満なら商用利用可能)
    • GLM-4.7-flashやGemmaは3-4倍高い
    • Llama 3.1は多言語対応で日本語も十分実用レベル

ブラウザ互換性

ブラウザ micモード pcモード mixモード 備考
Chrome タブ共有時のみ「タブの音声を共有」で音声取得可
Edge Chrome同等(Chromiumベース)
Firefox getDisplayMediaのaudioサポートなし
Safari audioサポートなし
  • ONNX Runtime Webは numThreads = 1 で強制シングルスレッド化しているため、COOP/COEPヘッダー不要で全ブラウザで動作
  • AudioContext.resume() をユーザージェスチャー時(startボタン押下)に呼ぶため、Autoplay Policy対応済み

ちなみにBrowser版はここの履歴と見当違いの余計な設定がおきていたのですぐに公開する予定はないっぱ((´Θ`))ケラケラ

まあ、マイクから字幕を起こすだけならBrowser版の方がトークンも節約できる仕様だっぱが⋯ Python版よりわずかに精度が落ちる気配が現状だとあるのでその辺りもうちょい綺麗にしたいっぱねぇ⋯

Enjoy Use It :DV6.6

コメント