Pythonを使った自動でポッドキャスト、Youtube動画の作成プログラムのサンプルコードと生成プロンプト

Pythonを使った自動でポッドキャスト、Youtube動画の作成プログラムのサンプルコードと生成プロンプト Thumbnail

Youtubeでお届けしておりました、音声収録後自動で編集してくれるプログラムのプロンプト例です。

しっかり応用ができるように、プロンプトをそのままコピペではなく、解説しながら進んでいきます。

プログラム構成

まずは重要なこと、それが、スタートとストップを入れるということです。
ポッドキャストを台本で読む方、筆者のようにフリースタイルで喋り続ける方いろいろいるかと思いますが、どうしても咳払いや、えーと、あのー、などの言葉が出そうな時などにストップをかけるというもの。

ストップ中の音声は破棄するという設定が重要です。

おおまかなフレームは次の通り。

1、プログラムを実行したらエンターボタンで実行できるように待機
2、エンターで録音開始
3、スペースで一時停止、スペースで録音再開
4、再びエンターで録音終了。

この流れで収録します。

重要な指示

重要な指示として収録後に、+5秒余白をいれること。
これを入れておかないと、どういうわけか、収録の音声が途切れる現象が発生しました。

また、BGMのボリュームです。
−16dbくらいにしていますが、これは使用する音楽や状況に応じて変化させていく必要があります。

また、Youtube用のmp4ファイルに関しては、

場所の指定

BGMにする音源素材のパス、Youtube用の動画のパス、出力のパスをそれぞれしっかりと定義する必要があります。

また、また、プログラムでは、mp3, mp4と二種類出力してもらいます。

忘れてはならないこと

忘れてはいけないこととして、収録した音声データのノーマライズです。

サンプルコード

実際は必要なライブラリをインストールしてパスだけ変えれば同じように動くと思います。
サンプリングレートなどはそれぞれ変更して使ってみてください。

もし動かない場合やエラーの場合、は、生成AIのガイドでご自身の環境に合わせて変化させてください。

また、最後に、最初から生成AIで作りたい方のためのプロンプト例をシェアしておきます。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
自動ポッドキャスト生成ツール v2.1 (Python 3.13対応版)

概要:
  - Enter キーで録音開始、Space キーで一時停止/再開、再度 Enter キーで録音終了
  - 録音中は、端末上で状態が点滅表示され、録音中か一時停止中かを視覚的に表示する
  - 録音後、録音時間を表示し、Enter キーで編集開始を待機
  - 編集処理:
      1. 録音音声(ステレオ)は個別に正規化(ヘッドルーム 0.1 dBFS)
      2. 指定BGMを個別に正規化し、–16 dB のゲインを適用して音量を大幅に下げる
      3. BGMの長さは、録音音声より5秒長くし、末尾5秒にフェードアウト処理を適用
      4. 録音音声とBGMをミックス
  - 出力ファイル(2種類):
      1. MP3版: 320kbps/48kHz のMP3として出力(音声のみ)
      2. MP4版: 素材動画を録音時間に合わせてカットし、音声と結合してYouTube用動画を生成
  - 出力ファイルは YYYYMMDD_HHMMSS の形式で あなたのパス に保存

依存ライブラリ:
  - pyaudio
  - pynput
  - numpy
  - soundfile
  - scipy
  - subprocess (FFmpeg制御用)
"""

import pyaudio
import threading
import time
import os
import sys
import subprocess
import numpy as np
import soundfile as sf
from datetime import datetime
from pynput import keyboard
from scipy import signal

# FFmpeg のパス設定
FFMPEG_PATH = '/opt/homebrew/bin/ffmpeg'

# 定数の設定
CHUNK = 1024                 # 一度に読み込むフレーム数
FORMAT = pyaudio.paInt16     # 16bitフォーマット
CHANNELS = 2                 # 録音はステレオ
RATE = 48000                 # サンプルレート (48kHz)
BGM_FILE = '/あなたの場所'
VIDEO_SOURCE = 'あなたの場所'
OUTPUT_DIR = 'あなたの場所'
MP3_BITRATE = "320k"

# グローバル変数(録音状態管理)
recording = False
paused = False
finish_recording = False

# イベントオブジェクト(非同期制御用)
start_event = threading.Event()
stop_event = threading.Event()

def on_press(key):
    """
    キーボード入力監視関数:
    - Enter キーで録音の開始/終了
    - Space キーで一時停止/再開を制御
    """
    global recording, paused, finish_recording
    try:
        if key == keyboard.Key.enter:
            if not recording:
                recording = True
                print("\nRecording started...")
                start_event.set()
            else:
                finish_recording = True
                stop_event.set()
                print("\nRecording stopping...")
        elif key == keyboard.Key.space:
            if recording:
                paused = not paused
                if paused:
                    print("\nRecording paused. Press Space to resume.")
                else:
                    print("\nRecording resumed.")
    except Exception as e:
        print("Error in key press:", e)

def record_audio():
    """
    PyAudio を用いてステレオ録音を行い、録音データと実際の録音時間(秒)を返す。
    一時停止中はデータを読み捨て、pause状態の時間はカウントされない。
    """
    global paused, finish_recording
    audio = pyaudio.PyAudio()
    stream = audio.open(format=FORMAT,
                        channels=CHANNELS,
                        rate=RATE,
                        input=True,
                        frames_per_buffer=CHUNK)
    frames = []
    total_frames = 0
    pause_message_shown = False

    while not finish_recording:
        if paused:
            if not pause_message_shown:
                print("\rREC PAUSED         ", end="", flush=True)
                pause_message_shown = True
            stream.read(CHUNK)
            continue
        else:
            pause_message_shown = False
        try:
            data = stream.read(CHUNK)
        except Exception as e:
            print("Error during recording:", e)
            break
        frames.append(data)
        total_frames += CHUNK

    stream.stop_stream()
    stream.close()
    audio.terminate()
    recorded_time = total_frames / RATE
    print(f"\nActual recorded time: {recorded_time:.2f} seconds")
    return b"".join(frames), recorded_time

def blinking_status():
    """
    録音中に状態を視覚的に表示する関数。
    """
    while not finish_recording:
        if recording:
            if not paused:
                print("\rREC: [ON]   ", end="", flush=True)
                time.sleep(0.5)
                print("\rREC: [   ]  ", end="", flush=True)
                time.sleep(0.5)
            else:
                print("\rREC PAUSED  ", end="", flush=True)
                time.sleep(0.5)
        else:
            time.sleep(0.2)
    print("\r               ", end="", flush=True)

def normalize_audio(audio_data, headroom_db=0.1):
    """
    音声データを正規化する
    headroom_db: デジタルクリッピング防止のためのヘッドルーム(dBFS)
    """
    # ヘッドルームを線形スケールに変換
    headroom_linear = 10 ** (-headroom_db / 20)
    
    # 最大振幅を取得
    max_amplitude = np.max(np.abs(audio_data))
    
    if max_amplitude > 0:
        # 正規化係数を計算
        normalization_factor = headroom_linear / max_amplitude
        normalized = audio_data * normalization_factor
    else:
        normalized = audio_data
    
    return normalized

def apply_gain_db(audio_data, gain_db):
    """
    音声データにdB単位でゲインを適用
    """
    gain_linear = 10 ** (gain_db / 20)
    return audio_data * gain_linear

def apply_fade_out(audio_data, sample_rate, fade_duration_sec=5.0):
    """
    音声データの末尾にフェードアウトを適用
    """
    fade_samples = int(sample_rate * fade_duration_sec)
    
    if fade_samples >= len(audio_data):
        fade_samples = len(audio_data)
    
    # フェードアウトカーブを作成
    fade_curve = np.linspace(1.0, 0.0, fade_samples)
    
    # ステレオの場合、各チャンネルに適用
    if len(audio_data.shape) == 2:
        fade_curve = fade_curve.reshape(-1, 1)
    
    # 末尾にフェードアウトを適用
    audio_data[-fade_samples:] = audio_data[-fade_samples:] * fade_curve
    
    return audio_data

def edit_and_export(recorded_audio, recorded_time):
    """
    録音後の編集処理を行い、以下の2ファイルを出力する:
      1. MP3ファイル(音声のみ)
      2. MP4ファイル(YouTube用動画)
    """
    # BGMファイルが存在するかチェック
    if not os.path.isfile(BGM_FILE):
        print(f"Error: BGM file not found: {BGM_FILE}")
        sys.exit(1)
    
    # 素材動画ファイルが存在するかチェック
    if not os.path.isfile(VIDEO_SOURCE):
        print(f"Error: Video source file not found: {VIDEO_SOURCE}")
        sys.exit(1)

    print("\n=== Editing audio ===")
    
    # 録音音声をnumpy配列に変換(16bit signed intからfloat32へ)
    audio_array = np.frombuffer(recorded_audio, dtype=np.int16)
    # ステレオなので2チャンネルに分割
    audio_array = audio_array.reshape(-1, CHANNELS).astype(np.float32) / 32768.0
    
    # 録音音声を正規化
    print("Normalizing recorded audio...")
    normalized_audio = normalize_audio(audio_array, headroom_db=0.1)
    
    # BGMを読み込み
    print("Loading BGM...")
    bgm_data, bgm_rate = sf.read(BGM_FILE, dtype='float32')
    
    # BGMのサンプルレートが異なる場合はリサンプリング
    if bgm_rate != RATE:
        print(f"Resampling BGM from {bgm_rate}Hz to {RATE}Hz...")
        num_samples = int(len(bgm_data) * RATE / bgm_rate)
        bgm_data = signal.resample(bgm_data, num_samples)
    
    # BGMがモノラルの場合はステレオに変換
    if len(bgm_data.shape) == 1:
        bgm_data = np.stack([bgm_data, bgm_data], axis=1)
    
    # BGMを正規化
    print("Normalizing BGM...")
    normalized_bgm = normalize_audio(bgm_data, headroom_db=0.1)
    
    # –16 dB のゲインを適用
    print("Applying -16dB gain to BGM...")
    adjusted_bgm = apply_gain_db(normalized_bgm, -16)
    
    # BGMの長さを調整(録音音声 + 5秒)
    target_samples = len(normalized_audio) + int(RATE * 5)
    
    if len(adjusted_bgm) >= target_samples:
        final_bgm = adjusted_bgm[:target_samples]
    else:
        # BGMをループして必要な長さにする
        times_to_repeat = (target_samples // len(adjusted_bgm)) + 1
        repeated_bgm = np.tile(adjusted_bgm, (times_to_repeat, 1))
        final_bgm = repeated_bgm[:target_samples]
    
    # BGMの末尾5秒にフェードアウトを適用
    print("Applying fade-out to BGM...")
    final_bgm = apply_fade_out(final_bgm, RATE, fade_duration_sec=5.0)
    
    # 録音音声とBGMをミックス(オーバーレイ)
    print("Mixing audio with BGM...")
    # 録音音声の長さに合わせてゼロパディング
    padded_audio = np.zeros((len(final_bgm), CHANNELS), dtype=np.float32)
    padded_audio[:len(normalized_audio)] = normalized_audio
    
    # ミックス
    mixed = final_bgm + padded_audio
    
    # クリッピング防止
    max_val = np.max(np.abs(mixed))
    if max_val > 1.0:
        mixed = mixed / max_val
    
    # int16に変換
    mixed_int16 = (mixed * 32767).astype(np.int16)
    
    # 出力ファイル名の生成
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
    temp_wav = os.path.join(OUTPUT_DIR, f"{timestamp}_temp.wav")
    output_mp3 = os.path.join(OUTPUT_DIR, f"{timestamp}.mp3")
    output_mp4 = os.path.join(OUTPUT_DIR, f"{timestamp}.mp4")
    
    # 一時WAVファイルとして保存
    print(f"Saving temporary WAV file...")
    sf.write(temp_wav, mixed_int16, RATE)
    
    # WAVからMP3に変換
    print(f"Converting to MP3: {output_mp3}")
    cmd_mp3 = [
        FFMPEG_PATH,
        '-i', temp_wav,
        '-b:a', MP3_BITRATE,
        '-ar', str(RATE),
        '-y',
        output_mp3
    ]
    subprocess.run(cmd_mp3, check=True, capture_output=True)
    print(f"✅ MP3 exported: {output_mp3}")
    
    # MP4出力(動画処理)
    print("\n=== Processing video ===")
    print(f"Cutting video to {recorded_time:.2f} seconds...")
    
    # 一時ファイル名
    temp_video_cut = os.path.join(OUTPUT_DIR, f"{timestamp}_temp_cut.mov")
    
    try:
        # Step 1: 素材動画を録音時間に合わせてカット
        cmd_cut = [
            FFMPEG_PATH,
            '-i', VIDEO_SOURCE,
            '-t', str(recorded_time),
            '-c', 'copy',
            '-y',
            temp_video_cut
        ]
        subprocess.run(cmd_cut, check=True, capture_output=True)
        print("✅ Video cut completed")
        
        # Step 2: カットした動画と音声を結合
        print("Merging audio with video...")
        cmd_merge = [
            FFMPEG_PATH,
            '-i', temp_video_cut,
            '-i', output_mp3,
            '-c:v', 'libx264',
            '-preset', 'fast',
            '-crf', '18',
            '-c:a', 'aac',
            '-b:a', '320k',
            '-ar', str(RATE),
            '-map', '0:v:0',
            '-map', '1:a:0',
            '-shortest',
            '-y',
            output_mp4
        ]
        subprocess.run(cmd_merge, check=True, capture_output=True)
        print(f"✅ MP4 exported: {output_mp4}")
        
        # 一時ファイル削除
        if os.path.exists(temp_video_cut):
            os.remove(temp_video_cut)
        if os.path.exists(temp_wav):
            os.remove(temp_wav)
        print("✅ Temporary files cleaned up")
        
        print("\n=== Export completed ===")
        print(f"Audio (MP3): {output_mp3}")
        print(f"Video (MP4): {output_mp4}")
        
    except subprocess.CalledProcessError as e:
        print(f"Error during video processing: {e}")
        print(f"FFmpeg error output: {e.stderr.decode() if e.stderr else 'N/A'}")
        # 一時ファイルのクリーンアップ
        for temp_file in [temp_video_cut, temp_wav]:
            if os.path.exists(temp_file):
                os.remove(temp_file)
        sys.exit(1)

def main():
    """
    メイン関数
    """
    print("=== Auto Podcast Generator v2.1 (Python 3.13 Compatible) ===")
    print("Press Enter to start recording...")
    listener = keyboard.Listener(on_press=on_press)
    listener.start()

    # 録音状態表示用のブリンクングスレッドを開始
    blink_thread = threading.Thread(target=blinking_status, daemon=True)
    blink_thread.start()

    start_event.wait()  # Enterキーで録音開始

    recorded_audio, active_recording_time = record_audio()
    print(f"\nRecorded time: {active_recording_time:.2f} seconds")

    input("\nPress Enter to start editing...")

    edit_and_export(recorded_audio, active_recording_time)

    # 終了処理
    listener.stop()
    global finish_recording
    finish_recording = True
    blink_thread.join()

if __name__ == '__main__':
    main()

生成AI用のプロンプト

自動ポッドキャスト生成ツール v2.1 作成プロンプト

## 要件定義

### 基本機能
- キーボード操作による録音制御
  - [Enter] 録音開始/終了
  - [Space] 一時停止/再開
  - 録音中は "REC: [ON]" を点滅表示
  - 一時停止中は "REC PAUSED" を表示

### 録音仕様
- フォーマット: ステレオ / 48kHz / 16bit
- ライブラリ: PyAudio
- 一時停止中のデータは破棄(実録音時間にカウントしない)

### 音声編集処理
1. 録音音声を正規化(ヘッドルーム 0.1 dBFS)
2. BGMファイルを読み込み、正規化後に –16 dB のゲイン適用
3. BGMの長さを「録音時間 + 5秒」に調整(ループ処理)
4. BGMの末尾5秒にフェードアウト適用
5. 録音音声とBGMをオーバーレイミックス

### 動画処理
1. 素材動画(15分)を録音時間でカット
2. カットした動画とミックス済み音声を結合
3. YouTube用高品質エンコード(H.264/AAC 320kbps)

### 出力仕様
- 出力先: あなたの場所
- ファイル名形式: 今日の日付
- 出力ファイル:
  1. 〇〇〇〇〇〇〇〇_〇〇〇〇〇〇.mp3(音声のみ版)
  2. 〇〇〇〇〇〇〇〇_〇〇〇〇〇〇.mp4(YouTube用完成動画)

## 技術スタック

### 必須ライブラリ
- pyaudio: 録音処理
- pynput: キーボード入力監視
- numpy: 音声データ数値処理
- soundfile: WAVファイル読み書き
- scipy: リサンプリング処理
- subprocess: FFmpeg制御

### 環境要件
- Python 3.13対応(audioop非依存)
- FFmpegパス: /opt/homebrew/bin/ffmpeg

### 固定パス
あなたのパスを入力
- BGMファイル:
- 素材動画: 
- 出力先: 

## 音声処理アルゴリズム

### 正規化関数
- ヘッドルーム 0.1 dBFS(10^(-0.1/20))
- max振幅で除算し、ヘッドルーム係数を乗算

### ゲイン適用
- dB → 線形変換: 10^(dB/20)

### フェードアウト
- 末尾5秒間に線形フェードカーブ(1.0 → 0.0)を適用

### ミックス処理
- BGMと録音音声を加算
- クリッピング防止(max > 1.0 なら正規化)

## FFmpeg処理

### 動画カット
ffmpeg -i 素材動画 -t 録音時間 -c copy 出力


### 音声結合
ffmpeg -i カット動画 -i MP3音声
-c:v libx264 -preset fast -crf 18
-c:a aac -b:a 320k -ar 48000
-map 0✌️0 -map 1🅰️0 -shortest 出力


## ワークフロー

1. プログラム起動 → Enter待機
2. [Enter] 録音開始 → 点滅表示
3. [Space] 任意回数で一時停止/再開
4. [Enter] 録音終了 → 録音時間表示
5. [Enter] 編集開始
6. 音声ミックス処理
7. MP3出力
8. 動画カット+音声結合
9. MP4出力
10. 一時ファイル削除
11. 完了メッセージ表示

## 制約条件

### 必須遵守事項
- pydubを使用しない(Python 3.13非対応)
- numpy/soundfile/scipyで音声処理を実装
- 一時ファイル(WAV/動画カット版)は処理後に自動削除
- エラー時も一時ファイルをクリーンアップ

### エラーハンドリング
- BGMファイル存在確認
- 素材動画ファイル存在確認
- FFmpegエラー出力表示
- subprocess例外処理

## 実行環境

### 仮想環境

また、Pythonを使ったことがない方や、プログラミングが苦手な方は、このプログラムを運用するための仮想環境の環境構築を最初にガイドしてくださいとプロンプトを出すことで、環境構築からガイドしてもらえます。

このようにするとポッドキャスト収録が劇的によくなりますので、ぜひ試してみてください。

ちなみに筆者のポッドキャストは、筆者のオリジナルマイクX-86Sで収録しています。

MIC
Handcrafted in Japan

その音に、
まだ見ぬ「静寂」と「熱」を。

記事で紹介している音をよりグレードアップするために
Kotaro Studioは、大量生産品では決して到達できない
音を追求したマイクロフォンを手作りしています

あなたの録音を、ワンランク上の世界へ。

今すぐ試聴する