ポッドキャストのゲストにAIを迎えてお話ししたいなと思いました。いきなり全てを作るのは大変なので、まずは一往復のやりとりを実装してみます。
実装
ざっくりフロー。
- Record
- Speech To Text: 私の音声をテキストに変換
- Chat: テキストをChatGPTに入力してレスポンスを得る
- Text To Speech: ChatGPTのレスポンスを音声に変換
- Play
RecordとPlayは読んで字の如く。なお、簡易に実装するために録音時間を3秒固定としています。主要な処理は真ん中3つの処理。
Speech To Text
音声を文字起こしするサービスはいろいろあると思います。
今回はOpenAIの「Speech to text」というAPIを利用しました。
ほぼサンプルコードのままです。
def speech_to_text(filepath): file = open(filepath, "rb") transcript = openai.Audio.transcribe("whisper-1", file) return transcript["text"]
Chat
ChatはもちろんChatGPTに担当してもらいます。
OpenAI API | Chat Completions API
こちらもほとんどサンプルコードのままですね。
def chat_gpt(content): response = openai.ChatCompletion.create( # gpt-4を使う場合はwaitlistにJOINする model="gpt-3.5-turbo", messages=[ { "role": "user", "content": content } ] ) return response.choices[0]["message"]["content"].strip()
messages
でキャラクター設定を与えると本人に近づきます。それと、GPT-4 APIを利用できるように、さっさとwaitlistにJOINしておきましょう。
Text To Speech
最後にChatGPTで生成したテキストを読み上げます。
ひとまずVOICEVOXを利用しました。
VOICEVOX | 無料のテキスト読み上げソフトウェア
VOICEVOX(音声合成)をREST-APIで利用する - Qiita
def text_to_speech(text, filepath): params = ( ("text", text), # 「0: 四国めたん・あまあま」 ("speaker", 0) ) headers = { "Content-Type": "application/json" } # 音声合成用クエリ生成 audio_query_response = requests.post( "http://localhost:50021/audio_query", headers=headers, params=params ) # 音声合成 synthesis_response = requests.post( "http://localhost:50021/synthesis", headers=headers, params=params, data=json.dumps(audio_query_response.json()) ) save_wave(filepath, synthesis_response.content, rate=24000)
ローカルでVOICEVOXを起動しておき、
の順にHTTPリクエストを投げるだけです。音声合成のレスポンスをWAVEファイルとして保存します。サンプリングレートが24000Hzである点に注意。
コード全体
import json import os import wave import openai import pyaudio import requests import simpleaudio QUESTION_WAVE_FILE_PATH = "./question.wav" ANSWER_WAVE_FILE_PATH = "./answer.wav" openai.apikey = os.getenv("OPENAI_API_KEY") def record_speech(time, filepath): CHUNK = 2**10 FORMAT = pyaudio.paInt16 CHANNELS = 1 RATE = 44100 audio = pyaudio.PyAudio() stream = audio.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK) # timeの間だけ録音する print("start") frames = [] for i in range(0, int(RATE / CHUNK * time)): data = stream.read(CHUNK) frames.append(data) print("end") stream.stop_stream() stream.close() audio.terminate() save_wave(filepath, b"".join(frames), sampwidth=audio.get_sample_size(FORMAT), rate=RATE) def speech_to_text(filepath): file = open(filepath, "rb") transcript = openai.Audio.transcribe("whisper-1", file) return transcript["text"] def chat_gpt(content): response = openai.ChatCompletion.create( # gpt-4を使う場合はwaitlistにJOINする model="gpt-3.5-turbo", messages=[ { "role": "user", "content": content } ] ) return response.choices[0]["message"]["content"].strip() def text_to_speech(text, filepath): params = ( ("text", text), # 「0: 四国めたん・あまあま」 ("speaker", 0) ) headers = { "Content-Type": "application/json" } # 音声合成用クエリ生成 audio_query_response = requests.post( "http://localhost:50021/audio_query", headers=headers, params=params ) # 音声合成 synthesis_response = requests.post( "http://localhost:50021/synthesis", headers=headers, params=params, data=json.dumps(audio_query_response.json()) ) save_wave(filepath, synthesis_response.content, rate=24000) def save_wave(filepath, frames, channels=1, sampwidth=2, rate=44100): wf = wave.open(filepath, "wb") wf.setnchannels(channels) wf.setsampwidth(sampwidth) wf.setframerate(rate) wf.writeframes(frames) wf.close() def play_wave(filepath): wave_obj = simpleaudio.WaveObject.from_wave_file(filepath) play_obj = wave_obj.play() play_obj.wait_done() def main(): record_speech(3, QUESTION_WAVE_FILE_PATH) question = speech_to_text(QUESTION_WAVE_FILE_PATH) print("Q: ", question) answer = chat_gpt(question) print("A: ", answer) text_to_speech(answer, ANSWER_WAVE_FILE_PATH) play_wave(ANSWER_WAVE_FILE_PATH) if __name__ == "__main__": main()
録音部分でコードが膨れ上がっていますが、SpeechRecognitionを使うとスマートに書けそうです。
デモ
ノーカット。
Q: アイドルって楽しい?
A: 私はAIプログラムであり、感情を持たないため、アイドルが楽しいかどうかについて個人的な意見を持つことはできません。ただし、多くの人々がアイドルを好み、彼らの音楽、ダンス、ファッション、新しい文化を楽しんでいます。アイドル活動に携わる人々にとっても、その仕事が楽しいと感じる人は多いでしょう。
ChatGPTのレスポンスと音声合成に時間がかかるため、AIが喋り出すまでに30秒も待ちます。ポッドキャストなら編集で何とでもできますが、会話をしている感じはしないですね。
おわり
100年ぶりにPythonを書きました。前回Pythonを書いた時も同じことを言ってるんだろうな。
さて、今回はVOICEVOXを利用して音声を読み上げました。これは、日本語を学習させてカスタマイズ音声を作り出すことが簡単にできそうなText To Speechサービスが見当たらなかったからです。声が星野アイになるのはもう少し先の話。*1
*1:学習データの問題があるから一生実現しないかもね。