1
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?

the cabsからはじめる音響信号処理入門(Python)

Last updated at Posted at 2025-04-11

はじめに

the cabs のライブ、申し込みすら叶わなかったその悔しさを胸に、Pythonで音を分析してみました。この記事では、信号処理による解析の過程と結果をお届けします。

参考リンクまとめ

image.png

使う曲
the cabsの『2月の兵隊』は、私が浪人生活を共に過ごした曲です。
(ハヌマーンの猿の学生と共にずっと聴いてました笑笑)

Pythonコード

YouTubeの音楽動画から音声をMP3形式で抽出し、波形やスペクトログラムによって可視化を行った後、テンポ(BPM)の推定、MFCCに基づく構造クラスタリング、ピッチ推定、そしてスペクトル重心やRMSに基づいた感情スコア(焦燥感・浮遊感)の算出までを一貫して行います。

# 完全統合版:YouTube → MP3 → 可視化 → テンポ解析 → 構造解析 → ピッチ/感情マッピング

# --- Step 1: ライブラリのインストール(Colab再起動後用) ---
!pip install yt-dlp pydub librosa scikit-learn --quiet
!apt install ffmpeg -y

# --- Step 2: YouTubeからMP3をダウンロード ---
import yt_dlp
import os
from pydub import AudioSegment

# ダウンロードするYouTube動画のURL
url = "https://www.youtube.com/watch?v=soLFVWKc5B4"  # 任意のYouTube動画URL

# 出力パス設定
output_path = "downloaded_audio.%(ext)s"

# yt-dlpの設定オプション
ydl_opts = {
    'format': 'bestaudio/best',  # 最も良い音質を選択
    'outtmpl': output_path,      # 出力ファイル名設定
    'postprocessors': [{
        'key': 'FFmpegExtractAudio',    # MP3形式に変換
        'preferredcodec': 'mp3',        # MP3形式で保存
        'preferredquality': '192',      # 音質設定
    }],
    'quiet': True  # 出力を抑制
}

# YouTubeから音声をダウンロードしてMP3に変換
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
    ydl.download([url])

# ダウンロードしたファイルの名前を変更
os.rename("downloaded_audio.mp3", "audio.mp3")
print("MP3変換完了")  # 成功メッセージ

# --- Step 3: 音声読み込みと可視化 ---
import librosa
import librosa.display
import matplotlib.pyplot as plt
import numpy as np

# 音声ファイルを読み込み(サンプルレートは元のまま)
y, sr = librosa.load("audio.mp3", sr=None)

# 波形表示
plt.figure(figsize=(12, 3))
librosa.display.waveshow(y, sr=sr)  # 波形の可視化
plt.title("Waveform")               # タイトル
plt.xlabel("Time (s)")              # X軸ラベル
plt.ylabel("Amplitude")             # Y軸ラベル
plt.grid(True)                      # グリッド表示
plt.show()                          # 表示

# スペクトログラムの表示
plt.figure(figsize=(12, 4))
D = librosa.amplitude_to_db(np.abs(librosa.stft(y)), ref=np.max)  # スペクトルのdB表示
librosa.display.specshow(D, sr=sr, x_axis='time', y_axis='log')    # スペクトログラムの可視化
plt.colorbar(format='%+2.0f dB')                                      # カラーバー表示
plt.title("Spectrogram")                                              
plt.tight_layout()                                                    # レイアウト調整
plt.show()

# --- Step 4: テンポ解析 ---
tempo, beat_frames = librosa.beat.beat_track(y=y, sr=sr)  # テンポと拍の位置を抽出
print(f" 推定テンポ: {float(tempo):.2f} BPM")  # 推定されたBPM(ビート/分)

# --- Step 5: 拍ごとの構造解析 + ピッチ + 感情指標 ---
from scipy.cluster.hierarchy import linkage, fcluster
from sklearn.preprocessing import StandardScaler
from scipy.spatial.distance import cdist

# MFCC(メル周波数ケプストラム係数)の抽出と標準化
mfcc = librosa.feature.mfcc(y=y, sr=sr, n_mfcc=13)  # MFCCを13次元で抽出
mfcc_scaled = StandardScaler().fit_transform(mfcc.T)  # 標準化

# 階層クラスタリングによる楽曲の構造推定
Z = linkage(mfcc_scaled, method='ward')  # クラスタリング
clusters = fcluster(Z, t=4, criterion='maxclust')  # 4つのセクションに分ける

# ピッチの推定
pitches, magnitudes = librosa.piptrack(y=y, sr=sr)  # ピッチとその強度を抽出
pitch_values = pitches[magnitudes > np.median(magnitudes)]  # 強度が中央値以上のピッチを取得
pitch_mean = np.mean(pitch_values) if len(pitch_values) > 0 else 0  # 平均ピッチを計算

# 感情スコア指標計算
centroid = librosa.feature.spectral_centroid(y=y, sr=sr)[0]  # スペクトル重心
rms = librosa.feature.rms(y=y)[0]  # RMS(Root Mean Square)

# 感情スコアの計算
emotion_score = {
    "焦燥感 (agitation)": float(tempo) * np.mean(centroid) * np.mean(rms),  # 焦燥感の計算
    "浮遊感 (floating)": (1.0 / (1 + np.mean(centroid))) * (1.0 / (1 + np.std(rms)))  # 浮遊感の計算
}

# 楽曲構造の可視化(クラスタごとに色分け)
plt.figure(figsize=(12, 2))
plt.plot(clusters, color='purple')  # クラスタの表示
plt.title("Estimated Song Structure Segments")  # タイトル
plt.xlabel("Frame Index")  # X軸ラベル
plt.ylabel("Section Cluster")  # Y軸ラベル
plt.grid(True)  # グリッド表示
plt.tight_layout()  # レイアウト調整
plt.show()

# 感情スコアをDataFrame化して表示
import pandas as pd
emotion_df = pd.DataFrame.from_dict(emotion_score, orient='index', columns=["Score"])  # DataFrameに変換
emotion_df.index.name = "Emotion"  # インデックス名設定

# 感情スコアを表形式で表示
from IPython.display import display
display(emotion_df)  # 表形式で表示


結果

image.png

image.png

image.png


曲の解析レポート

波形(Waveform)

  • 時間軸(x軸):0秒〜約2分40秒
  • 振幅(y軸):-1.0〜+1.0 の範囲
  • 特徴
    • 冒頭と終盤に数回、無音(または静音)部分が確認できます(振幅が急激に0近くになる)。
    • 全体としてダイナミックレンジ(音量の強弱差)が狭い=マスタリングされている可能性が高い。
    • 波形がほぼフラットに近い → 圧縮された音像(音圧の高いEDMやポップ系に多い)。

スペクトログラム(Spectrogram)

  • x軸:時間(秒)
  • y軸:周波数(0Hz〜16kHz超)
  • :明るい=音が強い(+0 dB)、暗い=音が弱い(-80 dB)
  • 特徴
    • 64〜512 Hz付近が特に強く、安定的 → 低音(ベースやキック)が中心。
    • その上(1kHz〜8kHz)も中〜高域が活発 → ボーカルやシンセ、ハイハットの存在。
    • 無音区間(黒く抜けた帯)が数箇所ある → 曲のブレイクまたは展開の切り替え部分。
    • 周波数成分が常に満遍なく存在しており、音の密度が高い

構造セグメント(Estimated Song Structure Segments)

  • x軸:フレーム番号(≒時間)
  • y軸:クラスタ(1〜4のセクション番号)
  • 特徴
    • 曲全体を4つのセクションに分類(クラスタリング)。
    • セクションの切り替えが多く、繰り返しも見られる → リフレイン構造あり(例:サビの再登場)。
    • 12500〜13000付近の連続したクラスタ3〜4は、印象的な展開(ブリッジや間奏など)を示唆。
    • 最終的にはクラスタ2で終わっている → 導入と同じセクションに戻るパターンの可能性。

総合評価と推定ジャンル

項目 推定内容
テンポ感 速め(おそらく160〜180 BPM)
音圧 非常に高い(圧縮強め)
構成 セクションが多く、展開に富む
ジャンル傾向 EDM・アニメOP・J-pop・ロック系にも該当し得る
感情表現 焦燥感・推進力・高揚感が強い

1
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
1
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?