論語をAIに読ませてポッドキャスト化した
最近、孔子の論語にハマっています。3000年も前に書かれた内容ですが、胸に刺さることが多いです。
全部で500章(500教え)ぐらいあるのですが、ザッと内容を知りたいなと思い、また、前々からLLMの「Text-to-speech (TTS)」を活用したいなとも思っていたので、AI に論語を1章ずつ解説させてみることにしました。
そして、どうせ音声化するなら、論語にちょっと興味がある他の人にも聴いてもらえればなと思い、ポッドキャストとして配信してみることにしました。
以下では、その技術的な部分を共有したいと思います。
事前準備
論語全章のデータ収集
論語は全部で20篇、各篇に10〜30ぐらいの章が収録されていて、合計で500章ぐらいあります。
まず ChatGPT(チャット)を使って、この全章のデータを CSV ファイル形式で取得しました。
quotes
├── 01_学而篇.csv
├── 02_為政篇.csv
├── 03_八佾篇.csv
├── 04_里仁篇.csv
├── 05_公冶長篇.csv
├── 06_雍也篇.csv
├── 07_述而篇.csv
├── 08_泰伯篇.csv
├── 09_子罕篇.csv
├── 10_郷党篇.csv
├── 11_先進篇.csv
├── 12_顔淵篇.csv
├── 13_子路篇.csv
├── 14_憲問篇.csv
├── 15_衛霊公篇.csv
├── 16_季氏篇.csv
├── 17_陽貨篇.csv
├── 18_微子篇.csv
├── 19_子張篇.csv
└── 20_堯曰篇.csv
台本のフォーマット用意
解説台本のフォーマットも、ChatGPT(チャット)を使って、System Prompt として利用できる形で生成してもらいました。
解説音声の生成
以下、スクリプトは Ruby で書いています。
台本の生成
解説台本は OpenAI の API 経由で生成します。
先ほど生成した System Prompt と、篇・章の番号とその原文を含めた User Prompt を渡して、章ごとの台本を生成してもらいます。生成結果は input.txt
に保存します。
require 'dotenv/load'
require 'openai'
require_relative "rongo"
puts "論語のデータを読み込みます..."
rongo = Rongo.new(part_num, chapter_num)
rongo.load_chapter_quote
puts "\n論語のデータを読み込みました:\n#{rongo.part_name} #{rongo.chapter_name}\n#{rongo.quote}"
puts "\nOpenAI API を呼び出します..."
client = OpenAI::Client.new(
access_token: ENV.fetch('OPENAI_API_KEY'),
log_errors: true
)
user_prompt = <<~PROMPT
#{rongo.part_name} #{rongo.chapter_name}「#{rongo.quote}」の論語ラジオの台本を作って。
Max 900 bytes per sentenceのエラーがでないように、1文あたり200文字以内、なるべく短く切る。
PROMPT
response = client.chat(
parameters: {
model: "gpt-4o",
messages: [
{ role: "system", content: File.read("prompts/transcript_system_prompt.txt") },
{
role: "user",
content: user_prompt
}
],
temperature: 0.8
}
)
text = response.dig("choices", 0, "message", "content")
File.write("input.txt", text)
puts "\n台本を生成しました!"
台本の読み上げ(音声ファイルの生成)
台本の読み上げには、Google Cloud の Text-to-Speech AI を使用しています。OpenAI や ElevenLabs も試してみましたが、日本語の場合、イントネーションが不自然だったり、漢字の読み間違いが多かったりしました。試した中だと、Google Cloud が現時点では一番安定していて自然に感じました。
インプットには、はじめ SSML(Speech Synthesis Markup Language)を使っていました。SSML とは、TTS エンジンに、音声の読み上げスタイルを指示するための XML ベースの言語です。SSML を使うと、例えば、以下のように、間や読むペースを細かく指示できます:
<speak>
<p>
こんにちは。
<break time="700ms"/>
アホでも分かる論語ラジオのお時間です。
</p>
<p>
<prosody rate="slow">
今日の論語はこちら。
</prosody>
<break time="600ms"/>
子曰く、「学びて時に之を習う、亦説ばしからずや」
</p>
</speak>
「細かく調整できて、SSML 良さそう!」と思いましたが、Google Cloud の最上位の音声モデルが SSML に対応しておらず、「SSML+下位モデル」より「Text+最上位モデル」の方が全体的な音声の質が高かったので、結局、普通のテキスト形式でいくことにしました。
先ほど生成した台本 input.txt
を Text-to-Speech AI に渡して読み上げてもらいます。出来上がる MP3 ファイルは output/
に保存します。
require "google/cloud/text_to_speech"
# 認証用の環境変数を設定
ENV["GOOGLE_APPLICATION_CREDENTIALS"] = "gen-lang-client.json"
puts "音声合成を開始します..."
client = Google::Cloud::TextToSpeech.text_to_speech
text = File.read("input.txt")
input = { text: text }
voice = {
language_code: "ja-JP",
name: "ja-JP-Chirp3-HD-Alnilam"
}
audio_config = {
audio_encoding: :MP3,
speaking_rate: 1.0,
}
puts "音声合成リクエストを送信..."
# 音声合成リクエスト
response = client.synthesize_speech(
input: input,
voice: voice,
audio_config: audio_config
)
puts "音声合成完了"
# ファイル保存
filename = "output/#{Time.now.strftime('%Y%m%d%H%M%S')}.mp3"
File.open(filename, "wb") do |file|
file.write(response.audio_content)
end
puts "音声ファイルを '#{filename}' に保存しました"
ポッドキャストとして配信
ポッドキャストの配信は、「Spotify for Creators」を使うとめちゃくちゃ簡単で、一瞬でできました。
音声ファイルの編集
生成したMP3ファイルを「録って出し」してもいいのですが、ちょっとしたノイズを除去したり、効果音を入れてみたくなったので、「Audacity」というツールで、ほんのちょこっとだけ編集しています。
有料のツールを使えば、ここら辺も自動化できたりするのかもしれませんが(よく知らないですが)、ほんのひと手間かける程度なので、今のところは真心込めながら手作業でやっています。
音声ファイルのアップロード
Spotify for Creators 管理画面の「新しいエピソード」にて、編集した MP3 ファイルをアップロードし、タイトルと説明文を入力すれば、投稿完了です🚀
工夫が必要な点
日本語読みが不自然
TTS の漢字の読みやイントネーションが変な時がある。例えば、「君子(くんし)」を「きみこ」と読んだりする😅(誰?)
現時点の対応
- 部分的に「漢字 → ひらがな」に変換したり、改行や読点を入れたりして、台本を微調整することで対応。
- ラジオパーソナリティは「AIのロボシ」という設定にして、最初からロボットが読んでいることを明らかにする(=期待を下げる)。
今使ってる音声モデルが SSML 対応したら、結局、SSML で書いた方が早いのかも?ただ、あとちょっと技術が進歩すれば、平文を渡すだけでよしなにしてくれるようになる気がします。
音声の質
TTS で生成される音声がなんとなく機械音っぽかったり、文章間の「間」がなさすぎたりする。
現時点の対応
- 手作業で簡単な編集することで対応。先述のとおり、Audacity でノイズ除去するなど。
台本が短くなってしまう
3, 4分のボリュームになるように文字数を指定しているが、生成される音声ファイルが3分を超えたことがない(なぜかいつも無視される)。
現時点の対応
- 未対応。TODO: System Prompt と User Prompt の両方をチューニングする。
所感
今回実際に TTS を活用してみて、日本語読みの精度はまだまだ改善の余地があるなと思いました。ただ、最近の技術革新のスピードはすこいので、あっと言う間に、もはやロボットの音声とは思えないレベルまで改善されるんだろうなと思います。あと、効果音の追加などもサクッと自動化できるようになったら嬉しいですね。
論語は約500章もあるので、平日に週5配信したとしても1周するのに2年弱はかかります。今回書いたスクリプトを使って、こつこつアップロードしながら勉強していきたいと思います。
もし、論語に興味があればぜひ聴いてみてください。
Discussion