0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

MP3音声 → 時間ごとのスペクトログラム化(0.1秒刻みのFFT) → MP4動画化

0
Posted at

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-dlppytube よりも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スペクトログラムの視覚効果

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?