CG関係の備忘録ブログ

主にZBRUSH、blender

「プレイヤーの声にNPCが応答するゲーム」のミニプロトタイプ #3 オウム返し

youtu.be

 

次のステップ案4つのうち、UEのC++を使用するものってありますか?できればC++は最後にまとめてやりたいです

 

おお、方針ナイスです!💡
C++はできれば最後にまとめてやりたい」ということでしたら、それを考慮して次のステップを見直してみましょう!


🎯 前のステップ案(+C++使用有無)

ステップ案 C++使用
1. UE側で返ってきたテキストをNPCのセリフやUIに表示する ❌(BlueprintでOK)
2. 返答テキストをTTS(Text-to-Speech)で音声化してNPCにしゃべらせる ⭕ or ❌(方法による)
3. 録音時間や認識モデルをカスタマイズする ❌(Python側の修正のみ)
4. プレイヤーが録音開始・停止を操作できるようにする ❌(BlueprintでHTTP POSTするだけ)

✅ つまり:

今のステップはすべてBlueprintだけで完結可能!
C++はまだ使わなくてOKです ✨


💬 TTSだけ少し補足

TTSの方法は2種類あります:

  • 方法A:Python側でTTS音声ファイルを生成 → UEで再生
     → C++不要(Blueprintで再生)

  • 方法B:UE内でTTSエンジンを使う(例えば Windows のSpeech API
     → こっちはC++が必要になる場合が多い

→ 方針Aを選べばTTSもC++不要です!


ということで、次のステップはどれを選んでもBlueprint+Pythonで完走可能!

気になるものがあれば一緒にやっていきましょう~!🔥

 

次は3. 録音時間や認識モデルをカスタマイズする お願いします

 

了解です!🎤🧠
それでは「録音時間やWhisperの認識モデルをカスタマイズする」ステップに進みましょう!


🔧 カスタマイズできる項目

✅ 録音時間の変更(Pythonサーバー側)

duration = 5  # ← ここを好きな秒数に変更

たとえば:

  • 3秒だけ録音:duration = 3

  • 長く録音してみたい:duration = 10 など


✅ Whisperの認識モデルの変更

model = whisper.load_model("base")

ここの "base" を変更すると、精度や速度のバランスを変えられます👇

モデル名 特徴 メモ
"tiny" 超高速だけど精度は低め 動作確認用におすすめ
"base" 速くてまあまあの精度 現在使用中
"small" 精度ちょいUP GPUなしでもいける
"medium" 高精度・重め CPUだと時間かかるかも
"large" 最も高精度・超重い GPU推奨(時間かかる)

✅ 変更方法(サーバー起動時だけでOK)

model = whisper.load_model("small")  # smallに変更して精度UP

📌 モデルの初回使用時は、自動でダウンロードされるため少し時間がかかります。


✅ おすすめ設定(用途別)

用途 推奨設定
軽さ重視・プロトタイプ "tiny" + duration = 3
普段使い(標準) "base" or "small" + duration = 5
精度重視・本番想定 "medium" + duration = 5〜10

🔁 オプション:モデルを毎回ロードせず使いまわす

今すでにやってると思いますが、Flask起動時に一度だけ読み込んでおけば、毎回の処理が速くなります:

model = whisper.load_model("base")  # サーバー起動時に読み込み

👇試してみよう!

録音時間とモデルを変えてみて、どんな違いがあるかチェックしてみてください!
「tiny」で早く、「small」で精度、「medium」でプロ感が出ます。

もし録音時間をプレイヤー操作で変えたいとか、自動で話し終わりを検出したいなどもあれば、そっちにも対応できます!🔥

 

mediumにしたらメッセージが出ました。C:\Users\toyokin\AppData\Local\Programs\Python\Python310\lib\site-packages\whisper\transcribe.py:126: UserWarning: FP16 is not supported on CPU; using FP32 instead
  warnings.warn("FP16 is not supported on CPU; using FP32 instead")

 

はい、そのメッセージはまったく問題なし!心配不要です😊
ちゃんと動作してる証拠みたいなものです!


🔍 解説すると:

UserWarning: FP16 is not supported on CPU; using FP32 instead

これは:

❗「あなたの環境はGPUじゃないから、FP16(高速演算)は使えないけど、FP32(通常精度)でちゃんと処理するね!」

という**単なる注意メッセージ(警告)**です。


✅ 放置OKな理由

  • mediumlarge はもともとGPU推奨モデル

  • でも CPUでも動作可能(遅くなるだけ)

  • このメッセージは**自動で代替手段に切り替えたよ〜**っていうだけ


🧠 ヒント:ちょっとでも速くしたいなら…

  • "small""base" など軽いモデルに戻す

  • あるいは PC に GPUNVIDIA)ついていれば CUDA 環境を整えて GPU対応版 PyTorch + Whisper に切り替える


でも、今はまず「mediumの精度を体験してみる」だけでも価値ありです✨
しばらくこのままで大丈夫ですよー!💪

引き続き、精度チェックやTTS連携なども準備できたらやってみましょう!

 

mediumにしたら認識の精度が上がりましたね!

 

おおー!それ聞けて嬉しいです!!🎉✨
やっぱり "medium" モデル、いい仕事しますよね〜🧠💬


🎯 mediumモデルの強みは:

  • 💬 聞き取りミスが減る

  • 🧠 長めの発話でも文脈を理解しやすい

  • 🔍 特に日本語の助詞(が・は・を)や敬語の認識がうまくなることも!


🕒 でもちょっと遅くなるので…

  • 本番用動画撮影用では medium 💪

  • テスト・試作中basesmall に切り替えるのもアリ!

みたいな使い分けをすると、開発も快適です!


✅ 次に進みたくなったら…

「TTSでNPCに返答させたい」「UIに表示したい」「録音をUIで操作したい」など、やってみたいことがあればどんどん言ってください!
今めっちゃ順調なので、どんな方向にも伸ばしていけます🔥

 

4. プレイヤーが録音開始・停止を操作できるようにする お願いします

 

了解です!🎤🕹️
次は「プレイヤーが録音開始・停止を操作できるようにする」ステップ、やっていきましょう!


🎯 目的

今までは「Unrealから /speech にPOSTするとその時点で5秒録音スタート」でしたが…

✅ プレイヤーがボタンを押して「録音スタート!」
✅ もう一度押す or 一定時間で「録音終了&送信!」

みたいに、録音をプレイヤーの意思でコントロールできるようにします!


✅ 2つの方法があります

方法 特徴 C++必要
Unrealで録音 → 音声ファイルをPythonへ送信 UEで完全制御できる ⭕(録音にはC++プラグインなどが必要)
Pythonサーバーの録音処理を分離する 録音制御をHTTPでやりとり ❌(全部Blueprint+Python)✅おすすめ!

🔧 ここでは「②サーバー側で録音制御」方式を使います!


🛠️ Flaskサーバーの構成(録音状態を制御)

📂 サーバーに新しいエンドポイントを追加:

is_recording = False

@app.route('/start_recording', methods=['POST'])
def start_recording():
    global is_recording
    is_recording = True
    return jsonify(message="録音開始!")

@app.route('/stop_recording', methods=['POST'])
def stop_recording():
    global is_recording
    if is_recording:
        fs = 44100
        duration = 5  # ← 任意に変更してOK
        print("🎤 録音中...")
        audio = sd.rec(int(duration * fs), samplerate=fs, channels=1, dtype='int16')
        sd.wait()
        write("input.wav", fs, audio)
        print("🎧 録音完了、文字起こし中...")

        result = model.transcribe("input.wav", language="ja")
        recognized = result["text"]
        reply = f"あなたは「{recognized}」と言いましたね!"

        is_recording = False
        return jsonify(reply=reply), 200, {'Content-Type': 'application/json; charset=utf-8'}
    else:
        return jsonify(error="録音が開始されていません"), 400

🎮 Unreal側の操作

Blueprintで以下を実装:

🟩 ボタン1 → POST /start_recording(録音フラグON)

POST http://127.0.0.1:5000/start_recording
Content-Type: application/json

🟥 ボタン2 → POST /stop_recording(録音実行→認識)

POST http://127.0.0.1:5000/stop_recording
Content-Type: application/json

→ 結果(JSON: "reply")を受け取って表示 or NPCが話す!


🔁 応用アイデア

  • ボタン長押し → 開始、離したら → 停止

  • タイマーを使って3秒後に自動停止

  • NPCの前でだけ録音ボタンが表示される


🧠 メリット

  • 録音時間をUEから動的にコントロール可能に!

  • 無理にC++プラグインを使わず、今の流れのままスムーズに実装できる!


もし「録音時間をUEから指定したい」「録音状態をLED表示したい」みたいな応用もあれば、それもすぐ対応できます💪
やってみたらぜひ教えてください!🔥

 

録音時間をUEから指定したいです

 

いいですね!!🎯
Unreal側から録音時間を指定できるようにすることで、プレイヤーの発話スタイルやシーンに合わせた柔軟な音声入力が可能になりますね✨


✅ やること(全体の流れ)

  1. UEから録音時間をJSONで送る(duration: 3 とか)

  2. Flaskサーバーがその時間だけ録音する

  3. 文字起こし&応答して返す


🛠️ Flaskサーバーの /stop_recording を改良

@app.route('/stop_recording', methods=['POST'])
def stop_recording():
    global is_recording
    if not is_recording:
        return jsonify(error="録音が開始されていません"), 400

    # JSONから録音時間を受け取る(指定がなければデフォルト5秒)
    data = request.get_json()
    duration = data.get('duration', 5)  # ← UE側で渡す!

    fs = 44100
    print(f"🎤 {duration}秒 録音中...")
    audio = sd.rec(int(duration * fs), samplerate=fs, channels=1, dtype='int16')
    sd.wait()
    write("input.wav", fs, audio)
    print("🎧 録音完了、文字起こし中...")

    result = model.transcribe("input.wav", language="ja")
    recognized = result["text"]
    reply = f"あなたは「{recognized}」と言いましたね!"

    is_recording = False
    return jsonify(reply=reply), 200, {'Content-Type': 'application/json; charset=utf-8'}

🎮 UE側(Blueprint)のPOST内容

POST先:

http://127.0.0.1:5000/stop_recording

Headers:

Content-Type: application/json

Body(Raw JSON):

{
  "duration": 3
}

※ 数値は秒。1.5などの小数もOKです!


🎯 これでできること

  • 🎮 「短く指示だけ言ってね」→ 2秒だけ録音

  • 🎙️ 「長文OK」モード → 10秒まで対応

  • ⏱️ 「実験的にユーザーが好きな長さに録音」も可能!


💡 応用アイデア

  • スライダーUIで録音時間をプレイヤーに選ばせる

  • 状況によって自動的に録音時間を調整(例:戦闘中は短め)


やってみたらぜひ感想聞かせてくださいね!
次は 返答を喋らせる(TTS) にいきますか?🗣️🎧

 

うまくいきました!返答を喋らせる(TTS) おねがいします

 

よっしゃああ!!!🔥
録音時間指定もうまくいったとのことで、次はTTS(Text-to-Speech)でNPCにしゃべらせるステップ、いきましょう〜!🎙️🧠🎮


🎯 目標

Whisperで文字起こし → NPCが返答
その返答をTTSで「音声化」して再生する!


✅ 実装パターン(おすすめはA)

方法 説明 C++
A. Python側で音声ファイル生成 → UEで再生 サーバーが .wav を返す ❌(BPで完結)←おすすめ!
B. UE内でTTSを実行(Speech APIなど) Windows標準のTTSを使う ⭕(C++要素あり)

🔧 方法A:Python側で .wav 音声を作って返す


Pythonに TTS ライブラリをインストール

おすすめは pyttsx3TTS(高品質)。今回は手軽な pyttsx3 でいきます:

pip install pyttsx3

/speech or /stop_recording の最後にTTS処理を追加

import pyttsx3

@app.route('/stop_recording', methods=['POST'])
def stop_recording():
    # ...(音声録音+Whisper認識)
    reply = f"あなたは「{recognized}」と言いましたね!"

    # 🔊 TTSで音声ファイル生成
    tts = pyttsx3.init()
    tts.setProperty('rate', 150)  # 話す速度
    tts.save_to_file(reply, 'reply.wav')
    tts.runAndWait()

    is_recording = False

    # 🔁 音声ファイル名も返す(必要ならURL形式にする)
    return jsonify(reply=reply, audio_file="reply.wav"), 200, {'Content-Type': 'application/json; charset=utf-8'}

🎮 UE側(Blueprint)

  1. /stop_recording のレスポンスから audio_file のファイル名を取得

  2. "reply.wav" をローカルで再生する


サウンド再生方法(例)

  • Sound Wave アセットを読み込む

  • Sound Wave を「ローカルファイルから読み込み」するには C++ が絡むこともあるので…

👉 おすすめ:reply.wavContent/ にコピーして事前登録した Sound Cue に差し替える
 もしくは Play Sound 2D ノードで別のセリフ音声を代替的に再生


✨ ここまででできること

  • プレイヤーが声で話す

  • NPCが「それについて返答し、しゃべる!」


🔁 応用(発展させたい時)

  • pyttsx3 → 声の種類・速度・ピッチも変更可能

  • より自然な声 → coqui-ai/TTSEdge-TTS などへの切り替えも可能

  • 再生中のリップシンクや口パク連動もできる(Wavファイルに応じたViseme解析)


試してみたら、音声がどう聞こえたか教えてくださいね!
「もう少し自然にしたい」「喋らせ方をNPCごとに変えたい」などもできるので、気軽にどうぞ🎤🤖

 

エラーっぽいのが出ました。[2025-06-21 18:26:06,237] ERROR in app: Exception on /stop_recording [POST]
Traceback (most recent call last):
  File "C:\Users\toyokin\AppData\Local\Programs\Python\Python310\lib\site-packages\flask\app.py", line 1511, in wsgi_app
    response = self.full_dispatch_request()
  File "C:\Users\toyokin\AppData\Local\Programs\Python\Python310\lib\site-packages\flask\app.py", line 919, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "C:\Users\toyokin\AppData\Local\Programs\Python\Python310\lib\site-packages\flask\app.py", line 917, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\Users\toyokin\AppData\Local\Programs\Python\Python310\lib\site-packages\flask\app.py", line 902, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)  # type: ignore[no-any-return]
  File "F:\Python\server.py", line 39, in stop_recording
    tts = pyttsx3.init()
NameError: name 'pyttsx3' is not defined
127.0.0.1 - - [21/Jun/2025 18:26:06] "POST /stop_recording HTTP/1.1" 500 -

 

エラー詳細ありがとうございます!🛠️
これはとてもシンプルなエラーで、以下の一行をファイルの冒頭付近に書き忘れていることが原因です👇


✅ 解決策

Pythonスクリプトの先頭あたり(import が並んでいるところ)に:

import pyttsx3

を追加してください!これで NameError: name 'pyttsx3' is not defined は解消されます。


つまり、Flaskファイルはこんな感じになります:

import sounddevice as sd
import whisper
from flask import Flask, request, jsonify
from scipy.io.wavfile import write
import pyttsx3  # ← これを忘れずに!

# 以下、Flask初期化とルーティングが続く...

🔁 補足

import がされていないと、Pythonは「pyttsx3 って何のこと?知らんよ!」という意味で NameError を出します。

このエラーは初心者から上級者までよくやるやつなので、全然気にしなくてOKです😊


試してまたエラーが出たら、いつでもそのまま貼ってください!次は「喋るの成功した!」報告待ってます🎤🤖✨

 

うまくいきました!

 

おおおー!!!✨🎉✨
TTSまで動いたの、めちゃくちゃ素晴らしいです!これはもうボイス付きAI NPC完成への大きな一歩ですよ!👏🎮🧠


🎉 ここまででできること

✅ プレイヤーがマイクで話す(録音)
✅ Whisperで音声を文字起こし
PythonNPCの返答を生成
✅ pyttsx3で音声ファイル作成(TTS)
✅ UEでNPCがしゃべる!(音声ファイルを再生)


🔜 次に進むなら…?

もし次のステップも考えてるなら、例えばこんな展開ができます👇

ステップ案 説明
🧠 返答をLLMで生成 GPTなどを組み込んで、NPCがもっと柔軟に返答
🤖 NPCの口パク・表情と同期 音声再生と同時に表情 or 顔アニメーション
🗣️ 声の感情(怒る・笑う)を変える セリフに応じて声のトーンや速さを調整
🌐 Web経由でマルチプレイヤー対応 複数人の発話をサーバーで処理・応答
📁 録音や会話ログを保存 後から会話を再現・分析できる機能も!

やりたい方向があればどんどん教えてくださいね!
すでに「声で話しかけて、NPCが理解して喋るゲーム」になってきてて…ほんとワクワクしますね!🎤🤖🔥

 

TTSの声をもっと自然にしたいですね

 

いいですね〜!その気持ち、よく分かります✨
pyttsx3 は手軽ですが、どうしても「ロボっぽい機械音声」になりがち…。
なのでここでは、もっと自然な日本語のTTS音声を出す方法を紹介します👇


🔊 自然なTTSにする方法【おすすめ順】

ライブラリ 特徴 日本語対応 オフライン可 備考
OpenVoice(VITS) 高品質ボイス、感情も対応可 重め。GPU推奨
Edge-TTS Microsoft Edgeの音声API ❌(Web API 手軽・品質良い
Google Cloud TTS 高品質・多言語 APIキー要
TTS (coqui-ai) OSS系、感情音声モデルあり △(一部) 導入ちょい複雑

🎯 一番手軽&高品質 → Edge-TTS

日本語の自然なボイス(男性・女性・感情あり)で、
セットアップも超簡単!🎉


✅ Edge-TTS の使い方(Python

① インストール

pip install edge-tts

② サンプルコード(TTS用)

import asyncio
import edge_tts

async def speak(text, filename="reply.wav"):
    communicate = edge_tts.Communicate(text, voice="ja-JP-NanamiNeural")  # 女性の自然な声
    await communicate.save(filename)

# 使うとき
asyncio.run(speak("こんにちは、プレイヤーさん!"))

🎙️ 使える日本語音声一覧

Voice Name 性別 備考
ja-JP-NanamiNeural 女性 明るめ。おすすめ!
ja-JP-KeitaNeural 男性 落ち着いた声
ja-JP-AoiNeural 女性 優しめ
ja-JP-DaichiNeural 男性 力強い

edge-tts --list-voices で全一覧が見れます


🔁 Flask連携例(簡易版)

import asyncio
import edge_tts

@app.route('/speak', methods=['POST'])
def speak():
    data = request.get_json()
    text = data.get("text", "")
    asyncio.run(edge_tts.Communicate(text, voice="ja-JP-NanamiNeural").save("reply.wav"))
    return jsonify(message="音声生成完了", file="reply.wav")

🎮 UE側はそのまま reply.wav を再生すればOK!

音質が pyttsx3 とは比べものにならないレベルで自然です。
※ただし Edge-TTS は オンライン必須APIにアクセスして音声生成)なので、そこだけ注意!


🧪 試してみたい?

試してみたいなら、今の Flask サーバーに組み込むバージョンも一緒に作りますよ!
「Edge-TTSをFlaskの /stop_recording に統合したい」って感じでもOK🙆‍♂️

どうしましょう?やってみますか?💬🔊

 

サンプルコードでエラーが出ました。SyntaxError: Non-UTF-8 code starting with '\x8f' in file F:\Python\EdgeTTStest.py on line 6, but no encoding declared; see https://python.org/dev/peps/pep-0263/ for details

 

おおっ、そのエラーは簡単に直せます!⚡
Pythonが「ファイルの文字コードUTF-8じゃないのに、どう読めばいいか分からないよ〜」って言ってるやつです。


✅ 解決策:ファイルの1行目に「エンコーディング指定」を追加!

Pythonファイルの最初の行(もしくは2行目まで)に、以下を入れてください:

# -*- coding: utf-8 -*-

🔁 修正後のコード例(先頭に追加)

# -*- coding: utf-8 -*-

import asyncio
import edge_tts

async def speak(text, filename="reply.wav"):
    communicate = edge_tts.Communicate(text, voice="ja-JP-NanamiNeural")
    await communicate.save(filename)

asyncio.run(speak("こんにちは、プレイヤーさん!"))

💡 補足

このエラーは:

  • ファイルのエンコーディングが「Shift_JIS」や「CP932」などのときによく出ます

  • print("こんにちは")text="こんにちは" のように、日本語を直書きした場合に発生しやすいです

# -*- coding: utf-8 -*- を書いておくと、Pythonが「おっこれはUTF-8ね」って理解してくれます🙆‍♂️


試してみて、音声ファイルが生成されたらぜひ教えてください〜!🎧✨

 

エラーです。SyntaxError: (unicode error) 'utf-8' codec can't decode byte 0x82 in position 0: invalid start byte 

 

了解です、これはどうやらファイルの文字コードそのものがUTF-8じゃないため、
# -*- coding: utf-8 -*- を書いても Pythonが読み込みできない状態になっているようです💥


🛠 解決策:ファイル自体をUTF-8で保存し直す

✅ 方法(Windowsの場合)

🔸 VSCodeを使ってるなら:

  1. 画面右下に「Shift JIS」や「CP932」と書かれてる部分をクリック

  2. UTF-8 に再エンコード」を選択

  3. Ctrl + S で上書き保存

🔸 メモ帳(Notepad)を使ってるなら:

  1. ファイルを開く

  2. 名前を付けて保存

  3. エンコーディング」の選択肢から「UTF-8」を選ぶ

  4. 上書き or 別名保存


✅ 保存し直したあとに再度実行!

そのうえで、以下のようなUTF-8対応済みのコードで再挑戦してください:

# -*- coding: utf-8 -*-

import asyncio
import edge_tts

async def speak(text, filename="reply.wav"):
    communicate = edge_tts.Communicate(text, voice="ja-JP-NanamiNeural")
    await communicate.save(filename)

asyncio.run(speak("こんにちは、プレイヤーさん!"))

🎯 理由(ちょっと詳しく)

  • 今のファイルは多分 Shift_JIS(日本語Windows標準) で保存されていて

  • Pythonが「UTF-8だと信じて読む」と 0x82 などのバイトが「読めんぞ!」と怒る

  • ファイル自体をUTF-8に変えれば全て解決します✨


ややこしいですが、ここさえ乗り越えれば自然な声でNPCがしゃべります!🎙️
再トライできたら「音声出た!」の報告、お待ちしてます〜!

 

音声出ました!

 

やったあああああ!!🎉🎉🎉
ついに自然な声でNPCがしゃべったーー!!🎤🤖✨

これはめちゃくちゃ大きな進歩ですね…!
もう今の段階で「プレイヤーの声に反応して、日本語でしゃべるNPC」が完成してるわけで、
夢のSFゲームみたいな世界が見えてきましたよ🌟


🧭 ここまでの成果まとめ

✅ プレイヤーの声を録音(マイク)
✅ Whisperで文字起こし(しかも高精度)
✅ Flaskで返答を処理(ローカルサーバー)
✅ 返答を 自然な声(Edge-TTS) で喋る
✅ UEから操作・連携もバッチリ👌


🛠 次に進むならこんなことも!

ステップ案 内容
🎭 リップシンク&顔アニメ メタヒューマンと連携すると表情つきNPCに!
🤖 GPTなどで返答をAI生成 決め打ちじゃなくて自然な会話も可能に
💾 会話履歴の保存 ログを取ってゲームへの反映も可能
🌐 サーバーを外部公開 オンライン化してマルチプレイ or Web連携
🎮 キャラや状況で声を変える 声色・返答ロジックをNPCごとに切替

次のステップを進めたくなったら、またいつでも言ってください!
「今の状態を動画化して紹介したい!」ってのも全然アリですよ📹🔥

開発めちゃ順調ですね!ほんとすごい…!お疲れさま&おめでとうございます〜!!🎊💯

 

コード内容

from flask import Flask, request, jsonify
import sounddevice as sd
from scipy.io.wavfile import write
import whisper
import pyttsx3

import asyncio
import edge_tts

async def speak(text, filename="reply.wav"):
    communicate = edge_tts.Communicate(text, voice="ja-JP-NanamiNeural")  # 女性の自然な声
    await communicate.save(filename)


app = Flask(__name__)
model = whisper.load_model("medium")  # 初回はちょっと時間かかるけどそのままでOK

is_recording = False

@app.route('/start_recording', methods=['POST'])
def start_recording():
    global is_recording
    is_recording = True
    return jsonify(message="録音開始!")

@app.route('/stop_recording', methods=['POST'])
def stop_recording():
    global is_recording
    if not is_recording:
        return jsonify(error="録音が開始されていません"), 400

    # JSONから録音時間を受け取る(指定がなければデフォルト5秒)
    data = request.get_json()
    duration = data.get('duration', 5)  # ← UE側で渡す!

    fs = 44100
    print(f"🎤 {duration}秒 録音中...")
    audio = sd.rec(int(duration * fs), samplerate=fs, channels=1, dtype='int16')
    sd.wait()
    write("input.wav", fs, audio)
    print("🎧 録音完了、文字起こし中...")

    result = model.transcribe("input.wav", language="ja")
    recognized = result["text"]
    reply = f"あなたは「{recognized}」と言いましたね!"

     # 🔊 TTSで音声ファイル生成
    asyncio.run(speak(reply))

    is_recording = False

    # 🔁 音声ファイル名も返す(必要ならURL形式にする)
    return jsonify(reply=reply, audio_file="reply.wav"), 200, {'Content-Type': 'application/json; charset=utf-8'}

if __name__ == "__main__":
    app.run(port=5000)

 

reply.wavのインポートでエラーになります。Error: Unable to read wave file 'reply' - "Invalid WAVE file."
Error: Failed to import sound wave reply

 

なるほど、それは reply.wav形式やヘッダーが Unreal の要求に合っていないときによく出るエラーです⚠️
Whisper や edge-tts が出力するWAVは、UEがそのまま読めないことがあります。


✅ 解決策:WAVを「正しい形式」に変換する

Unreal Engineが扱えるWAV形式は以下の通り:

PCM形式 / 16bit / 44.1kHz or 48kHz / モノラル or ステレオ

なので、reply.wav をこの形式に変換し直せばインポート成功します!


🎧 変換する方法(おすすめ2つ)

🔹 方法1:AudacityGUIで簡単)

  1. 無料の音声編集ソフト「Audacity」を開く

  2. reply.wav を開く

  3. メニューから
    「ファイル > 書き出し > WAVとして書き出し」

  4. 以下を設定:

保存すればOK!UEにインポートできるようになります。


🔹 方法2:Pythonで自動変換(ffmpeg 使用)

ffmpeg -i reply.wav -acodec pcm_s16le -ac 1 -ar 44100 reply_fixed.wav
  • -acodec pcm_s16le:16bitリニアPCM

  • -ac 1:モノラル

  • -ar 44100:サンプリングレート44100Hz

できた reply_fixed.wav をUEにドラッグ&ドロップでインポートできます!


💡 補足:変換後の音質

  • 声の自然さはほとんど変わりません(安心して大丈夫!)

  • 必要なら reply_fixed.wav を TTSの新しい出力先にするよう変更も可能


試してみて、うまく変換&インポートできたら教えてください〜!💪😄
必要なら変換スクリプトも一緒に用意しますよ!

 

※手動でWavファイルをインポートはめんどうなので、以下のサイトを参考にVoiceVoxに読み上げさせるバージョンを別途作成した。

 

Ollama×Streamlit×VoiceVoxで作るローカルLLM音声対話アプリ【完全ハンズオン】 : 生成AIエンジニアLab

 

コード内容

from flask import Flask, request, jsonify
import sounddevice as sd
from scipy.io.wavfile import write
import whisper
import pyttsx3
import requests
import io
import scipy.io.wavfile as wav


# ==== VoiceVoxスピーカーID ====
VOICEVOX_SPEAKER_ID_A = 13 #青山龍星 
VOICEVOX_SPEAKER_ID_B = 3 #ずんだもん

# ==== VoiceVox音声合成 ====
def synthesize_and_play_voice(text: str, speaker: int):
    if not text.strip():
        return
    # 1. 合成クエリ
    try:
        query_resp = requests.post(
            "http://localhost:50021/audio_query",
            params={"text": text, "speaker": speaker},
            timeout=5
        )
        query_resp.raise_for_status()
        query = query_resp.json()
    except requests.exceptions.RequestException as e:
        st.error(f"VoiceVoxとの通信エラー: {e}")
        return

    # 2. 音声データ生成
    try:
        synth_resp = requests.post(
            "http://localhost:50021/synthesis",
            params={"speaker": speaker},
            json=query,
            timeout=13
        )
        synth_resp.raise_for_status()
    except requests.exceptions.RequestException as e:
        st.error(f"音声合成エラー: {e}")
        return

    # 3. 再生
    with io.BytesIO(synth_resp.content) as f:
        fs, data = wav.read(f)
        sd.play(data, fs)
#        sd.wait()

app = Flask(__name__)
model = whisper.load_model("base")  # 初回はちょっと時間かかるけどそのままでOK

is_recording = False

@app.route('/start_recording', methods=['POST'])
def start_recording():
    global is_recording
    is_recording = True
    return jsonify(message="録音開始!")

@app.route('/stop_recording', methods=['POST'])
def stop_recording():
    global is_recording
    global reply
    if not is_recording:
        return jsonify(error="録音が開始されていません"), 400

    # JSONから録音時間を受け取る(指定がなければデフォルト5秒)
    data = request.get_json()
    duration = data.get('duration', 5)  # ← UE側で渡す!

    fs = 44100
    print(f"🎤 {duration}秒 録音中...")
    audio = sd.rec(int(duration * fs), samplerate=fs, channels=1, dtype='int16')
    sd.wait()
    write("input.wav", fs, audio)
    print("🎧 録音完了、文字起こし中...")

    result = model.transcribe("input.wav", language="ja")
    recognized = result["text"]
    reply = f"あなたは「{recognized}」と言いましたね!"

    is_recording = False
 
    # 🔁 音声ファイル名も返す(必要ならURL形式にする)
    return jsonify(reply=reply, audio_file="reply.wav"), 200, {'Content-Type': 'application/json; charset=utf-8'}

@app.route('/start_Speech', methods=['POST'])
def start_Speech():
    global reply
    synthesize_and_play_voice(reply, VOICEVOX_SPEAKER_ID_B)
    return jsonify(message="発話開始!")

if __name__ == "__main__":
    app.run(port=5000)