授業や研究発表の準備でPowerPointを使っていると、「発表者ノートを音声で聞きながら練習したい」「通勤中に耳で復習したい」という気分になってきます。自分も発表時間を気にするときに、どれくらいのスピードで話すと時間におさまるのか?こういうチェックを実際に声を出して、何度も練習をしていると声が枯れてくるといった状況にもなったりします😭あと、発表気分にするためにラジオっぽく聞いていたりもします。
この部分が発表者ノートになります

実はPowerPointの標準機能では、ノートを簡単に音声化することはできません。資料配布用の印刷物として PDF ファイルにすることはできるのですが、テキスト部分のみの出力ができません。そのため、別途読み上げツールなどと連携しようとするとコピー&ペーストをすることになってしまいます。そうすると修正も面倒なんですよね...🤔
印刷物であればノートも取り出せるのですが…

そこで今回、PowerPoint のノート抽出 → テキスト整形 → 音声合成(MP3) を自動化する Python プログラムを作ってみました。パイプライン処理で効率化しつつ、進捗表示をつけています。
処理の流れ
今回のプログラムは大きく3つに分ける事ができます。
- PowerPointのpptxファイルから発表者ノートを抽出しテキストファイルに変換
- Markdownの制御文字を削除するフィルタ
- テキストファイルをgTTSを使用してmp3ファイル化する
以下では大まかにどんな処理をしているかを解説していきます。
- 処理の流れ
- 1. PowerPointのpptxファイルから発表者ノートを抽出しテキストファイルに変換
- 2. Markdownの制御文字を削除するフィルタ
- 3. テキストファイルをgTTSを使用してmp3ファイル化する
- ファイルの使い方(GitHubからClone後の作業)
- おわりに
1. PowerPointのpptxファイルから発表者ノートを抽出しテキストファイルに変換
最初のステップは、PowerPointファイル(.pptx)から発表者ノートを抽出することです。python-pptx というライブラリが便利で、これを使えば簡単にノートのテキストを取り出せます。
notes_export.py(抜粋)
def get_slide_notes(slide: Slide) -> str: """スライドから発表者ノートを抽出する""" notes_texts = [] try: notes_slide = slide.notes_slide if notes_slide and hasattr(notes_slide, "notes_text_frame"): text = notes_slide.notes_text_frame.text or "" if text.strip(): notes_texts.append(text.strip()) except Exception: pass return "\n\n".join(notes_texts) # メイン処理部分 presentation = Presentation(str(args.pptx_path)) for idx, slide in enumerate(presentation.slides, start=1): title = get_slide_title(slide) notes = get_slide_notes(slide) # Markdown, CSV, JSON で出力
作ってみて便利だったのが、複数の出力形式に対応させたこと。Markdown だけでなく CSV や JSON でも出力できるようにしたので、後で別のツールと連携したくなっても対応できます。
実行方法は以下のようになります。
$ uv run python notes_export.py slides.pptx -o notes.md \ --format md --md-separator="---" --md-pagebreak
--md-separator オプションでスライド間の区切りを指定できたり、--md-pagebreak で印刷用の改ページを入れられるようにもしました。地味に便利です。
2. Markdownの制御文字を削除するフィルタ
音声合成する前に、Markdownファイルで使用される記号を取り除く必要があります。見出しの # とか、リンクの [text](url) とか、そのまま読み上げられると困りますよね。clean_markdown.py では正規表現でごりごり削除します。
clean_markdown.py(抜粋)
def clean(self, text: str) -> str: """Markdown記法をクリーニングする""" # コードブロック ``` ... ``` を削除 text = re.sub(r"```.*?```", "", text, flags=re.DOTALL) # インラインコード `code` を中身だけに text = re.sub(r"`([^`]*)`", r"\1", text) # 画像とリンクのURL部分を除去 text = re.sub(r"!\[([^\]]*)\]\([^)]+\)", r"\1", text) text = re.sub(r"\[([^\]]+)\]\([^)]+\)", r"\1", text) # 見出し記号 # を削除 text = re.sub(r"^#{1,6}\s*", "", text, flags=re.MULTILINE) return text.strip()
実行方法は以下の通りです。
$ cat notes.md | uv run python clean_markdown.py - -o -
3. テキストファイルをgTTSを使用してmp3ファイル化する
最後に、テキストファイルから音声ファイル(mp3)に変換します。Google Text-to-Speech(gTTS)を使うと、無料で質の良い音声が作れます。変換できる文字数に制限があるので、長文を分割する処理を行っています。
tts_gtts_make_mp3.py(抜粋)
def chunk_text(self, text: str) -> List[str]: """長文を適切なサイズに分割する(句点・改行で自然に区切る)""" chunks = [] i = 0 n = len(text) while i < n: end = min(i + self.chunk_size, n) # 文末でない場合、自然な区切り位置を探す if end != n: slice_text = text[i:end] # 優先順位: 句点 > 改行 > 読点 > スペース break_points = [ slice_text.rfind("。"), slice_text.rfind("\n"), slice_text.rfind("、"), slice_text.rfind(" "), ] last_break = max(break_points) if last_break > self.chunk_size * 0.3: # 30%以上の位置なら採用 end = i + last_break + 1 chunks.append(text[i:end].strip()) i = end return chunks
長文のチャンク分割する箇所は句点や改行にしています。また、結構処理時間がかかるので進捗表示も行っています。どれくらい進んでいるか見えると安心です。
音声合成中 (gTTS/ja): 100%|████████| 12/12 [00:48<00:00, 4.03s/chunk] 音声結合中 (pydub): 100%|████████| 12/12 [00:02<00:00, 5.47file/s] 完了: MP3ファイルを生成しました 出力: slide_notes.mp3 サイズ: 15.2 MB
ファイルの使い方(GitHubからClone後の作業)
以下のリポジトリにいれてあります。無保証ですが、ご自由にお使いください。
以下は補足情報です。
環境構築
最近は uv という高速なパッケージマネージャーを使っています。pip より断然速いのでおすすめです。
# 1. リポジトリをクローン $ git clone https://github.com/ueponx/powerpoint-notes-to-speech.git $ cd powerpoint-notes-to-speech # 2. uvのインストール(未インストールの場合) $ curl -LsSf https://astral.sh/uv/install.sh | sh # 3. 依存関係をインストール(pyproject.toml使用) $ uv sync # 4. ffmpegのインストール(音声処理に必要) # WSL/Ubuntu $ sudo apt update && sudo apt install ffmpeg # macOS(多分🙇) $ brew install ffmpeg
簡単な実行方法
一番簡単なのはリポジトリにあるシェルスクリプトpptx2mp3.shを使う方法です。
# 実行権限を付与(初回のみ) $ chmod +x pptx2mp3.sh # 実行の様子 $ ./pptx2mp3.sh slide.pptx slide.mp3 INFO: Checking dependencies... ======================================== PowerPoint Notes to Speech Converter ======================================== Input: slide.pptx Output: slide.mp3 Language: ja Speed: 1.2x Gain: +3.0dB ======================================== INFO: Step 1/3: Extracting notes from PowerPoint... 完了: 21 スライドのノートを抽出しました -> slide_notes.md INFO: Step 2/3: Cleaning markdown... INFO: Step 3/3: Converting to speech... 音声合成を開始します... 言語: 日本語 速度: 1.2x 音量: +3.0dB チャンクサイズ: 1800文字 ---------------------------------------- 音声合成中 (gTTS/ja): 100%|████████████████████████████████████████████████████| 2/2 [00:54<00:00, 27.20s/chunk] 音声結合中 (pydub): 100%|███████████████████████████████████████████████████████| 2/2 [00:02<00:00, 1.28s/file] ---------------------------------------- 完了: MP3ファイルを生成しました 出力: slide.mp3 サイズ: 11.1 MB INFO: Temporary files removed ======================================== SUCCESS: Conversion completed successfully! Output saved to: slide.mp3 File size: 12M ========================================
もちろん、各ステップを個別に実行することもできます。
# ステップ1: pptxファイルからノート抽出 $ uv run python notes_export.py presentation.pptx -o notes.md --format md # ステップ2: Markdownファイルのクリーニング $ uv run python clean_markdown.py notes.md -o notes_clean.txt # ステップ3: 音声化 $ uv run python tts_gtts_make_mp3.py notes_clean.txt -o audio.mp3 --no-clean
パイプでつなげて一気に処理することも可能です。
$ uv run python notes_export.py presentation.pptx -o - --format md \ | uv run python clean_markdown.py - -o - \ | uv run python tts_gtts_make_mp3.py -o audio.mp3 --no-clean
カスタマイズ
オプションスイッチを指定することで、音声の速度や音量を調整できるようにしています。 例えば、発表練習用なら速度を上げて、教材用ならゆっくりめに、といった使い分けもできます。
$ uv run python tts_gtts_make_mp3.py input.txt -o output.mp3 \ --chunk 1500 # チャンクサイズ(文字数) --speed 1.3 # 再生速度(1.0が標準) --gain 5.0 # 音量増幅(dB) --silence-ms 500 # チャンク間の無音(ミリ秒) --lang en # 言語指定(日本語はja)
おわりに
なんとなく作ってみましたが、目的がしっかりとしているため割と便利につかえています。
- 発表練習が楽になった … 電車の中でも練習できるし、何より喉を休めながら通し練習ができる
- 時間配分の確認 … 音声の長さで発表時間の目安がわかる
最近の自動音声の品質はどんどん上がっているので複数話者の対応とか、ページの切り替えでBGM を入れるといった機能も面白そうですね。