MENU

MP3から自動で会議録を作成するアプリのメモ

音声ファイル(MP3)をもとに以下の処理をすべてローカル環境で自動化するスクリプトを構築した記録です。

Plaud.aiをゲットしたものの無料書き起こしが毎月300分しかなく、上位プランは年間1万2千円、あるいは4万円なので、

書き起こしツールを作成してみました。

ゴール

  • MP3音声のWAV変換
  • 話者分離(スピーカー・ダイアリゼーション)
  • Whisperによる文字起こし(日本語)
  • 発言を話者ごとに結合
  • Plaud.ai風の議事録テキスト出力
  • Markmap形式のマインドマップMarkdown出力

使用ライブラリ

  • whisper
  • pyannote.audio
  • pydub
  • huggingface_hub

事前準備

  1. Python 3.9〜3.10 の仮想環境を推奨
  2. 必要ライブラリのインストール: pip install openai-whisper pydub pyannote.audio
  3. ffmpegのインストール(音声変換用)
  4. Hugging Faceでアカウント作成 + アクセストークン発行(read権限)
  5. モデルページでアクセス許可をリクエスト:

memo

  • Whisper単体では話者分離できないが、pyannote.audioと連携することで「誰が何を言ったか」が取れる
  • Markmapと組み合わせることで、視覚的なマップがすぐに生成できて便利
  • Hugging Faceのgatedモデルは要認証+許可が必要
  • Whisperのセグメントは厳密な時間一致ではなく、重なりを許容してマッチングするのが重要

今後の改善

  • 発話ごとの要約(ChatGPT APIと連携)
  • 発話内容のトピック分類やキーワード抽出
  • HTML/PDFでのフォーマット整形

ちなみに生成したマインドマップはhttps://markmap.js.org/repl/で即表示できます。

会議録、講義録、対談書き起こしなど、幅広く応用可能(のはず)

現状のコード

議事録と要約がうまく作れない…

from pydub import AudioSegment
from pyannote.audio import Pipeline
import whisper
import os

MP3_FILE = "sampleSuper.mp3"
WAV_FILE = MP3_FILE.replace(".mp3", ".wav")
HUGGINGFACE_TOKEN = "---------------"

# 1. MP3 → WAV 変換
if not os.path.exists(WAV_FILE):
    print("MP3 を WAV に変換中...")
    audio = AudioSegment.from_mp3(MP3_FILE)
    audio.export(WAV_FILE, format="wav")
    print(f"変換完了: {WAV_FILE}")

# 2. 話者分離
print("🎙️ 話者分離を実行中...")
pipeline = Pipeline.from_pretrained("pyannote/speaker-diarization", use_auth_token=HUGGINGFACE_TOKEN)
diarization = pipeline(WAV_FILE)

# 3. Whisper文字起こし
print("Whisperで文字起こし中...")
whisper_model = whisper.load_model("base")
whisper_result = whisper_model.transcribe(WAV_FILE, language="ja", verbose=False)

# 4. 発言と話者のマージ
print("🔗 発言を話者と統合中...")
merged_segments = []
for turn, _, speaker in diarization.itertracks(yield_label=True):
    turn_start = turn.start
    turn_end = turn.end
    matched_text = ""
    for segment in whisper_result["segments"]:
        if segment["start"] < turn_end and segment["end"] > turn_start:
            matched_text += segment["text"].strip() + " "
    merged_segments.append({
        "speaker": speaker,
        "start": turn_start,
        "end": turn_end,
        "text": matched_text.strip()
    })

# 5. 議事録出力
print("議事録:")
with open("plaud_transcript.txt", "w", encoding="utf-8") as f:
    for seg in merged_segments:
        s = f"{int(seg['start'] // 60):02}:{int(seg['start']) % 60:02}"
        f.write(f"{s}  {seg['speaker']}\n{seg['text']}\n\n")
        print(f"{s}  {seg['speaker']}\n{seg['text']}\n")

# 6. Mindmap出力
with open("mindmap.md", "w", encoding="utf-8") as f:
    f.write("# 会議内容\n")
    current_speaker = ""
    for seg in merged_segments:
        if seg["speaker"] != current_speaker:
            f.write(f"## {seg['speaker']}\n")
            current_speaker = seg["speaker"]
        f.write(f"- {seg['text']}\n")

print("議事録とマインドマップMarkdownファイルを生成しました")
目次