この記事でわかること
以前、selemiumを使ってWeb版のChatGPTの解答をスクレイピングして読み上げてもらうツールを作ったのですが、自動的に読み上げてもらうまで作れなかったので、こちらで実装しました。
目次
今回用意した環境
開発環境構築
以下の手順で行います。
- Voice Vox (Python Wheel版) のインストール
- ONNX Runtime のインストール
- Open JTalkのインストール
- PyAudioのインストール
- OpenAI (Pythonモジュール) のインストール
完成後のディレクトリはこのようになっている想定です
$ tree -L 1 . ├── README.md ├── main.py ├── onnxruntime-osx-x86_64-1.13.1 ├── onnxruntime-osx-x86_64-1.13.1.tgz ├── open_jtalk_dic_utf_8-1.11 └── open_jtalk_dic_utf_8-1.11.tar.gz 2 directories, 6 files
Voice Vox (Python Wheel版) のインストール
https://github.com/VOICEVOX/voicevox_core/releases
上のリンクから最新バージョンのものを利用したいと思います。(2023-07-07現在では0.14.4)
今回はMacOSで Python wheel
を利用したいと思います。私はIntel Macを利用しているのでvoicevox_core-0.14.4+cpu-cp38-abi3-macosx_10_7_x86_64.whl
を利用します。
以下のコマンドをターミナルに入力してインストールします。
pip3 install https://github.com/VOICEVOX/voicevox_core/releases/download/0.14.4/voicevox_core-0.14.4+cpu-cp38-abi3-macosx_10_7_x86_64.whl
ONNX Runtime のインストール
ONNX Runtime は、ONNX モデルを運用環境にデプロイするためのハイパフォーマンスの推論エンジンです。 クラウドとエッジの両方に最適化され、Linux、Windows、Mac で動作します。
VOICEVOXでも利用されているようです。
現状、VOICEVOXでは ONNX Runtime v1.13.1が利用されているのでこれを導入します。
https://github.com/microsoft/onnxruntime/releases/tag/v1.13.1
私はIntel Macを利用しているので、onnxruntime-osx-x86_64-1.13.1.tgz
をダウンロードします。
tgzファイルを適当ディレクトリに入れて以下のコマンドで解凍します
tar -xvf ***.tgz
※この手順を飛ばすと出るエラー
この手順を飛ばすと以下のエラーが出るので注意。(検索に引っ掛かるようにあえて残しておきます。私は飛ばして失敗したので...)
library not loaded @rpath/libonnxruntime.1.13.1.dylib
Open JTalkのインストール
Open JTalkは日本語音声合成システムです。このソフトウェアは修正BSDライセンスの下でリリースされています。
https://sourceforge.net/projects/open-jtalk/
こちらからダウンロードしてください。
ダウンロードしたtgzファイルを適当ディレクトリに入れて以下のコマンドで解凍します
tar -xvf ***.tgz
この手順を飛ばすと出る可能性のあるエラー
playsound is relying on a python 2 subprocess. Please use `pip3 install PyObjC` if you want playsound to run more efficiently. Traceback (most recent call last): File "[path]/ChatgptToVoiceVox/main.py", line 28, in <module> main() File "[path]/ChatgptToVoiceVox/main.py", line 23, in main audio_query: AudioQuery = core.audio_query(DEMO_TEXT, SPEAKER_ID) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ voicevox_core.VoicevoxError: OpenJTalkの辞書が読み込まれていません
PyAudioのインストール
- 今回はPyAudioを使いました。
- wavファイルを作成せず、メモリを利用して simpleaudio で 生成した音声データを再生するとノイズが除去できなかったためこちらを利用しています。
brew install PortAudio pip3 install pyaudi
openai (Pythonモジュール)をインストール
pip3 install openai
ChatGPT API キー取得
2023-07-07現在では5$まで無料のようです。tokenをある程度つかっても限界にはならなさそうで、クレカ登録も不要だったので、利用します。
基本的には他のサイトでも紹介されているとおりの方法で取得できます。
4. Python 実装
まずは作成したソースコードをそのまま貼り付けます。のちほど解説していきます。
あと正直いらいないものも含まれているのですみません...
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from __future__ import annotations import io import os import wave import re from time import sleep import asyncio from ctypes import CDLL from pathlib import Path # ONNX Runtime のダイナミックライブラリをimport CDLL(str(Path('onnxruntime-osx-x86_64-1.13.1/lib/libonnxruntime.dylib').resolve(strict=True))) # OPEN JTalk のパス open_jtalk_path=Path('open_jtalk_dic_utf_8-1.11') from voicevox_core import AudioQuery, VoicevoxCore import pyaudio import openai SPEAKER_ID = 29 DEMO_TEXT = "こんにちは。こちらはテスト音声です。" DEMO_LONG_TEXT = '''はい、自己紹介させていただきます。 初めまして、私はAIのアシスタントです。私の名前はOpenAI GPT-3です。私は自然言語処理を用いて、様々な質問や会話に対応することができます。 私は多くの分野について知識を持っており、文法や表現にも精通しています。また、日本語だけでなく、英語や他の言語にも対応することができます。 私の目的は、ユーザーのお手伝いをすることです。質問や疑問があれば、どんなことでもお気軽にお聞きください。私は最善の答えを提供するために努力します。 どうぞよろしくお願いします。 ''' FAIL_TEXT = "チャットGPTでのテキスト生成に失敗しました。しばらく待ってから試してみてください。" MODEL = "gpt-3.5-turbo" openai.api_key = "" # [MUST] please input your api_key def generate_text(prompt, conversation_history): try: conversation_history.append({"role": "user", "content": prompt}) response = openai.ChatCompletion.create( model=MODEL, messages=conversation_history, temperature=0.2, # 創造性の指標 max_tokens=2048, ) content = response.choices[0].message["content"] # print(content) # 会話履歴を追加 conversation_history.append({"role": "assistant", "content": content}) return content except Exception as e: print(e) return FAIL_TEXT def play_wavfile(wav_file: bytes): # Defines a chunk size chunk = 1024 p = pyaudio.PyAudio() # Bytes型の音声データをWave_read型に変換する wr: wave.Wave_read = wave.open(io.BytesIO(wav_file)) # wavファイルを書き込むストリームを作成する。 # 出力を "True "に設定すると、サウンドは録音されるのではなく、"再生 "される。 stream = p.open( format=p.get_format_from_width(wr.getsampwidth()), channels=wr.getnchannels(), rate=wr.getframerate(), output=True ) # Read data in chunks data = wr.readframes(chunk) # Play the sound by writing the audio data to the stream while data : stream.write(data) data = wr.readframes(chunk) sleep(0.01) stream.stop_stream() stream.close() p.terminate() def speakToChat(answer: str = None) -> None: core: VoicevoxCore = VoicevoxCore(open_jtalk_dict_dir=open_jtalk_path) core.load_model(SPEAKER_ID) if not answer == None: sentence_list = re.split("。", answer) else: sentence_list = [FAIL_TEXT] for sentence in sentence_list: sentence_sub = sentence.strip() output = 'No.7: ' + sentence_sub output = output if output[-1] == '?' else output + '。' print(output) wave_bytes: bytes = core.tts(sentence_sub, SPEAKER_ID) play_wavfile(wav_file=wave_bytes) def directSpeakToChat(book: str = None): core: VoicevoxCore = VoicevoxCore(open_jtalk_dict_dir=open_jtalk_path) core.load_model(SPEAKER_ID) if not book == None: wave_bytes: bytes = core.tts(book, SPEAKER_ID) play_wavfile(wav_file=wave_bytes) else: sentence_list = [FAIL_TEXT] if __name__ == '__main__': # 会話履歴を格納するためのリストを初期化 conversation_history = [] while True: # ユーザーに質問を入力させる input_prompt = input("prompt: ") generated_text = generate_text(input_prompt, conversation_history) speakToChat(answer=generated_text)
実行結果
$ p main2.py prompt: 何か褒めてください! No.7: あなたはとても親切で思いやりのある人です。 No.7: 周りの人々をいつも助けていて、その優しさは本当に素晴らしいです。 No.7: また、あなたの明るい笑顔は周りの人々に元気を与えています。 No.7: あなたのポジティブなエネルギーはとても魅力的で、人々を引き付ける力があります。 No.7: あなたの努力と頑張りは誰もが認めるべきです。 No.7: 素晴らしい人間性を持っているあなたは、周りの人々にとって本当に大切な存在です。
やり残したこと
- 実装の説明が足りていないです。
- 文章のセパレータが「。」になっているが「!」「?」は大丈夫なのか?
- 音声生成と音声読み上げをマルチスレッドにしたら高速化できそうだがスレッドセーフな実装をするのはコストかかりそう?
- コンソール上でのチャットだがWebGLなどに組み込めないか?そしたらLive2Dが喋っているように見えそう?