音声ファイル(MP3)をもとに以下の処理をすべてローカル環境で自動化するスクリプトを構築した記録です。
Plaud.aiをゲットしたものの無料書き起こしが毎月300分しかなく、上位プランは年間1万2千円、あるいは4万円なので、
書き起こしツールを作成してみました。
ゴール
- MP3音声のWAV変換
- 話者分離(スピーカー・ダイアリゼーション)
- Whisperによる文字起こし(日本語)
- 発言を話者ごとに結合
- Plaud.ai風の議事録テキスト出力
- Markmap形式のマインドマップMarkdown出力
使用ライブラリ
whisperpyannote.audiopydubhuggingface_hub
事前準備
- Python 3.9〜3.10 の仮想環境を推奨
- 必要ライブラリのインストール:
pip install openai-whisper pydub pyannote.audio - ffmpegのインストール(音声変換用)
- Hugging Faceでアカウント作成 + アクセストークン発行(read権限)
- モデルページでアクセス許可をリクエスト:
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ファイルを生成しました")