Log

いろいろ

AIと話したい

ポッドキャストのゲストにAIを迎えてお話ししたいなと思いました。いきなり全てを作るのは大変なので、まずは一往復のやりとりを実装してみます。

実装

ざっくりフロー。

  • Record
  • Speech To Text: 私の音声をテキストに変換
  • Chat: テキストをChatGPTに入力してレスポンスを得る
  • Text To Speech: ChatGPTのレスポンスを音声に変換
  • Play

RecordとPlayは読んで字の如く。なお、簡易に実装するために録音時間を3秒固定としています。主要な処理は真ん中3つの処理。

Speech To Text

音声を文字起こしするサービスはいろいろあると思います。

今回はOpenAIの「Speech to text」というAPIを利用しました。

OpenAI API | Speech to text

ほぼサンプルコードのままです。

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しておきましょう。

GPT-4 API waitlist

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を使うとスマートに書けそうです。

GitHub - Uberi/speech_recognition: Speech recognition module for Python, supporting several engines and APIs, online and offline.

デモ

ノーカット。

Q: アイドルって楽しい?
A: 私はAIプログラムであり、感情を持たないため、アイドルが楽しいかどうかについて個人的な意見を持つことはできません。ただし、多くの人々がアイドルを好み、彼らの音楽、ダンス、ファッション、新しい文化を楽しんでいます。アイドル活動に携わる人々にとっても、その仕事が楽しいと感じる人は多いでしょう。

ChatGPTのレスポンスと音声合成に時間がかかるため、AIが喋り出すまでに30秒も待ちます。ポッドキャストなら編集で何とでもできますが、会話をしている感じはしないですね。

おわり

100年ぶりにPythonを書きました。前回Pythonを書いた時も同じことを言ってるんだろうな。

さて、今回はVOICEVOXを利用して音声を読み上げました。これは、日本語を学習させてカスタマイズ音声を作り出すことが簡単にできそうなText To Speechサービスが見当たらなかったからです。声が星野アイになるのはもう少し先の話。*1

*1:学習データの問題があるから一生実現しないかもね。