2026/08/14バージョン6.5でもういいっぱよね?(EN/JP6.4)
07:50
おぱよう かっぱだっぱ(o’Θ’o)ノ))フリフリ
いい加減PC側からできることは一通り試したっぱので
(PCに負荷をかけず翻訳結果を字幕にするコレ)
次回はブラウザーに音声認識を代行させてトークンをさらに節約させて低負荷で動作させられるものかどうかを検証する予定でいるっぱ
大体は問題ないっぱがたまぁにアンチチートの規約に不穏な事が書いてあるものもあるご時世だからの
これにもいえるし今後のものにしてもくれぐれもゲーム側のルールに違反しないように気をつけてどうぞっぱ
たまに
グレーゾーン扱いしてる枠が多すぎるゲームもあるようなので正直
おいらにこのゲームは大丈夫?
あのゲームは大丈夫といわれても┐(゜Θ゜)┌ェエエだっぱ
特にえげつないのは ゲーム会社が問題視してないと返事が来ても アンチチートの会社がそうではない場合もあるので 特にアプデでダメになった系はどうしろとなるからの ┌┛┌┛ズコ!
いちいちそれらの対応の為に長い時間を取るのは無理と判断したっぱ(ただちには)
お金もらってやってるとかなら(Θ゜*)ゞエヘッ
しかたないっぱねぇと
聞いてくるっぱが
EAC(EpicGames)に関しては
ゲストIDで問い合わせをした場合
アカウントを取得してEAC向けの専用の議論場所に誘導するお返事のメールが日本語で届いたっぱ
(o’Θ’))ゥンゥン 参考になったっぱ
現状使う人もそうおらんし
(外人が配信に突撃してきて困惑している人の補助の目的は達しているのでOK)
どこぞのAIにここの生成物をコピペして
業者発注してサポートしてもらったらいくら?
そういえばどんぐらいの手間がかかってるかもわかるだろうからの
(おいらがもの好きかつお金の問題よりコミュニケーションの問題を著しく毛嫌いしていてコミュニケーションできない人々を悪用してルール無用の真似をする一部のボケナス対策に必要と思ったので用意しただけだからの・・)
話がそれたっぱ
最新版のReadMeをはっておくっぱ
一番最後に日本語版と英語版のファイルおいておくっぱよ
次回は余裕があればブラウザで動くよ版っぱ(ボソ
OBS + Cloudflare Workers AI + AI Gateway 字幕翻訳スクリプト v6.5 (0810版)
OBS配信でリアルタイム字幕翻訳を行うPythonスクリプト。Cloudflare Workers AIの無料枠(10,000 Neurons/日)で動作します。
📋 目次
- 概要
- 必要なもの
- Cloudflareアカウントのセットアップ
- 使い方
- OBS側の設定
- ユースケース
- Whisperモデル選択とNeuron消費
- トラブルシューティング
- 機能一覧
- v6.5の修正内容
- コード構造解説(v6.5でコメント削除・README移管)
- バージョン履歴
- ライセンス
- 免責事項
✨ 概要
マイク音声または配信音声をリアルタイムで文字起こしし、翻訳してOBSのテキストソースに表示します。
- 🎤 マイクモード: 自分の声を拾って翻訳(日本語→英語 / 英語→日本語 / 自動検出)
- 🎮 配信モード: ゲーム・配信音声を拾って翻訳
📋 必要なもの
- Python 3.10+
- Cloudflareアカウント(無料プランでOK)
- OBS Studio(テキストソース(GDI+)が必要)
- pip install:
sounddevice requests numpy scipy
pip install sounddevice requests numpy scipy
scipyは任意ですが、高品質なリサンプリングのために推奨されます。
☁️ Cloudflareアカウントのセットアップ
ステップ1: Cloudflareアカウントの作成
- Cloudflare でサインアップ
- Cloudflare Dashboard にログイン
ステップ2: Account IDの確認
- ダッシュボードのサイドバーでアカウントを選択
- Account ID(32文字の英数字)をコピー
- スクリプト内の
ACCOUNT_IDに設定
ステップ3: APIトークンの作成
- API Tokens に移動
- Create Token をクリック
- Create Custom Token を選択
- 以下の権限を設定:
- Account → Workers AI → Read
- Account → AI Gateway → Read, Edit
- Continue to summary → Create Token
- トークンをコピー(一度しか表示されません)
ステップ4: AI Gatewayの作成
- AI Gateway に移動
- Create Gateway をクリック
- 名前を入力(例:
default) - Create をクリック
💡 自動作成: APIリクエストでGateway IDに
defaultを指定すると、最初のリクエスト時に自動作成されます。
Guardrails: ON or OFF?
| 対象 | Guardrails | NGワードリスト | 理由 |
|---|---|---|---|
| 日本語話者 | OFF(推奨) | カスタム200+語 | Neuron節約。日本語のNG表現はカスタムフィルタでカバー |
| 英語話者 | ON(推奨) | 不要 | Cloudflareのネイティブコンテンツモデレーションが英語に高精度 |
日本語話者: GuardrailsをOFFにしてください。スクリプトに日本語コンテンツ向けの200+語のカスタムNGフィルタが含まれており、Neuronを節約しながら不適切コンテンツをフィルタします。
英語話者: AI GatewayダッシュボードでGuardrailsをONにしてください。Cloudflareがネイティブにコンテンツモデレーションを処理します。Neuron消費はわずかに増加しますが、英語のNG検出はより精度が高くなります。
ステップ5: AI Gatewayキャッシュの有効化(★必須 — これをONにしないとNeuron節約なし)
- AI Gateway でゲートウェイを選択
- Settings を開く
- Cache Responses を ON に設定
- デフォルトキャッシュTTLを 300秒(5分)に設定
⚠️ これをONにしないとキャッシュが機能しません。
cf-aig-cache-ttlヘッダーだけでは効果がありません。ダッシュボードで「Cache Responses」を有効にする必要があります。
💡 AI Gatewayは完全に無料。 キャッシュ・ログ機能ともに無料で追加課金なし。追加サブスクリプション不要です。
スクリプトは cf-aig-cache-ttl ヘッダー(TRANSLATE_CACHE_TTL = 300)と cf-aig-cache-key ヘッダー(翻訳方向+ソーステキストのみをキーに使用し、プロンプトのフォーマットに関わらず同じフレーズでキャッシュヒット)も指定しています。
v6.4キャッシュキー改善: v6.3までは
cf-aig-cache-keyにf"{direction}:{text}"をそのまま使っており、日本語テキストがHTTPヘッダーに直接入っていました。v6.4以降はSHA-256ハッシュ(64文字ASCII)を使用し、安全かつ同一性を保証します。
キャッシュログ: スクリプトは
[Cache MISS]と[Cache Hit]の両方をログ出力するので、キャッシュ動作を確認できます。[Cache MISS]ばかりの場合は、ダッシュボードで「Cache Responses」がONになっているか確認してください。
ステップ6: スクリプトに認証情報を設定
環境変数を使用する場合(推奨):
# Windows (コマンドプロンプト)
set CF_API_TOKEN=あなたのトークン
# Windows (PowerShell)
$env:CF_API_TOKEN = "あなたのトークン"
# Mac / Linux
export CF_API_TOKEN=あなたのトークン
またはスクリプトに直接入力:
CLOUDFLARE_API_TOKEN = os.environ.get("CF_API_TOKEN", "cfat_paste_your_token_here")
ACCOUNT_ID と GATEWAY_ID も設定:
ACCOUNT_ID = "your_account_id"
GATEWAY_ID = "default" # 作成したゲートウェイ名
ステップ7: Workers AIアクセスの確認
- Workers AI に移動
- モデルリストが表示されることを確認
- Usage タブでNeuron消費を確認
💰 料金(心配不要)
| 項目 | コスト |
|---|---|
| AI Gateway作成・使用 | 無料 |
| AI Gatewayキャッシュ機能 | 無料 |
| AI Gatewayログ機能 | 無料 |
| 追加サブスクリプション | 不要 |
Workers AI(Whisper + LLM翻訳)の料金:
| プラン | 無料枠 | 上限超過 | 自動課金 |
|---|---|---|---|
| Free(現在) | 10,000 Neurons/日 無料 | エラーで停止 | なし |
| Paid ($5/月) | 10,000 Neurons/日 無料 | $0.011 / 1,000 Neurons | あり |
✅ Freeプランで使えます。 1日10,000 Neuronsまで無料です。 ✅ 上限超過で自動課金されません。 エラーで停止するだけです。 ⚠️ 課金には意図的にWorkers Paid($5/月)へのアップグレードが必要です。
🚀 使い方
python obs_subtitle_translator_0810_v6_5.py
起動時に以下を選択します:
- モード: マイク (1) または 配信 (2)
- 言語: 翻訳方向 (1-5)
- デバイス: デフォルト (d) または 一覧 (l) または デバイス番号
- ジャンル: ゲーム/雑談/技術/音楽/なし/かっぱ (1-6)
- レイテンシー: 短縮(3秒) / 通常(5秒) / 精度優先(8秒)
- Whisperモデル: 標準 (1) または 高精度 (2)
⚠️ デバイス番号の警告: デフォルトデバイス番号(
DEFAULT_MIC_DEVICE = 7,DEFAULT_GAME_DEVICE = 61)は作者のマシン固有のものです。あなたのデバイス番号は異なります。 デバイス選択プロンプトでlを入力して利用可能なデバイスを一覧表示し、正しいものを選んでください。
📺 OBS側の設定
- OBS Studioを開く
- ソース → + → テキストソース(GDI+)
- テキストソースのプロパティを開く
- ファイルから読み込む にチェック
output_mic.txtまたはoutput_game.txtを参照して選択- エンコーディング を UTF-8 に設定
- フォント、サイズ、色を調整
字幕は自動的に更新されます。15秒間無音が続くと字幕が消去されます。
⚠ マイクモード使用時の注意
- 起動直後の約30秒は黙って待つ(ノイズフロア学習中)
- 学習中はマイクに触れない(ノイズフロアが跳ね上がる)
[ノイズゲート確定]のログが出たら話し始めてOK- 配信中と同じ環境(BGMあり)で学習するのが推奨
- 終了は
Ctrl+C(outputファイルが空になります)
💡 ユースケース
例1: 日本語配信者 → 英語字幕
モード: 1 (マイク) 言語: 1 (日本語→英語) レイテンシー: 2 (通常 5秒)
例2: 英語ゲーム配信 → 日本語字幕
モード: 2 (配信) 言語: 1 (英語→日本語) レイテンシー: 3 (精度優先 8秒)
例3: 双方向自動検出(日本語・英語混在配信)
モード: 1 (マイク) 言語: 3 (自動検出) レイテンシー: 2 (通常 5秒)
例4: 英語のみモード(日本語スルー)
モード: 2 (配信) 言語: 4 (英語のみ、日本語スルー)
例5: かっぱ配信(語彙ヒント)
「かっぱ配信」は配信者が語尾に「っぱ」をつける日本のVTuber/配信スタイルです。ジャンル6はこれらのパターンをWhisperの語彙ヒントとして追加し、認識精度を向上させます。
モード: 1 (マイク) 言語: 1 (日本語→英語) ジャンル: 6 (かっぱ)
例6: 技術配信(技術用語)
モード: 1 (マイク) 言語: 1 (日本語→英語) ジャンル: 3 (技術/プログラミング)
例7: 低レイテンシモード
モード: 1 (マイク) 言語: 1 (日本語→英語) レイテンシー: 1 (短縮 3秒)
例8: ノイズの多い環境向け精度優先
モード: 2 (配信) 言語: 1 (英語→日本語) レイテンシー: 3 (精度優先 8秒)
例9: 高精度Whisperモデル
モード: 1 (マイク) 言語: 1 (日本語→英語) Whisperモデル: 2 (高精度)
🎯 Whisperモデル選択とNeuron消費
モデル比較
| モデル | Neurons/分 | 精度 | temperature=0.0 | ハルシネーション率 |
|---|---|---|---|---|
| 標準 (@cf/openai/whisper) | 41.14 | 中 | ✅ 対応 | 低 |
| 高精度 (whisper-large-v3-turbo) | 46.63 | 高 | ❌ 非対応 | やや高 |
高精度モデルは
temperature/promptパラメータ非対応で、base64 JSON形式で音声を送信します。temperature=0.0(決定的出力)がないため、高精度モデルはハルシネーションがやや多くなる可能性があります(無音から「Thank you for watching」等が出力される等)。
Neuron消費の内訳
実際の配信テスト(ゲーム配信44分、8秒チャンク、自動検出モード)に基づく:
| コンポーネント | Neurons/44分 | 全体比 |
|---|---|---|
| Whisper(音声認識) | ~1,587 | 93% |
| 翻訳(LLM) | ~113 | 7% |
| 合計 | ~1,700 | 100% |
重要: WhisperがNeuron消費の 93% を占めます。翻訳はわずか7%。 翻訳コスト削減(キャッシュ等)は全体で最大3-5%の節約。 実行時間を延ばす最も効果的な方法は、Whisper呼び出しを減らすこと(VADフィルタリング)。
推定実行時間(10,000 Neurons/日 無料枠)
| シナリオ | モデル | Neurons/分 | 推定実行時間 |
|---|---|---|---|
| ゲーム配信(常時会話) | 標準 | ~38 | 約4.4時間 |
| ゲーム配信(常時会話) | 高精度 | ~43 | 約3.9時間 |
| 雑談配信(会話に間あり) | 標準 | ~25 | 約6.6時間 |
| マイクモード(静か、VAD 50-70%フィルタ) | 標準 | ~13 | 約13時間 |
VAD棄却率: ゲーム配信 ~13%(常に音声あり)、マイクモード ~50-70%(無音フィルタ)。 VAD棄却率が高い = Whisper呼び出しが少ない = 実行時間が長い。
キャッシュ効果
| キャッシュ種別 | 機能 | Neuron節約 |
|---|---|---|
AI Gatewayキャッシュ (cf-aig-cache-ttl) |
同一翻訳リクエストをキャッシュ | 全体 ~3-5%(翻訳のみ) |
Prompt Caching (x-session-affinity) |
共通プロンプトプレフィックスをキャッシュ | 全体 ~1-2% |
カスタムキャッシュキー (cf-aig-cache-key) |
ソーステキストのみをキーに使用 (v6.3) | 類似フレーズのヒット率向上 |
| SHA-256キャッシュキー (v6.4) | 日本語テキストをハッシュ化して安全にキャッシュ | 同上(安全性向上) |
重要: AI Gatewayキャッシュを機能させるには、AI Gatewayダッシュボード設定で「Cache Responses」を有効にする必要があります。これがないと
cf-aig-cache-ttlヘッダーは効果がなく、キャッシュヒットも発生しません。スクリプトは
[Cache MISS]と[Cache Hit]の両方をログ出力するので、キャッシュ動作を確認できます。[Cache MISS]ばかりの場合は、ダッシュボード設定を確認してください。
🔧 トラブルシューティング
| 症状 | 原因 | 解決策 |
|---|---|---|
| 話声を拾わない | VAD事前チェック閾値が高すぎる | VAD_PRECHECK_THRESHOLD を 3.0→2.0 に下げる |
| ノイズを拾いすぎる | VAD事前チェック閾値が低すぎる | VAD_PRECHECK_THRESHOLD を 3.0→5.0 に上げる |
| ゲーム音声を拾わない | VAD閾値が高すぎる | VAD_THRESHOLD_GAME を 400→300 に下げる |
| ゲーム音声を拾いすぎる | VAD閾値が低すぎる | VAD_THRESHOLD_GAME を 400→500 に上げる |
| APIエラー 401 | トークン権限不足 | Workers AI Read + AI Gateway Read/Edit 権限を確認 |
| APIタイムアウト | ネットワーク遅延 | API_TIMEOUT を 30→60 に上げる |
| 字幕が文字化け | OBSのエンコーディング | テキストソース(GDI+)でUTF-8を指定 |
| 起動時に何も拾わない | ノイズ学習中(正常) | 30秒待つ。[ノイズゲート確定] 後に話してOK |
| [Cache Hit] が出ない | AI Gateway Cache Responses がOFF | ダッシュボードでCache ResponsesをON → TTL 300秒 |
| scipyエラー | scipy未インストール | pip install scipy(なくても動作しますが精度が落ちます) |
| [Neuron上限到達] で停止 | 10,000 Neurons/日の無料枠を使い切った | 翌日まで待つ、またはWorkers Paid($5/月)にアップグレード |
📦 機能一覧
音声認識・翻訳
- Whisper音声認識(標準 / 高精度 / 英語専用の自動選択)
- LLM翻訳(llama-3.1-8b-instruct-fast)
- AI Gatewayルーティング(キャッシュ・ログ・レート制限)
- Prompt Caching(x-session-affinity、TTFT短縮+Neuron節約)
- トークン使用量表示(
[Token] in=X out=Y、Neuron消費監視)
ノイズ除去・VAD
- 動的ノイズゲート(マイクモードのみ、起動時に30秒学習)
- 周波数VAD(200-3400Hz帯域パワーで人の声を判定、BGM/SFXをフィルタ)
- FFT前エネルギー事前チェック(低エネルギーチャンクのFFTをスキップ、CPU削減)
- 無音トリミング(前後の無音をカットしてAPIペイロード削減)
- 音声正規化(DCオフセット除去 + ピークゲイン調整)
ハルシネーション検出
- 短文スキップ(3文字以下・1単語以下、jaモード時のみ — v6.5修正)
- 繰り返し検出(同一3文字パターンが8回以上)
- プロンプト漏出検出(「厳守ルール」等)
- 言語固有フィルタ(jaモード時のみ):
- ハングル / キリル / ギリシャ / タイ / 中国語(簡体字)
- 特殊ラテン(トルコ) / 北欧系文字
- ヨーロッパ言語(ドイツ語・フランス語・スウェーデン語・オランダ語・スペイン語・イタリア語)
- 漢字のみ短文
- 英語ハルシネーション(jaモードで英語のみが出力された場合)
フィルタ・品質管理
- NGワードフィルタ(200+語、単語境界
\b対応、マスク/スキップ選択可) - 翻訳入力のNGフィルタバイパス(表示用はフィルタ済み、翻訳用は生テキストで文脈保持)
- 重複スキップ(Jaccard類似度 85%以上)
- 翻訳ループ検出・自動切り詰め
- 翻訳安全拒否検出(「I cannot provide」等を検出、元テキストを使用)
- 翻訳失敗検出(ja_to_enで日本語のまま返却された場合、元テキストを使用)
録音・パイプライン
- InputStream連続録音(PortAudioコールバック、ギャップレス)
- リングバッファ(
deque(maxlen=...)で古いデータを自動破棄、ブロック数ベースで計算 — v6.4修正) - 非ブロッキングパイプライン(録音スレッド × APIワーカースレッド分離)
- HTTPセッション再用(TCP/TLSハンドシェイク1回のみ、サイクル毎100-300ms節約)
- Atomic write(テンポラリファイル →
os.replace()でOBS読み込み破損を防止) - APIリトライ(Whisper・翻訳APIとも最大2回)
- Guardrailsエラーハンドリング(コード2016/2017を検出してスキップ)
- 429エラーgraceful shutdown(Neuron上限到達時に安全に停止、無限リトライ防止 — v6.5修正)
モード・設定
- マイクモード / 配信モード
- 双方向自動検出(
[JA]/[EN]/[JA?]タグ付き) - 英語のみモード(日本語スルー)
- 日本語以外すべてモード(日本語スルー)
- レイテンシモード(3秒 / 5秒 / 8秒)
- ジャンルヒント(ゲーム/雑談/技術/音楽/かっぱ)
- 翻訳ジャンルヒント(Llama system promptに注入、直訳癖を軽減)
- Whisperモデル選択(標準 / 高精度)
- 15秒自動消去
- 環境変数対応(
CF_API_TOKEN)
🆕 v6.5の修正内容
修正①: 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_threshold は False となり、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 len(stripped) <= 3 and len(stripped.split()) <= 1 and not _KANA_RE.search(stripped) and not _CJK_RE.search(stripped):
return True
# 修正後
if whisper_language == "ja" and len(stripped) <= 3 and len(stripped.split()) <= 1 and not _KANA_RE.search(stripped) and not _CJK_RE.search(stripped):
return True
- 英語モード・autoモードで正当な短い英語発話が破棄されなくなる
- 日本語モードでは従来通りハルシネーション防止が有効
修正③: translate() でlist型レスポンスに対応 ★★☆
問題: translate() 関数のレスポンス処理で isinstance(response, str) と isinstance(response, dict) はチェックしていたが、list 型が返った場合の処理がなかった。else ブロックで str(response) が実行され、list が [...] という文字列表現になってしまっていた。
修正: isinstance(response, list) の分岐を追加。listの最初の要素がdictの場合は response/text キーを探し、そうでない場合は要素を文字列として扱う。
# 追加した分岐
elif isinstance(response, list) and response:
first = response[0]
if isinstance(first, dict):
val = first.get("response")
if val is None:
val = first.get("text", "")
translated = str(val).strip() if val else ""
else:
translated = str(first).strip() if first else ""
- Workers AI APIの仕様変更等でlist型レスポンスが返った場合でも正しく翻訳結果を抽出
- 既存のstr/dict処理には影響なし
修正④: コメント削除・README移管 ★★☆
問題: v6.4のpyファイルには約80行のヘッダコメント(機能一覧・使い方・トラブルシューティング・バージョン履歴等)が含まれており、ファイルサイズが肥大化していた。
修正: pyファイルから全コメントを削除し、ヘッダに4行の最小限の参照のみ残した。削除した内容は本READMEに統合・移管。
# pyファイルのヘッダ(v6.5)
# OBS + Cloudflare Workers AI + AI Gateway 字幕スクリプト v6.5 (0810版)
# 詳細な使い方・設定・トラブルシューティングは README_v6_5.md を参照
# Python 3.10+, pip install sounddevice requests numpy scipy
# MIT License — Copyright (c) 2026
- pyファイルのサイズを約15%削減
- 説明文の重複を解消(READMEが唯一の情報源)
- コードの可読性向上(実行に不要なコメントが排除)
修正⑤: 429エラー(Neuron上限到達)時のgraceful shutdown ★★★
問題: 10,000 Neurons/日の無料枠を使い切るとAPIが429エラーを返すが、従来はこれを一時エラーとしてリトライし続けた。実配信ログでは上限到達後も数十回の無意味なリトライを繰り返し、コンソールが429エラーメッセージで埋め尽くされていた。
修正: モジュールレベルの _quota_exhausted イベントフラグを追加。transcribe() / translate() で429を検出したらフラグをセットし、リトライせず即座にリターン。worker_thread_fn() と main() のループがフラグを検知してgraceful shutdownする。
# フラグ定義
_quota_exhausted = threading.Event()
# transcribe() / translate() 内
if resp.status_code == 429:
print("[Neuron上限到達] 10,000 Neurons/日の無料枠を使い切りました。")
_quota_exhausted.set()
return ""
# worker_thread_fn() 内
if _quota_exhausted.is_set():
print("[Neuron上限到達 - 処理を停止します]")
shutdown_event.set()
break
# main() ループ内
if _quota_exhausted.is_set():
print("=== Neuron上限到達 — 停止中... ===")
shutdown_event.set()
break
- 上限到達後の無意味なリトライを完全排除
- コンソールが429エラーで埋め尽くされる問題を解消
- InputStream・ワーカースレッド・メインループが連携して安全に停止
- ユーザーに分かりやすいメッセージを表示(Workers Paidプランへの案内付き)
📖 コード構造解説(v6.5でコメント削除・README移管)
v6.5ではpyファイルからコメントを削除しました。以下にコード構造を解説します。
定数セクション(ファイル上部)
| 定数 | 説明 |
|---|---|
CLOUDFLARE_API_TOKEN |
APIトークン(環境変数 CF_API_TOKEN から取得) |
ACCOUNT_ID / GATEWAY_ID |
CloudflareアカウントID / AI Gateway名 |
DEFAULT_MIC_DEVICE / DEFAULT_GAME_DEVICE |
デフォルトデバイス番号(作者のマシン固有 — 要変更) |
WHISPER_MODEL_* |
Whisperモデル名(標準/英語専用/高精度) |
TRANSLATE_MODEL |
翻訳LLMモデル名(llama-3.1-8b-instruct-fast) |
TRANSLATE_CACHE_TTL |
翻訳キャッシュTTL(300秒) |
API_TIMEOUT |
APIタイムアウト(30秒) |
MAX_LINES / AUTO_HIDE_SEC |
最大表示行数(2) / 自動消去秒数(15) |
VAD_THRESHOLD_MIC / VAD_THRESHOLD_GAME |
VAD閾値(マイク100 / 配信400) |
VAD_PRECHECK_THRESHOLD |
FFT前エネルギー事前チェック閾値(3.0) |
NG_WORDS / NG_WORDS_BOUNDED |
NGワードリスト(部分一致 / 単語境界) |
関数一覧
| 関数 | 説明 |
|---|---|
select_mode() |
起動時の対話式セットアップ(モード・言語・デバイス・ジャンル・レイテンシー・モデル選択) |
get_whisper_model() |
言語に応じたWhisperモデルを返す(en→tiny-en、それ以外→標準) |
get_headers() |
APIリクエストヘッダーを構築(認証・キャッシュTTL・session affinity・cache key) |
check_ng_words() |
NGワードフィルタ(マスクまたはスキップ) |
normalize_audio() |
DCオフセット除去 + ピークゲイン調整 |
trim_silence() |
前後の無音をカット |
detect_language_simple() |
テキストの言語を簡易判定(かな→ja、簡体字→zh、漢字のみ→zh_or_kanji_only、その他→other) |
detect_hallucination() |
ハルシネーション検出(短文/繰り返し/プロンプト漏出/言語固有フィルタ) |
transcribe() |
Whisper APIで音声をテキスト化(リトライ2回・ガードレールエラー処理・ハルシネーションフィルタ) |
truncate_repeated_translation() |
翻訳出力のループ検出・切り詰め |
translate() |
LLM APIで翻訳(キャッシュ・リトライ・安全拒否検出・翻訳失敗検出) |
similarity() |
Jaccard類似度による重複判定 |
update_output_file() |
Atomic write(テンポラリファイル→os.replace())でOBS出力ファイルを更新 |
audio_callback() |
PortAudioコールバック(リングバッファに音声ブロックを追加) |
check_vad() |
周波数VAD(200-3400Hz帯域パワーで人の声を判定、FFT前エネルギー事前チェック付き) |
capture_one_chunk() |
リングバッファから1チャンク分の音声を取得・ノイズゲート・VAD・リサンプリング・WAVエンコード |
capture_one_chunk_fallback() |
InputStream失敗時のフォールバック(sd.rec() で直接録音) |
worker_thread_fn() |
API処理スレッド(音声キューから取得→文字起こし→NGフィルタ→翻訳→OBS出力) |
main() |
メイン関数(セットアップ→InputStream起動→チャンク取得ループ→自動消去) |
パイプライン構造
[マイク/配信音声]
↓
InputStream (PortAudio callback) ← 連続録音・ギャップレス
↓
Ring Buffer (deque, maxlen=ブロック数) ← 20秒分保持
↓
capture_one_chunk() ← ノイズゲート(mic) → VAD → 正規化 → 無音トリミング → リサンプリング → WAV
↓
Audio Queue (maxsize=5) ← 録音スレッドとAPIスレッドの分離
↓
worker_thread_fn() ← Whisper API → ハルシネーション検出 → NGフィルタ → 翻訳API → OBS出力
↓
output_mic.txt / output_game.txt ← Atomic write → OBS Text Source (GDI+)
英語版との違い
英語版(obs_subtitle_translator_0810_v6_5_en.py)は日本語版と機能的に同等ですが、以下の違いがあります:
- 言語固有ハルシネーションフィルタ: 日本語版ではjaモード時にハングル・キリル・ギリシャ・タイ・中国語・ヨーロッパ言語等の検出が有効。英語版ではこれらを削除し、多言語配信者の正当な発話が破棄されないようにしている(汎用フィルタ=短文・繰り返し・プロンプト漏出のみ残存)
- UI言語: プロンプト・ログメッセージが英語
- 短文フィルタ: v6.5でjaモードのみに限定したため、英語版では短文フィルタが実質無効化(英語の短い発話 “Yes”, “OK” 等が破棄されない)
📜 バージョン履歴
v6.5 (0810)
- check_vad で voice_mask 空配列時のNaN発生を防止(
np.any()事前チェック追加) - 短文フィルタをjaモードのみに限定(英語モードで “Yes”, “OK”, “Hi”, “No” 等が破棄される問題を修正)
- translate() でlist型レスポンスに対応(listの最初の要素から翻訳結果を抽出)
- 429エラー(Neuron上限到達)時のgraceful shutdown(無限リトライを防止、安全に停止)
- pyファイルからコメント削除・README移管(ファイルサイズ約15%削減、説明文の重複解消)
v6.4 (0809)
- Cache Key を SHA-256 ハッシュ化(日本語テキストがHTTPヘッダーに直接入る問題を修正)
- 漢字のみテキストをautoモードで捨てない(
[JA?]タグ付きでja_to_en処理) - Ring Buffer の maxlen をブロック数で計算(メモリ浪費を修正)
- Token Usage 取得を型安全化(
isinstanceチェック +or {}追加) - 翻訳入力のNGフィルタをバイパス(表示用はフィルタ済み、翻訳用は生テキストを渡して文脈破壊を防止)
- 翻訳system promptにジャンルヒントを注入(直訳癖・ゲーム用語の誤訳を軽減、起動ログに
翻訳ジャンルヒント: あり/なし表示を追加)
v6.3 (0805)
- 言語固有ハルシネーションフィルタを
whisper_language == "ja"時のみ有効化(auto/enモードで多言語対応) - イタリア語ハルシネーション検出を追加
- カスタムキャッシュキー(
cf-aig-cache-key)追加 — 翻訳方向+ソーステキストのみをキーにしてヒット率向上 [Cache MISS]ログ追加([Cache Hit]と併せてキャッシュ動作確認用)- Neuron消費内訳・推定実行時間表をREADMEに追加
- デバイス番号警告、かっぱ配信説明、多言語ユースケースをREADMEに追加
v6.2 (0805)
- 翻訳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トークン使用量表示追加
v6.1 (0802)
- FFT前エネルギー事前チェック(CPU負荷削減)
- 英語専用Whisperモデル自動選択
- 翻訳API AI Gatewayキャッシュ
- 周波数マスク事前計算
- VAD判定FFTウィンドウ短縮
v6.0
- InputStream + リングバッファ(連続録音)
- 非ブロッキングパイプライン
- NGワード正規表現の事前コンパイル
- 翻訳APIリトライ / Atomic write / 環境変数化
- HTTPセッション再用 / レイテンシモード選択
- ガードレールエラー対応
- ノイズゲート学習フレーム数の動的調整
📄 ライセンス
使用するAIモデルのライセンス(2026/08/10現在)
| モデル | 提供元 | ライセンス | 商用利用 |
|---|---|---|---|
| @cf/openai/whisper | OpenAI | MIT License | ✅ 可能 |
| @cf/openai/whisper-tiny-en | OpenAI | MIT License | ✅ 可能 |
| @cf/openai/whisper-large-v3-turbo | OpenAI | MIT License | ✅ 可能 |
| @cf/meta/llama-3.1-8b-instruct-fast | Meta | Llama Community License | ✅ 可能(月間7億ユーザー未満) |
⚠ 注意: 今後別のモデル(パートナーモデル等)に置き換える場合は、それぞれの利用規約・ライセンスを確認してください。本スクリプトは2026/08/10時点で上記モデルを使用しており、すべてオープンソースライセンスで商用利用可能です。
本スクリプトのライセンス
MIT Licenseの下で公開します。自由に使用・改変・再配布してください。
MIT License
Copyright (c) 2026
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
⚖️ 免責事項
- 本スクリプトは現状のまま(AS IS)提供され、いかなる保証もありません
- Cloudflare Workers AIの無料枠(10,000 Neurons/日)で動作しますが、利用量によっては上限に達する場合があります
- 超過しても自動課金はされません(エラーで停止するだけです)
- 課金には意図的にWorkers Paid($5/月)へのアップグレードが必要です
- AIによる文字起こし・翻訳の精度は完全ではなく、誤認識が発生する場合があります
- 配信での使用に伴ういかなる損害についても、作者は責任を負いません
- Cloudflareのサービス仕様・料金・利用規約は変更される可能性があります。最新情報はCloudflare公式ドキュメントで確認してください
- 使用するAIモデルのライセンス条件は変更される可能性があります。本スクリプトを改変して別のモデルを使用する場合は、必ず該当モデルのライセンスを確認してください
🔗 参考
- Cloudflare Workers AI
- Cloudflare AI Gateway
- Workers AI Model Catalog
- AI Gateway Caching
- Cloudflare API Token Creation
OBS + Cloudflare Workers AI + AI Gateway Subtitle Script v6.5 (0810)
Real-time subtitle translation script for OBS streaming. Combines Cloudflare Workers AI (Whisper speech recognition + LLM translation) with AI Gateway for low-latency, low-cost subtitle generation. Runs entirely on the Cloudflare Workers AI free tier (10,000 Neurons/day).
Table of Contents
- Features
- Requirements
- Cloudflare Account Setup
- Running the Script
- OBS Configuration
- Use Cases
- Whisper Model Selection & Neuron Consumption
- Troubleshooting
- Feature List
- v6.5 Fixes
- Code Structure
- Version History
- License
- Disclaimer
- Differences from the Japanese Version
Features
Speech Recognition & Translation
| Feature | Description |
|---|---|
| Whisper speech recognition | Uses @cf/openai/whisper (multilingual), @cf/openai/whisper-tiny-en (English-only, fast), or @cf/openai/whisper-large-v3-turbo (high-accuracy) on Cloudflare Workers AI |
| LLM translation | Translates via @cf/meta/llama-3.1-8b-instruct-fast. Supports Japanese to English, English to Japanese, bidirectional auto-detect |
| AI Gateway routing | All API requests go through AI Gateway for caching, logging, and rate limiting |
| Prompt Caching | x-session-affinity header caches common prefix for reduced TTFT + Neuron savings (v6.2+) |
| Token usage display | [Token] in=X out=Y shows real-time Neuron consumption (v6.2+) |
Noise Removal & VAD
| Feature | Description |
|---|---|
| Dynamic noise gate | Learns ambient noise for 30 seconds at startup, auto-sets noise floor (mic mode only) |
| Frequency VAD | Judges human voice by 200-3400Hz band power. Filters BGM/SFX |
| Pre-FFT energy check | Skips FFT for low-energy chunks (CPU savings). Threshold tuned to 3.0 (v6.2+) |
| Silence trimming | Cuts leading/trailing silence to reduce API payload |
| Audio normalization | DC offset removal + peak gain adjustment |
Hallucination Detection
| Feature | Description |
|---|---|
| Short text skip | Skips 1-word hallucinations <=3 chars (ja mode only – v6.5 fix) |
| Repetition detection | Trims when same 3-char phrase repeats 8+ times |
| Prompt leak detection | Detects prompt leaks, skips |
Note: Language-specific filters (Hangul, Cyrillic, Greek, Thai, Chinese, European languages, Nordic, Special Latin) are intentionally removed in the English version. English-version users may want to transcribe/translate those languages, so filtering them out would drop valid transcriptions.
Filters & Quality Control
| Feature | Description |
|---|---|
| NG word filter | 200+ NG words pre-compiled as regex. Word boundary and substring match variants |
| Translation input NG filter bypass | Display text is filtered, raw text passed to translation to preserve context (v6.4) |
| Duplicate skip | Skips if >=85% similar to previous transcription |
| Translation loop detection | Trims when same phrase repeats in translation output |
| Translation safety refusal detection | Detects “I cannot provide” etc., uses original text |
| Translation failure detection | Detects Japanese returned as-is in ja_to_en, uses original text |
Recording & Pipeline
| Feature | Description |
|---|---|
| InputStream continuous recording | PortAudio callback for gapless recording |
| Ring buffer | deque(maxlen=...) auto-discards old data on overrun. Maxlen calculated as block count (v6.4 fix) |
| Non-blocking pipeline | Separate recording thread and API worker thread |
| HTTP session reuse | TCP/TLS handshake only once. Saves 100-300ms per cycle |
| Atomic write | Temp file -> os.replace() prevents OBS read corruption |
| API retry | Both Whisper and translation API retry up to 2 times |
| Guardrails handling | Detects AI Gateway Guardrails blocks (code 2016/2017), skips |
| 429 graceful shutdown | Detects Neuron limit reached (429), shuts down safely without infinite retries (v6.5 fix) |
Modes & Settings
| Feature | Description |
|---|---|
| Microphone mode | Picks up your voice. Dynamic noise gate enabled |
| Stream mode | Picks up game/stream audio. Higher VAD threshold |
| Bidirectional auto-detect | Auto-detects Japanese/English, decides translation direction. [JA]/[EN]/[JA?] tags |
| English-only mode | Picks up English only, passes through Japanese |
| Non-Japanese all mode | Picks up all non-Japanese audio, translates to Japanese |
| Latency modes | Choose 3s (low) / 5s (normal) / 8s (accuracy) |
| Genre hints | Game/chat/tech/music/kappa vocabulary hints for Whisper |
| Translation genre hint | Injected into Llama system prompt to reduce literal translation (v6.4) |
| Whisper model selection | Choose standard or high-accuracy model at startup (v6.2+) |
| 15s auto-clear | Auto-clears subtitle after 15 seconds of silence |
| Environment variable | API token via CF_API_TOKEN environment variable |
Requirements
- Python 3.10+
- pip packages:
sounddevice,requests,numpy,scipy - Cloudflare account (Free tier OK)
- OBS Studio (Text Source (GDI+) required)
pip install
pip install sounddevice requests numpy scipy
scipyis optional but recommended for higher-quality resampling.
Cloudflare Account Setup
Step 1: Create a Cloudflare Account
- Sign up at Cloudflare
- Log in to the Cloudflare Dashboard
Step 2: Get Your Account ID
- Select your account in the dashboard sidebar
- Copy the Account ID (32-character alphanumeric)
- Set it in the script as
ACCOUNT_ID
Step 3: Create an API Token
- Go to API Tokens
- Click Create Token
- Select Create Custom Token
- Set the following permissions:
- Account > Workers AI – Read
- Account > AI Gateway – Read, Edit
- Continue to summary -> Create Token
- Copy the token (shown only once)
Step 4: Create an AI Gateway
- Go to AI Gateway
- Click Create Gateway
- Enter a name (e.g.,
default) - Click Create
Auto-creation: Specifying
defaultas the Gateway ID in API requests will auto-create the gateway on first request.
Guardrails: ON or OFF?
| Audience | Guardrails | NG word list | Reason |
|---|---|---|---|
| English speakers | ON (recommended) | Not needed | Cloudflare’s native content moderation is high-accuracy for English. Simplest setup. |
| Japanese speakers | OFF (recommended) | Custom 200+ words | Neuron savings. Japanese NG expressions covered by custom filter. |
English speakers: Turn Guardrails ON in the AI Gateway dashboard. Cloudflare will handle content moderation natively.
Japanese speakers: Keep Guardrails OFF. The script includes a 200+ word custom NG filter optimized for Japanese content.
Step 5: Enable AI Gateway Cache (Required – no Neuron savings without it)
- Go to AI Gateway and select your gateway
- Open Settings
- Turn Cache Responses ON
- Set default cache TTL to 300 seconds (5 minutes)
Without this ON, caching won’t work. The
cf-aig-cache-ttlheader alone has no effect. You must enable “Cache Responses” in the dashboard first.
AI Gateway is completely free. Cache and logging features are free with no additional charges.
The script also specifies cache TTL via the cf-aig-cache-ttl header (TRANSLATE_CACHE_TTL = 300) and a custom cache key via cf-aig-cache-key header (uses only the translation direction + source text, so identical phrases get cache hits regardless of prompt formatting).
v6.4 Cache Key Security: v6.3 used
f"{direction}:{text}"directly as the cache key, putting Japanese text into the HTTP header. v6.4+ now uses SHA-256 hash (64-char ASCII) for safety while preserving cache identity.
Cache log: The script logs
[Cache MISS]and[Cache Hit]so you can verify caching is working.
Step 6: Set Credentials in the Script
Using environment variables (recommended):
# Windows (Command Prompt)
set CF_API_TOKEN=your_token
# Windows (PowerShell)
$env:CF_API_TOKEN = "your_token"
# Mac / Linux
export CF_API_TOKEN=your_token
Or write directly into the script:
CLOUDFLARE_API_TOKEN = os.environ.get("CF_API_TOKEN", "cfat_paste_your_token_here")
Also set ACCOUNT_ID and GATEWAY_ID:
ACCOUNT_ID = "your_account_id"
GATEWAY_ID = "default" # Gateway name you created
Step 7: Verify Workers AI Access
- Go to Workers AI
- Confirm the model list is displayed
- Check Usage tab for Neuron consumption
Pricing (Don’t Worry)
| Item | Cost |
|---|---|
| AI Gateway creation & usage | Free |
| AI Gateway cache feature | Free |
| AI Gateway logging | Free |
| Additional subscription | Not required |
Workers AI (Whisper + LLM translation) pricing:
| Plan | Free tier | Over limit | Auto-billing |
|---|---|---|---|
| Free (current) | 10,000 Neurons/day free | Errors and stops | No |
| Paid ($5/mo) | 10,000 Neurons/day free | $0.011 / 1,000 Neurons | Yes |
You can use the Free plan. Up to 10,000 Neurons per day is free. No auto-billing on overage. It just errors and stops. Billing requires intentionally upgrading to Workers Paid ($5/mo).
Running the Script
python obs_subtitle_translator_0810_v6_5_en.py
At startup, you’ll be prompted to select:
- Mode: Microphone (1) or Stream (2)
- Language: Translation direction (1-5)
- Device: Default (d) or list (l) or device number
- Genre: Game/chat/tech/music/none/kappa (1-6)
- Latency: Low (3s) / Normal (5s) / Accuracy (8s)
- Whisper model: Standard (1) or High-accuracy (2)
Device Number Warning: The default device numbers (
DEFAULT_MIC_DEVICE = 7,DEFAULT_GAME_DEVICE = 61) are specific to the author’s machine. Your device numbers will be different. Always enterlat the device selection prompt to list available devices and pick the correct one for your system.
OBS Configuration
- Open OBS Studio
- Sources -> + -> Text (GDI+)
- Open the text source properties
- Check Read from file
- Browse and select
output_mic.txtoroutput_game.txt - Set Encoding to UTF-8
- Adjust font, size, and color
Subtitles update automatically. After 15 seconds of silence, the subtitle is cleared.
Mic Mode Usage Notes
- Stay silent for the first ~30 seconds after startup (noise floor learning in progress)
- Do not touch the mic during learning (noise floor will spike)
- Once
[Noise Gate Confirmed]log appears, learning is done – OK to speak - Learning with the same environment as streaming (with BGM) is recommended
- Press
Ctrl+Cto exit (output file will be cleared)
Use Cases
Example 1: Japanese Streamer -> English Subtitles
Mode: 1 (Microphone) Language: 1 (Japanese to English) Latency: 2 (Normal 5s)
Example 2: English Game Stream -> Japanese Subtitles
Mode: 2 (Stream) Language: 1 (English to Japanese) Latency: 3 (Accuracy 8s)
Example 3: Bidirectional Auto-Detect (Mixed JP/EN Stream)
Mode: 1 (Microphone) Language: 3 (Auto-detect) Latency: 2 (Normal 5s)
Example 4: English-Only Mode (Japanese Pass-Through)
Mode: 2 (Stream) Language: 4 (English only, Japanese pass-through)
Example 5: Kappa Streaming (Vocabulary Hint)
“Kappa streaming” is a Japanese VTuber/streamer style where the streamer uses sentences ending with “-ppa” (a verbal tic). Genre 6 adds these patterns as Whisper vocabulary hints for better recognition.
Mode: 1 (Microphone) Language: 1 (Japanese to English) Genre: 6 (Kappa)
Example 6: Tech Streaming (Technical Terms)
Mode: 1 (Microphone) Language: 1 (Japanese to English) Genre: 3 (Tech/Programming)
Example 7: Low Latency Mode
Mode: 1 (Microphone) Language: 1 (Japanese to English) Latency: 1 (Low 3s)
Example 8: Accuracy Priority for Noisy Environments
Mode: 2 (Stream) Language: 1 (English to Japanese) Latency: 3 (Accuracy 8s)
Example 9: High-Accuracy Whisper Model
Mode: 1 (Microphone) Language: 1 (Japanese to English) Whisper model: 2 (High-accuracy)
Whisper Model Selection & Neuron Consumption
Model Comparison
| Model | Neurons/min | Accuracy | Temperature=0.0 | Hallucination Rate |
|---|---|---|---|---|
| Standard (@cf/openai/whisper) | 41.14 | Medium | Supported | Low |
| High-accuracy (whisper-large-v3-turbo) | 46.63 | High | Not supported | Higher |
The high-accuracy model does not support
temperature/promptparameters and sends audio as base64 JSON. Withouttemperature=0.0(deterministic output), the high-accuracy model may produce more hallucinations.
Neuron Consumption Breakdown
Based on real-world testing (44 minutes of game streaming, 8-second chunks, auto-detect mode):
| Component | Neurons/44min | % of Total |
|---|---|---|
| Whisper (speech recognition) | ~1,587 | 93% |
| Translation (LLM) | ~113 | 7% |
| Total | ~1,700 | 100% |
Key insight: Whisper accounts for 93% of Neuron consumption. Translation is only 7%. Reducing translation cost (via caching) saves at most 3-5% overall. The most effective way to extend runtime is to reduce Whisper calls (via VAD filtering).
Estimated Runtime (10,000 Neurons/day free tier)
| Scenario | Model | Neurons/min | Estimated Runtime |
|---|---|---|---|
| Game streaming (talking constantly) | Standard | ~38 | ~4.4 hours |
| Game streaming (talking constantly) | High-accuracy | ~43 | ~3.9 hours |
| Chat streaming (conversation gaps) | Standard | ~25 | ~6.6 hours |
| Mic mode (quiet, VAD filters 50-70%) | Standard | ~13 | ~13 hours |
VAD rejection rate: Game streaming ~13% (always audio), Mic mode ~50-70% (silence filtered). Higher VAD rejection = fewer Whisper calls = longer runtime.
Cache Effectiveness
| Cache Type | What It Does | Neuron Savings |
|---|---|---|
AI Gateway cache (cf-aig-cache-ttl) |
Caches identical translation requests | ~3-5% overall (translation only) |
Prompt Caching (x-session-affinity) |
Caches common prompt prefix | ~1-2% overall |
Custom cache key (cf-aig-cache-key) |
Uses only source text as key (v6.3) | Higher hit rate for similar phrases |
| SHA-256 cache key (v6.4) | Hashes source text for safe header usage | Same hit rate, improved safety |
Important: For AI Gateway cache to work, you must enable “Cache Responses” in the AI Gateway dashboard settings.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Not picking up voice | VAD precheck threshold too high | Lower VAD_PRECHECK_THRESHOLD from 3.0 to 2.0 |
| Picking up too much noise | VAD precheck threshold too low | Raise VAD_PRECHECK_THRESHOLD from 3.0 to 5.0 |
| Not picking up game audio | VAD threshold too high | Lower VAD_THRESHOLD_GAME from 400 to 300 |
| Picking up too much game audio | VAD threshold too low | Raise VAD_THRESHOLD_GAME from 400 to 500 |
| API error 401 | Token permissions insufficient | Verify Workers AI Read + AI Gateway Read/Edit permissions |
| API timeout | Network latency | Raise API_TIMEOUT from 30 to 60 |
| Subtitle garbled | OBS encoding | Set encoding to UTF-8 in Text Source (GDI+) |
| Nothing picked up at startup | Noise learning in progress (normal) | Wait 30 seconds. OK to speak after [Noise Gate Confirmed] |
| No [Cache Hit] shown | AI Gateway Cache Responses is OFF | Turn ON Cache Responses in dashboard -> TTL 300s |
| scipy error | scipy not installed | pip install scipy (works without it but with lower accuracy) |
| [Neuron limit reached] and stops | 10,000 Neurons/day free tier exhausted | Wait until next day, or upgrade to Workers Paid ($5/mo) |
Feature List
- Whisper speech recognition (standard / high-accuracy / English-only auto-select)
- LLM translation (llama-3.1-8b-instruct-fast)
- Dynamic noise gate (mic mode)
- Frequency VAD (with pre-FFT check)
- Hallucination detection (Repetition/Short text [ja mode only]/Prompt leak – language-specific filters removed for multilingual support)
- NG word filter (word boundary support)
- Translation input NG filter bypass (display filtered, raw text to translation for context preservation)
- Translation genre hint (Llama system prompt injection, reduces literal translation)
- Duplicate skip / Translation loop detection / Translation safety refusal detection
- 15-second auto-clear
- AI Gateway cache (TTL 300 seconds, custom cache key via
cf-aig-cache-key, SHA-256 hashed since v6.4) - Prompt Caching (x-session-affinity)
- Token usage display (Neuron monitoring)
- Whisper API / Translation API retry (up to 2 times)
- Auto-detect mode (bidirectional translation,
[JA]/[EN]/[JA?]tags) - Genre hints (Whisper vocabulary + Llama translation system prompt)
v6.5 Fixes
Fix 1: Prevent NaN in check_vad when voice_mask is empty
Problem: In check_vad(), if voice_mask results in all False (no frequency bins in 200-3400Hz range), np.abs(fft[voice_mask]).mean() returns NaN. Since NaN < vad_threshold evaluates to False, VAD would pass and noise would be sent to Whisper.
Fix: Add np.any(voice_mask) pre-check. If the mask is empty, return False (VAD fail).
Fix 2: Short text filter limited to ja mode only
Problem: The short text skip filter was always active regardless of whisper_language. In English mode, legitimate short English utterances like “Yes”, “OK”, “Hi”, “No” were discarded as hallucinations.
Fix: Only apply the short text filter when whisper_language == "ja".
Fix 3: Handle list-type responses in translate()
Problem: The translate() function had no handling for list type responses. The else block would execute str(response), turning a list into a [...] string representation.
Fix: Add an isinstance(response, list) branch. If the first element is a dict, look for response/text keys; otherwise, treat the element as a string.
Fix 4: Comment removal – README migration
Problem: The v6.4 py file contained ~80 lines of header comments, inflating the file size.
Fix: Removed all comments from the py file, leaving only 4 lines of minimal reference in the header. The removed content has been integrated into this README.
Fix 5: Graceful shutdown on 429 (Neuron limit reached)
Problem: When the 10,000 Neurons/day free tier is exhausted, the API returns 429. Previously, this was retried indefinitely, flooding the console with 429 error messages.
Fix: Added a module-level _quota_exhausted event flag. When 429 is detected, the flag is set and the script shuts down gracefully.
Code Structure
Constants Section (top of file)
| Constant | Description |
|---|---|
CLOUDFLARE_API_TOKEN |
API token (from CF_API_TOKEN env var) |
ACCOUNT_ID / GATEWAY_ID |
Cloudflare account ID / AI Gateway name |
DEFAULT_MIC_DEVICE / DEFAULT_GAME_DEVICE |
Default device numbers (author-specific – change these) |
WHISPER_MODEL_* |
Whisper model names (standard / English-only / high-accuracy) |
TRANSLATE_MODEL |
Translation LLM model name (llama-3.1-8b-instruct-fast) |
TRANSLATE_CACHE_TTL |
Translation cache TTL (300 seconds) |
API_TIMEOUT |
API timeout (30 seconds) |
MAX_LINES / AUTO_HIDE_SEC |
Max display lines (2) / Auto-clear seconds (15) |
VAD_THRESHOLD_MIC / VAD_THRESHOLD_GAME |
VAD thresholds (mic 100 / stream 400) |
VAD_PRECHECK_THRESHOLD |
Pre-FFT energy check threshold (3.0) |
NG_WORDS / NG_WORDS_BOUNDED |
NG word lists (substring match / word boundary) |
Function Reference
| Function | Description |
|---|---|
select_mode() |
Interactive setup at startup |
get_whisper_model() |
Returns Whisper model based on language |
get_headers() |
Builds API request headers |
check_ng_words() |
NG word filter (mask or skip) |
normalize_audio() |
DC offset removal + peak gain adjustment |
trim_silence() |
Cuts leading/trailing silence |
detect_language_simple() |
Simple language detection |
detect_hallucination() |
Hallucination detection |
transcribe() |
Whisper API speech-to-text (2 retries, guardrails, hallucination filter) |
truncate_repeated_translation() |
Translation output loop detection and trimming |
translate() |
LLM API translation (cache, retry, safety refusal, failure detection) |
similarity() |
Jaccard similarity for duplicate detection |
update_output_file() |
Atomic write to update OBS output file |
audio_callback() |
PortAudio callback (appends audio blocks to ring buffer) |
check_vad() |
Frequency VAD (200-3400Hz, pre-FFT, NaN guard) |
capture_one_chunk() |
Gets one chunk from ring buffer -> process -> WAV |
capture_one_chunk_fallback() |
Fallback when InputStream fails |
worker_thread_fn() |
API worker thread |
main() |
Main function |
Pipeline Architecture
[Mic/Stream Audio]
|
InputStream (PortAudio callback) <- continuous gapless recording
|
Ring Buffer (deque, maxlen=block count) <- holds 20 seconds
|
capture_one_chunk() <- noise gate (mic) -> VAD -> normalize -> trim -> resample -> WAV
|
Audio Queue (maxsize=5) <- separates recording thread from API thread
|
worker_thread_fn() <- Whisper API -> hallucination -> NG filter -> translation -> OBS output
|
output_mic.txt / output_game.txt <- atomic write -> OBS Text Source (GDI+)
Version History
v6.5 (0810)
- Prevent NaN in check_vad when voice_mask is empty (added
np.any()pre-check) - Short text filter limited to ja mode only (fixes “Yes”, “OK”, “Hi”, “No” being discarded in English mode)
- Handle list-type responses in translate() (extracts translation from first list element)
- Graceful shutdown on 429 (Neuron limit reached) (prevents infinite retries, safe shutdown)
- Comment removal – README migration (reduces py file size by ~15%, eliminates documentation duplication)
v6.4 (0809)
- Cache Key uses SHA-256 hash (fixes Japanese text in HTTP header)
- Kanji-only text not dropped in auto mode (
[JA?]tag, ja_to_en processing) - Ring buffer maxlen calculated as block count (fixes memory waste)
- Token usage extraction made type-safe
- Translation input NG filter bypass (display text filtered, raw text passed to translation to preserve context)
- Translation system prompt genre hint injection (reduces literal translation and game term mistranslation)
v6.3 (0805)
- Language-specific hallucination filters now only active when
whisper_language == "ja" - Italian hallucination detection added
- Custom cache key (
cf-aig-cache-key) added [Cache MISS]log added alongside[Cache Hit]- Neuron consumption breakdown and estimated runtime table added to README
v6.2 (0805)
- Translation API Prompt Caching (x-session-affinity) added
- VAD_PRECHECK_THRESHOLD adjusted 50.0 -> 3.0 (hallucination reduction)
- VAD_THRESHOLD_MIC adjusted 50 -> 100 (faint noise rejection)
- Kappa streaming prompt enhanced
- Whisper model selection added (standard / high-accuracy)
- whisper-large-v3-turbo support (base64 JSON format)
- Translation API token usage display added
v6.1 (0802)
- Pre-FFT energy check (CPU load reduction)
- Auto-select English Whisper model
- Translation API AI Gateway cache
- Pre-computed frequency mask
- Shortened VAD FFT window
v6.0
- InputStream + ring buffer (continuous recording)
- Non-blocking pipeline
- Pre-compiled NG word regex
- Translation API retry / Atomic write / Environment variable support
- HTTP session reuse / Latency mode selection
- Guardrails error handling
- Dynamic noise gate learning frame count
License
AI Model Licenses (as of 2026/08/10)
| Model | Provider | License | Commercial Use |
|---|---|---|---|
| @cf/openai/whisper | OpenAI | MIT License | Allowed |
| @cf/openai/whisper-tiny-en | OpenAI | MIT License | Allowed |
| @cf/openai/whisper-large-v3-turbo | OpenAI | MIT License | Allowed |
| @cf/meta/llama-3.1-8b-instruct-fast | Meta | Llama Community License | Allowed (< 700M MAU) |
Note: If you switch to different models (partner models, etc.), please check their respective terms of use and licenses.
Script License
Released under the MIT License. Feel free to use, modify, and distribute.
MIT License
Copyright (c) 2026
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
Disclaimer
- This script is provided “AS IS”, without any warranty
- Runs on the Cloudflare Workers AI free tier (10,000 Neurons/day), but may hit the limit depending on usage
- No auto-billing on overage (it just errors and stops)
- Billing requires intentionally upgrading to Workers Paid ($5/mo)
- AI speech recognition and translation are not perfect; misrecognition may occur
- The author is not responsible for any damages arising from use during streaming
- Cloudflare service specifications, pricing, and terms of use may change. Check the Cloudflare documentation for the latest information
- AI model license conditions may change. If you modify this script to use different models, be sure to check the applicable model licenses
Differences from the Japanese Version
The English version is functionally identical to the Japanese version except for the following changes:
Removed Features
| Feature | Japanese Version | English Version | Reason |
|---|---|---|---|
| Hangul detection | Detects Korean characters, skips | Removed | English-version users may want to transcribe Korean streams |
| Cyrillic detection | Detects Russian etc., skips | Removed | English-version users may want to transcribe Russian streams |
| Greek detection | Detects Greek, skips | Removed | English-version users may want to transcribe Greek streams |
| Thai detection | Detects Thai, skips | Removed | English-version users may want to transcribe Thai streams |
| Chinese detection | Detects simplified Chinese, skips | Removed | English-version users may want to transcribe Chinese streams |
| Special Latin detection | Detects Turkish characters, skips | Removed | English-version users may want to transcribe Turkish streams |
| Nordic detection | Detects Nordic characters, skips | Removed | English-version users may want to transcribe Nordic language streams |
| European language detection | Detects German/French/Swedish/Dutch/Spanish, skips | Removed | English-version users may want to transcribe European language streams |
| Kanji-only detection | Detects kanji-only text (<5 chars), skips | Removed | English-version users may want to transcribe kanji-only text |
Kept Features
| Feature | Status | Reason |
|---|---|---|
| Short text skip | Kept (ja mode only) | Only applies when whisper_language == "ja" (v6.5 fix) |
| Repetition detection | Kept | Not language-specific |
| Prompt leak detection | Kept | Not language-specific |
| English hallucination skip (ja mode) | Kept | Only applies when whisper_language == "ja" |
| Translation failure detection (ja_to_en) | Kept | Only applies when direction == "ja_to_en" |
| NG word filter | Kept | All 200+ NG words kept |
Why These Changes Were Made
The Japanese version was built for a Japanese-speaking streamer who primarily speaks Japanese. In this context, Whisper sometimes produces hallucinations in Korean, Chinese, German, French, etc. The language-specific filters were added to catch and discard these hallucinations.
However, the English version targets international users who may speak any language:
- A French streamer wanting English subtitles would have their French speech incorrectly discarded by the European language filter
- A Korean streamer wanting English subtitles would have their Korean speech incorrectly discarded by the Hangul filter
- A Russian streamer wanting English subtitles would have their Russian speech incorrectly discarded by the Cyrillic filter
Removing these filters ensures that valid speech in any language is never incorrectly discarded.
The NG word filter is kept because:
- It only masks words (replaces with
***), it doesn’t discard the entire transcription - English speakers using Guardrails ON (recommended) will have Cloudflare’s native moderation handle this
- Japanese speakers using Guardrails OFF still benefit from the 200+ Japanese NG words
- Users can customize
NG_WORDSandNG_WORDS_BOUNDEDin the script for their own language
v6.5 Short Text Filter Change
In v6.5, the short text filter was limited to whisper_language == "ja" only. This means:
- Japanese version (ja mode): Short text filter is active – same behavior as before
- English version (en/auto mode): Short text filter is effectively disabled – short English utterances like “Yes”, “OK”, “Hi”, “No” are no longer discarded
Summary
The Japanese version is optimized for Japanese streamers who need to filter out hallucinations in other languages. The English version is designed for international users who may transcribe and translate any language, so language-specific hallucination filters are removed to avoid dropping valid transcriptions.
References
- Cloudflare Workers AI
- Cloudflare AI Gateway
- Workers AI Model Catalog
- AI Gateway Caching
- Cloudflare API Token Creation
コメント