1. 処理の全体像(Pipeline)
YouTube → M4A音声 → FFTスペクトログラム → MP4動画 → MP4+音声 → ダウンロード
2. 各ステップの技術的解説
(1) yt-dlp による音声ダウンロード
!yt-dlp -f "bestaudio[ext=m4a]" -o "output_audio.%(ext)s" {url}
- YouTubeから最も高品質な音声トラック(
m4a形式)を取得。 -
yt-dlpはpytubeよりもYouTube更新に強く、安定動作。
(2) librosa による安全な音声読み込み
y, sr = librosa.load(filename, sr=None)
y = np.nan_to_num(y)
-
y: 音声波形データ(NumPy配列) -
sr: サンプリング周波数(例:44100 Hz) - NaNを除去してFFT演算が壊れないようにしている。
(3) 短時間フーリエ変換(STFT)
S = np.abs(librosa.stft(y, n_fft=2048, hop_length=int(0.1 * sr)))
S_db = librosa.amplitude_to_db(S, ref=np.max)
- 音声を0.1秒刻みで周波数成分(FFT)に分解。
- 対数スケール(dB)に変換して視覚的な強度を可視化。
(4) アニメーション生成 (FuncAnimation)
ani = FuncAnimation(fig, update, frames=len(times), interval=100)
ani.save("spectrogram.mp4", writer="ffmpeg", fps=10)
- 0.1秒ごとにスペクトログラムを更新して動画化。
- 空のフレームを避ける安全設計(IndexError対策済)。
(5) moviepy で音声を合成
audio = AudioFileClip(filename)
video = VideoFileClip("spectrogram.mp4").set_audio(audio)
video.write_videofile("spectrogram_with_audio.mp4", codec="libx264", audio_codec="aac")
- スペクトログラム映像(無音)に元の音声を重ね合わせ。
- 出力形式:H.264 + AAC の標準MP4形式。
(6) 自動ダウンロード
files.download("spectrogram_with_audio.mp4")
- Colab出力エリアに青いリンクを生成。
- クリックでブラウザ経由ダウンロード。
3. 安定化ポイント
このバージョンでは以下の点でエラーを防止:
- 無音ファイルやNaNを検出し除去。
- STFT結果が短すぎる場合に自動停止。
- Matplotlibの再描画イベントを安全に無効化。
4. 出力結果
-
spectrogram.mp4→ 無音のスペクトログラム映像 -
spectrogram_with_audio.mp4→ 音声付き最終MP4
(例:音楽や音声の時間変化を「見える化」)
5. 応用例
- 音楽の周波数構成やリズム解析
- 鳴き声・音響信号の視覚教材
- AIモデルの入力用データ可視化(音声→画像変換)
✅ Python実装例
# Program Name: youtube_to_spectrogram_stable.py
# Creation Date: 20251111
# Overview: Safe and stable Colab pipeline: YouTube → M4A → FFT Spectrogram → MP4 with audio → Download
!pip install yt-dlp librosa matplotlib moviepy --quiet
import os, warnings
import numpy as np
import librosa, librosa.display
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from moviepy.editor import AudioFileClip, VideoFileClip
from google.colab import files
warnings.filterwarnings("ignore")
#──────────────────────────────
# 1. Download YouTube audio (M4A format)
#──────────────────────────────
url = input("Enter YouTube URL (press Enter for sample): ")
if url.strip() == "":
url = "https://www.youtube.com/watch?v=2Vv-BfVoq4g"
print(f"Downloading audio from: {url}")
!yt-dlp -f "bestaudio[ext=m4a]" -o "output_audio.%(ext)s" {url}
filename = "output_audio.m4a"
if not os.path.exists(filename):
raise FileNotFoundError("Audio download failed.")
#──────────────────────────────
# 2. Load audio safely
#──────────────────────────────
y, sr = librosa.load(filename, sr=None)
if len(y) == 0:
raise ValueError("Loaded audio is empty.")
y = np.nan_to_num(y) # remove NaN if any
duration = librosa.get_duration(y=y, sr=sr)
print(f"Loaded {filename}: {duration:.2f} sec, Sampling rate {sr} Hz")
#──────────────────────────────
# 3. Compute STFT safely
#──────────────────────────────
frame_step = int(0.1 * sr)
n_fft = 2048
hop_length = frame_step
S = np.abs(librosa.stft(y, n_fft=n_fft, hop_length=hop_length))
if S.shape[1] < 2:
raise RuntimeError("STFT result too short for animation.")
S_db = librosa.amplitude_to_db(S, ref=np.max)
times = librosa.frames_to_time(np.arange(S_db.shape[1]), sr=sr, hop_length=hop_length)
#──────────────────────────────
# 4. Generate safe spectrogram animation
#──────────────────────────────
fig, ax = plt.subplots(figsize=(8, 4))
img = librosa.display.specshow(S_db[:, :1], sr=sr, hop_length=hop_length,
x_axis='time', y_axis='hz', cmap='magma', ax=ax)
ax.set(title="Spectrogram (0.1 s interval)")
fig.colorbar(img, ax=ax, format="%+2.0f dB")
def update(frame):
if frame >= len(times):
return ax
ax.clear()
librosa.display.specshow(S_db[:, :frame+1], sr=sr, hop_length=hop_length,
x_axis='time', y_axis='hz', cmap='magma', ax=ax)
ax.set(title=f"Spectrogram up to {times[frame]:.1f}s")
return ax
ani = FuncAnimation(fig, update, frames=len(times), interval=100, repeat=False)
ani.save("spectrogram.mp4", writer="ffmpeg", fps=10)
print("Saved spectrogram.mp4")
#──────────────────────────────
# 5. Combine audio + video
#──────────────────────────────
audio = AudioFileClip(filename)
video = VideoFileClip("spectrogram.mp4").set_audio(audio)
video.write_videofile("spectrogram_with_audio.mp4", codec="libx264", audio_codec="aac")
print("Saved spectrogram_with_audio.mp4")
#──────────────────────────────
# 6. Download MP4 automatically
#──────────────────────────────
files.download("spectrogram_with_audio.mp4")
✅ 結果
-
spectrogram.mp4
→ 音声の時間発展を0.1秒ごとにFFTで可視化した動画 -
spectrogram_with_audio.mp4
→ スペクトログラムに音声を合成
✅ 補足パラメータ
| 項目 | 変数 | 説明 |
|---|---|---|
| FFTサイズ | n_fft=2048 |
周波数分解能 |
| ステップ | hop_length=int(0.1*sr) |
0.1秒ごとの時間分解能 |
| 表示間隔 | interval=100 |
100ms単位のアニメーション更新 |
| カラーマップ | cmap='magma' |
dBスペクトログラムの視覚効果 |