0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OpenAIのTTS APIでPodcastのAIナレーションを作ってみた【Python】

0
Posted at

はじめに

個人で「構造観測所」というPodcastを制作しています。AIやデザイン、組織、日常の出来事などを題材に、その背後にある構造を考える番組です。

もともとは自分で収録した音声を編集して公開していたのですが、制作を続けるうちに、こんな疑問が生まれました。

「解説部分は、AIに喋ってもらっても成立するのでは?」

そこで、第9回ではOpenAIのTTS(Text-to-Speech)APIを使い、解説部分をAIナレーションに置き換えました。第10回では、制作を振り返る途中にAIのコメントを1か所だけ挿入する形式も試しています。

音声ファイルを生成するコードはシンプルでした。一方、実際の制作では、固有名詞や英語の誤読、原稿の末尾の読み飛ばしがあり、生成後の試聴と修正が欠かせませんでした。

この記事では、PythonでWAVを生成する方法と、番組として公開するまでに必要だった作業を紹介します。

1. 制作環境

項目 使用環境・設定
OS macOS
Python 3.13
OpenAI Python SDK(openai) 3.24.0
API OpenAI Audio API(Speech)
モデル gpt-4o-mini-tts
音声 marin
出力形式 WAV
音声編集 DaVinci Resolve
配信先 Spotify

声の候補を比較する際にはOpenAI.fmも利用し、声や話し方を試してからPythonで生成しました。

本記事は、2026年10月に「構造観測所」#9・#10を制作した際の記録をもとにしています。SDKのバージョンは制作環境で確認した値です。

掲載コードは、制作時の会話に残っているコード例を記事用に整理したものです。実行時のスクリプトファイルそのものとの完全一致や、掲載版を使ったAPIの再実行までは確認していません。複数ブロックの原稿は説明用の短い例に置き換えています。

2026年10月17日の原稿整理時点で、公式ガイドのPythonによるストリーミング保存例を確認しました。これは制作環境での実行検証とは別です。APIやSDKの仕様は変わる可能性があるため、実行時にはOpenAI公式のTTSガイドも確認してください。

2. OpenAIのTTS APIで単一のWAVを生成する

準備:ライブラリとAPIキー

Python 3.13が使える環境で、制作時と同じSDKのバージョンを指定する例です。

python3.13 -m pip install "openai==3.24.0"
python3.13 -m pip show openai

続いて、実行するターミナルでAPIキーを環境変数に設定します。YOUR_API_KEYは自分のキーに置き換えます。

export OPENAI_API_KEY='YOUR_API_KEY'

APIキーはソースコードに直接書かず、公開する原稿やリポジトリにも含めません。以下のOpenAI()は環境変数OPENAI_API_KEYからキーを読み込みます。音声生成の実行にはAPI利用料がかかります。

第10回のAIコメントを生成するコード例

第10回では、番組の制作記録に対するAIコメントを1か所だけ生成しました。次は、元のドラフトに掲載したコメント例と読み上げ指示です。generate_comment.pyとして保存します。

from pathlib import Path
from openai import OpenAI

client = OpenAI()
OUTPUT = Path("ai_comment.wav")

SCRIPT = """
構造観測所の、これまでの制作記録を振り返ってみます。

興味深いのは、10回の制作を通じて、
改善されてきたものが主に「番組の作り方」だったことです。

一方で、まだ十分に検証されていない問いがあります。

それは、誰が、どんな場面で、
何のためにこの番組を聴きたいと思うのか、
ということです。

番組を作れることと、
誰かに聴きたいと思ってもらえることは、
別の仮説です。
""".strip()

INSTRUCTIONS = """
Speak in natural Japanese as a calm podcast commentator.

- Use a warm, thoughtful, conversational tone.
- Speak at a moderate pace.
- Pause naturally between ideas.
- Avoid sounding overly dramatic or robotic.
- Read the supplied script faithfully.
- Do not skip the final sentence.
""".strip()

with client.audio.speech.with_streaming_response.create(
    model="gpt-4o-mini-tts",
    voice="marin",
    input=SCRIPT,
    instructions=INSTRUCTIONS,
    response_format="wav",
) as response:
    response.stream_to_file(OUTPUT)

print(f"Generated: {OUTPUT.resolve()}")

保存したフォルダで実行します。

python3.13 generate_comment.py

処理が正常に完了すると、実行したフォルダにai_comment.wavが保存されます。既存の同名ファイルは上書きされるため、比較したい音声は先に別名で残しておきます。

inputが読み上げる原稿、instructionsが話し方の指示です。コメントの文章を考える工程と、その文章をTTSで音声にする工程は分かれています。このコード自体が制作記録を分析してコメントを作るわけではありません。

また、Do not skip the final sentence.は末尾まで読むよう求める指示であり、省略を完全に防ぐ保証ではありません。生成後は最後の一文まで聴いて確認します。

3. 第9回では原稿を7ブロックに分けた

第9回のテーマはGoogle Career Certificatesでした。比較的長い解説原稿を用意し、7つのブロックに分けて音声を生成しました。生成した7つのWAVは、DaVinci Resolve上でつなぎました。

分割してよかった点は、読み間違いがあった部分だけを生成し直せることです。2番目のブロックに問題があれば、そのブロックだけを修正・再生成できます。

複数ブロックを生成するコード例

以下は、制作時の「原稿とファイル名を一覧にし、ループで生成する」処理を、単独で使える形に整理した例です。説明のため3ブロックだけ載せています。例文は第9回の実際の台本ではありません。 自分の原稿に置き換え、必要な数の要素を追加してください。

generate_segments.pyとして保存します。

from pathlib import Path
from openai import OpenAI

client = OpenAI()
OUTPUT_DIR = Path("tts_output")
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

INSTRUCTIONS = """
Speak in natural Japanese as a calm podcast commentator.
Use a warm, thoughtful, conversational tone.
Speak at a moderate pace and pause naturally between ideas.
Read the supplied script faithfully. Do not skip the final sentence.
""".strip()

# 説明用の例文。制作する番組の原稿に置き換える。
SEGMENTS = [
    {
        "filename": "01_origin.wav",
        "text": "最初に、このテーマを取り上げたきっかけを話します。",
    },
    {
        "filename": "02_search.wav",
        "text": "次に、調べる中で見えてきたことを整理します。",
    },
    {
        "filename": "03_pathway.wav",
        "text": "最後に、学ぶことと仕事につながることの関係を考えます。",
    },
]

for segment in SEGMENTS:
    output = OUTPUT_DIR / segment["filename"]

    with client.audio.speech.with_streaming_response.create(
        model="gpt-4o-mini-tts",
        voice="marin",
        input=segment["text"],
        instructions=INSTRUCTIONS,
        response_format="wav",
    ) as response:
        response.stream_to_file(output)

    print(f"Generated: {output.resolve()}")
python3.13 generate_segments.py

この例ではtts_outputフォルダに3つのWAVが保存されます。部分再生成するときは、SEGMENTSに対象のブロックだけを残して実行します。元の音声と比較する場合は、出力名を02_search_v2.wavなどに変えます。全ブロックを残したまま再実行すると、全件に対してAPIを呼び出します。

ブロック数の「7」は今回の原稿構成に合わせたもので、APIが要求する分割数ではありません。また、分割すると接続箇所が増えるため、前後で話し方や間の取り方が不自然になっていないかも確認します。

4. 実際に発生した問題

固有名詞「Yutao.」のドットまで読む

番組内では、私の名前を「Yutao.」と表記しています。ところが、生成された音声では「ユタオ」と読まれることもあれば、「ユタオドット」のように末尾の記号まで読まれることもありました。

表示用の名前としては自然でも、読み上げでは意図とずれる場合があります。

対策の候補は、読み上げ用の原稿だけ「ユタオ」とカタカナにすることです。これは、今回の経験から考えた改善案です。表示用の原稿と読み上げ用の原稿を分ければ、表記を維持しながら発音を調整しやすくなります。

英語の用語を誤読する

第9回では、「Design Thinking」が意図しない別の表現として読み上げられることがありました。該当ブロックを再生成したところ、2回目は意図した内容になりました。

この経験から、同じ原稿を渡しても、生成結果が同じになるとは限らないことが分かりました。ただし、再生成すれば必ず直るとまでは言えません。生成し直した音声も、最初から最後まで確認する必要があります。

原稿の末尾が読み飛ばされる

原稿の最後にある短い文章が読み上げられず、その直前で音声が終わることもありました。

「最後まで省略せずに読む」という指示を追加する方法も試しましたが、指示だけで完全に防げるとは考えていません。今回の記録だけでは、発生原因も特定できていません。

自然な締め方に聞こえても、原稿どおりとは限りません。耳で聴くだけでなく、原稿を見ながら照合することが大切でした。

制作フロー

Podcast制作ワークフロー図.png

原稿作成 → TTS生成 → 試聴・原稿照合 → 問題があれば原稿・指示の調整と部分再生成 → 再び試聴・照合 → 編集 → 公開、という流れです。部分再生成から、そのまま編集へ進まず、確認へ戻ることがポイントです。

5. AIに任せても、人間の確認は必要だった

今回、Pythonコードの作成にもAIを利用しました。私は手順に従って実行し、出力された音声を確認しました。

音声ファイルを作れることと、公開できる音声になっていることは別です。確認では、次の点を見ました。

  • 固有名詞や英語が意図したとおりに読まれているか。
  • 原稿の文章が抜けていないか。特に最後の一文まで読まれているか。
  • 話すテンポや間が、番組として聴きやすいか。
  • 前後のブロックや自分の声と自然につながるか。

問題のあるブロックを再生成し、原稿と照合し直します。使えると判断した音声をDaVinci Resolveへ読み込み、番組全体の流れを確認してから公開しました。

制作担当を整理すると、次のようになります。

工程 主な担当 担当したこと
テーマの決定 人間 何を取り上げ、何を問いにするか決める
原稿の構成・作成 人間+AI 話の流れや文章を検討する
Pythonコードの作成 AI 音声生成処理をコードにする
コードの実行 人間 制作環境で実行する
音声の生成 OpenAI TTS API 渡した原稿を音声化する
試聴・原稿照合 人間 誤読、省略、テンポを確認する
必要部分の再生成 人間+AI/API 人間が対象を判断し、必要に応じて調整して生成する
編集・公開 人間 音声を組み合わせ、完成版を判断する

AIによって作業の一部は省力化できました。ただ、どの音声を採用するか、番組として成立しているかの判断は、人間の仕事として残りました。

6. 結果と限界:Podcastとして成立するのか?

第9回:解説部分をAIに任せる

第9回では、冒頭と最後を自分で話し、解説部分にAIナレーションを使いました。完成した音声を聴き直したところ、少なくとも自分にとっては、話題が明確でテンポよく聴ける番組になったと感じました。

ただし、これは制作者自身の感想です。リスナーがどう評価するかまでは、今回の制作だけでは分かりません。

第10回:AIをコメンテーターとして入れる

第10回では、長い解説を任せるのではなく、制作を振り返る途中にAIコメントを1か所だけ挿入しました。コメントの焦点は、「番組を作る方法は改善してきたが、誰が何のために聴きたいのかは、まだ別の問いとして残っている」という点です。

第10回は、ほぼ編集なしで公開できました。ただし、その結果をTTSだけの効果と切り分けて測定したわけではありません。

人間の語りの中に別の視点を差し込む役割として、AIの声を使う。この形式も、番組の作り方の選択肢になりました。

今回確認できたこと、まだ分からないこと

観点 今回の結果・限界
制作・公開 AIナレーションを組み込んだ番組を制作し、公開できた
修正のしやすさ 分割生成により、問題のある箇所だけを再生成できた
読みの正確さ 誤読や省略があり、人間による照合が必要だった
再生成の効果 改善した事例はあったが、毎回の改善を保証するものではない
リスナーの評価 制作者の感想と区別して、今後の検証が必要
時間・費用の効果 比較計測していないため、削減率や費用対効果は示せない

7. 実際に制作したPodcast

構造観測所 #9

Googleはなぜ、Career Certificatesまで作るのか?

Spotifyで第9回を聴く

解説部分にOpenAIのTTS APIを使用しました。原稿を7ブロックに分けて生成した回です。

構造観測所 #10

10回やってみた。―「もったいない」から始まったPodcast制作実験

Spotifyで第10回を聴く

制作の振り返りに、AIからのコメントを挿入しています。

関連する記事:Google Career Certificatesについてのnote記事

おわりに

OpenAIのTTS APIを使うことで、PythonからPodcast用の音声を生成し、自分の声と組み合わせて番組を制作できました。

実際に使ってみて印象に残ったのは、生成後の工程です。固有名詞の読み方、英語の誤読、原稿の末尾の省略は、コードが正常終了しただけでは分かりません。試聴し、原稿と照合し、必要な箇所を作り直す。そのループまで含めて、今回の制作方法になりました。

今後も、AIを解説者やコメンテーターとしてどのように参加させると番組が面白くなるのか、試していきたいと思います。

参考資料

音声を公開するときは、AI生成音声であることをリスナーに明示します。これは公式ガイドでも案内されている点です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?