Pythonで楽器の音を可視化する
はじめに
データソリューション事業部の関田です。
今回は、ベースで「A(ラ)」の音を弾いて録音し、その音響データをPythonを使って様々な角度から可視化してみました。
音響信号処理の基礎である「波形」表示に加え、周波数解析を用いた「スペクトル」「メルスペクトログラム」の描画について、コードと実際の出力結果を交えて紹介します。
本記事はDAL Tech Blog Advent Calendar 2025として投稿しました。全ての記事は以下からご確認いただけます。
音響処理の基礎知識
まずは、音をデジタルデータとして扱うための基本を簡単におさらいします。
音とは?
物理的には空気の振動です。振動数によって音の高さが決まり、振動数が大きいほど音が高くなります。
この振動をマイクで電気信号に変換し、さらにAD変換(アナログ-デジタル変換)を行うことで、コンピュータで扱える数値配列になります。
サンプリング周波数(Sample Rate: sr)
1秒間に何回音のデータを記録するかという指標です。単位はHz(ヘルツ)です。
例えば、CD音質なら44.1kHz(1秒間に44,100回)です。この数値が高いほど、高い周波数の音まで正確に記録できます。
音階と周波数
音階と周波数の関係について、少し掘り下げてみます。
基準周波数 A=440Hz
一般的にチューニングの基準となる「A4(ラ)」は 440Hz です。
音階が1オクターブ下がると、周波数は半分になります。つまり、「A4(ラ)」よりも1オクターブ低いA3の周波数は220Hz、3オクターブ低いA1の周波数は55Hzです。
ベースにおける「A」
ベースの3弦開放弦(A1)の基音は55Hzですが、実際にはその整数倍の周波数を持つ倍音が多く含まれています。 そのため、1つの音を弾いただけでも、スペクトル上では以下のような周波数の成分が観測されます。
- 55Hz (A1): 基音(一番低い基本の音)
- 110Hz (A2): 第2倍音(1オクターブ上のラ)
- 220Hz (A3): 第4倍音(2オクターブ上のラ)
今回の可視化では、この「55Hz, 110Hz...」という倍音成分がどう見えるかに注目します。
補足:CDEとドレミの関係について
音楽に馴染みがない方向けに、アルファベット(音名)と一般的な「ドレミ」(階名)の対応表を載せておきます。
| 音名 | C | D | E | F | G | A | B |
|---|---|---|---|---|---|---|---|
| 階名 | ド | レ | ミ | ファ | ソ | ラ | シ |
実装コードと結果
それでは、実際のコードと出力結果を順に見ていきます。
⓪ 音響データ読み込み
librosa.load を使うと、WAVファイルの読み込み、モノラル変換、正規化(-1.0〜1.0)、サンプリングレートの取得を一括で行ってくれます。
y, sr = librosa.load("wavefile.wav", sr=None)
① 波形
時間ごとの振幅(音の大きさ)の変化を表します。
librosa.display.waveshow を使うと、時間軸の計算や描画の最適化を自動で行ってくれます。
plt.figure(figsize=(12, 4))
# librosaを使って波形を描画
librosa.display.waveshow(y, sr=sr, color="blue")
plt.title("Waveform")
plt.xlabel("Time [s]")
plt.ylabel("Amplitude")
plt.tight_layout()
plt.show()
▼ 出力結果

- 0.8秒付近で弦が弾かれ、その後徐々に音が減衰していく様子が見て取れます。
- 波形を見るだけでは「何の音程か」までは判別が難しいですが、音の立ち上がりや長さを確認するのに適しています。
② 周波数スペクトル
FFT(高速フーリエ変換)を行い、音響を周波数成分に分解します。「どの高さの音が、どれくらい含まれているか」がわかります。 ここには librosa ではなく、シンプルで高速な numpy のFFTを使います。
N = len(y)
# FFT計算(実数部分のみ)
fft_values = np.fft.rfft(y)
# 周波数軸の作成
freqs = np.fft.rfftfreq(N, d=1/sr)
# 振幅スペクトル(絶対値)
magnitude = np.abs(fft_values)
plt.figure(figsize=(12, 4))
plt.plot(freqs, magnitude)
plt.xscale("log") # 周波数は対数軸で表示
plt.xlabel("Frequency [Hz]")
plt.ylabel("Magnitude")
plt.title("Frequency Spectrum")
plt.grid(True, which="both", linestyle="--", alpha=0.7)
plt.xlim(10, 2000)
# ベース(A)の倍音ライン (55Hz, 110Hz, 220Hz...)
plt.vlines([55, 110, 220, 440, 880, 1760], 0, np.max(magnitude)*1.1,
linestyle="dashed", colors="gray", alpha=0.8)
plt.tight_layout()
plt.show()
▼ 出力結果

- グレーの点線は左から 55Hz, 110Hz, 220Hz... を示しています。
- グラフのいくつかのピークが、この点線と綺麗に重なっていることがわかります。これにより、この音が正確にチューニングされたA(ラ)の音であることが視覚的に確認できます。
- 特に110Hz(第2倍音)の成分が強く出ているのが見て取れます。
③ メルスペクトログラム
スペクトルに「時間変化」の情報を加え(スペクトログラム)、さらに周波数軸を人間の聴覚特性に合わせた「メル尺度」に変換したものです。 librosa.feature.melspectrogram で計算し、librosa.display.specshow で描画します。
n_fft = 2048
hop_length = 512
# メルスペクトログラムの計算
S = librosa.feature.melspectrogram(y=y, sr=sr, n_fft=n_fft, hop_length=hop_length)
# 振幅をデシベル(dB)に変換
S_db = librosa.power_to_db(S, ref=np.max)
plt.figure(figsize=(12, 5))
# y_axis="mel" とすることでメル尺度で表示
librosa.display.specshow(S_db, sr=sr, hop_length=hop_length, x_axis="time", y_axis="mel")
plt.colorbar(format="%+2.0f dB")
plt.title("Mel Spectrogram")
plt.tight_layout()
plt.show()
▼ 出力結果

- 横軸が時間、縦軸が周波数(メル尺度)、色が強さを表します。
- 人間の聴覚特性に合わせて、低周波数帯域(画像の下の方)が拡大されて表示されています。
- 時間が経つにつれて高音成分(上の方)から消えていく、弦楽器の減衰特性がよく見えます。
おわりに
Librosaを使うと、非常に少ないコード量でリッチな可視化が可能になります。 今回はベースの「A」音を例に、以下の3つの視点で分析しました。
- 波形: 音のアタック感や減衰といった「形状」を見る。
- スペクトル: 55Hz, 110Hzといった「周波数成分」を見る。
- メルスペクトログラム: 人間の耳に近い感覚で「時間変化と周波数成分」を同時に見る。
楽器の音作りや、音響解析の前処理としてぜひ活用してみてください!
Discussion