💡

ChatGPT+Pythonでボイスロイドとリアルタイムで音声会話できるプログラムを作った

2022/12/05に公開1

ChatGPTを試していて、想像以上に受け答えがしっかりしている印象を持ちました。入力と出力を音声に置き換えてアレクサのように応答できれば、音声ベースでAIと対話できるのでは?と思い、プログラムを作ってみました。

作ったもの

プログラムを起動し、音声で問いかけることで音声でメッセージが返ってきます。
音声出力の方法としてボイスロイドを用いました。このツイートの例ではVOICEVOXの「春日部つむぎ」にしゃべってもらっています。

作り方

やることはシンプルで↓のような流れになります。

  • 音声の入力を受けつけ、なるべく正確にテキストに変換する
  • ChatGPTにそのテキストを送信し、返答を受け取る
  • 返ってきたメッセージをVOICEVOXのメッセージ合成APIに送信し、合成後のメッセージを.wav形式で保存する
  • プログラム内でその.wavファイルを再生する

上記の流れをループさせることで、何回でも対話を続けることができます。
あまり馴染みのない処理もあり、そこそこ時間がかかるかもと思っていましたが、必要なパーツは全て公開されていたので2時間程度で完成しました。

↓がコードの全体です。書き捨てる前提で書いているのでいろいろ雑な部分があります。

from revChatGPT.revChatGPT import Chatbot
import os
import speech_recognition as sr
import requests
import json
import wave
import pyaudio

config = {
  "Authorization": "",
  "session_token": ""
}

r = sr.Recognizer()
mic = sr.Microphone()

chatbot = Chatbot(config, conversation_id=None)
chatbot.reset_chat()
chatbot.refresh_session()

print("はじめまして!")

def openWave():
    wf = wave.open("./test.wav", "r")

    p = pyaudio.PyAudio()
    stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                    channels=wf.getnchannels(),
                    rate=wf.getframerate(),
                    output=True)

    chunk = 1024
    data = wf.readframes(chunk)
    while data != b'':
        stream.write(data)
        data = wf.readframes(chunk)
    stream.close()
    p.terminate()

with mic as source:
    r.adjust_for_ambient_noise(source)

    while True:
      try:
        audio = r.listen(source)
        text = r.recognize_google(audio, language='ja-JP')
      except sr.UnknownValueError:
        pass

      resp = chatbot.get_chat_response(text, output="text")

      print(resp['message'])
      print("start")

      res1 = requests.post('http://localhost:50021/audio_query', params={'text': resp['message'], 'speaker': 8})
      res2 = requests.post('http://localhost:50021/synthesis', params={'speaker': 14}, data=json.dumps(res1.json()))

      with open('./test.wav', mode='wb') as f:
        f.write(res2.content)

      print("complete")
      openWave()

処理のそれぞれについて簡単に触れていきます。

音声入力を受け付け、テキストに変換する

音声入力はなるべく正確にテキストに変換しないとこちらの意図が正しくAIに伝わりません。
この点が一番のハードルだと思っていたのですが、speech_recognitionパッケージを使うことであっさり解決できました。

↓のように書くことでプログラム起動後に音声入力を受け付け、しゃべり終わるまで待機してくれます。
変換の精度もかなり高く、よほど滑舌悪く喋らない限りは発声した通りにテキストに変換してくれます。

r = sr.Recognizer()
mic = sr.Microphone()

try:
  audio = r.listen(source)
  text = r.recognize_google(audio, language='ja-JP')
except sr.UnknownValueError:
  pass

ChatGPTにそのテキストを送信し、返答を受け取る

この部分はWeb開発をしている人なら慣れている処理だと思います。revChatGPTというライブラリが公開されているのでそれを利用しました。

返ってきたメッセージをVOICEVOXのメッセージ合成APIに送信し、合成後のメッセージを.wav形式で保存する

VOICEVOXはローカル環境で動作させることができます。起動することでGUIが立ち上がり、そこで各種操作をすることでテキストを音声に変換することができるのですが、起動時にHTTPサーバーがlocalhostに立つので、そこにリクエストを送信することでも音声合成が可能です。

今回のプログラムでもこの方法を用い、ChatGPTからのレスポンスをVOICEVOX APIに送信しています。そしてそのレスポンスをwav形式のファイルに保存しています。

res1 = requests.post('http://localhost:50021/audio_query', params={'text': resp['message'], 'speaker': 8})
      res2 = requests.post('http://localhost:50021/synthesis', params={'speaker': 14}, data=json.dumps(res1.json()))

with open('./test.wav', mode='wb') as f:
  f.write(res2.content)

プログラム内でwavファイルを再生する

あとは保存したwavファイルをプログラム内から再生することで、あたかもボイスロイドが返答しているように見せかけることができます。再生処理はこちらの記事を参考にさせてもらいました。

def openWave():
    wf = wave.open("./test.wav", "r")

    p = pyaudio.PyAudio()
    stream = p.open(format=p.get_format_from_width(wf.getsampwidth()),
                    channels=wf.getnchannels(),
                    rate=wf.getframerate(),
                    output=True)

    chunk = 1024
    data = wf.readframes(chunk)
    while data != b'':
        stream.write(data)
        data = wf.readframes(chunk)
    stream.close()
    p.terminate()

問題点

やりたいことはおおよそ達成できたのですが、2つほど大きな問題があります。

音声を入力してから返答の出力までのラグが大きく、流暢に人と会話している感は味わえない

「ChatGPTにリクエストを送ってからレスポンスが返ってくるまで」、また「VOICEVOXにリクエストを送ってからレスポンスが返ってくるまで」のレイテンシが大きく、音声を入力してから出力されるまで早くても10秒は待つ必要があります。音声合成にかかる時間は対象のテキスト長に比例して長くなるため、ChatGPTが長文を返してきた場合はより時間がかかります。(ツイートの例は動画の尺を考慮して待ち時間部分を削除するよう編集しています)

そのため、まるでそこに人がいるように流暢に会話することは難しいです。このへんは技術の向上によって徐々に解消されていく部分かもしれません。

キャラクター性を持たせることができない

ChatGPTの返答は非常に紳士的で好感を持てるものが多いのですが、それゆえになんらかの特徴を持ったキャラクターとして返答させることができません。

ボイスロイドにはそれぞれキャラクター性があり、このキャラならこういった返事をするよね、という点も非常に大事なポイントなのですが、ChatGPTではそれを満たすことはできませんでした。

また、ざっくりとした雑談をしたいなと思っても、ChatGPT君はあまりにも丁寧な長文メッセージを返してくることが多々あります。まぁそれは人間も同じで、ある意味リアルと言えるかもしれないのですが、やはり期待する人格を再現することができないというのはキャラクターと会話するうえで違和感を感じるポイントでした。

まとめ

今回作ったプログラムは、真剣に会話するのはやや不向きなのですが、何か他の作業をしながらの会話相手にはそこそこ使えるように感じました。
もっと技術が進歩すると、本当に人と対話していると感じるようなAIやプロダクトが出てくるのかもしれません。

Discussion