音声処理と認識|第4回

第4回:スペクトログラムの理解と可視化演習

音声処理と認識

担当:小野原彩香
形式:オンライン同期(Zoom)

Zoom録画を後日公開します。課題はメール/Google Driveでも提出可。

小野原彩香 / 北星学園大学
音声処理と認識|第4回

本日の到達目標

  1. フーリエ変換の直感的な意味を説明できる
  2. DFT・FFTをPythonで計算できる
  3. STFTとスペクトログラムの仕組みを理解する
  4. 窓長・FFT長・ホップ長の役割を区別できる
  5. スペクトログラムから周期性・摩擦・破裂などの特徴を読み取れる
小野原彩香 / 北星学園大学
音声処理と認識|第4回

音を正弦波・余弦波の成分で表す

複雑な波形を、振幅・周波数・位相の異なる成分で表す。有限長の標本列はDFTで分解・再構成できる。

次は有限個の成分による模式的な表現:

例:ピアノの「ラ(440 Hz)」

  • 基音はおよそ440 Hz。最も強い成分とは限らない
  • 倍音はおよそ880 Hz、1320 Hzなど
  • 実際には整数倍からのずれや打撃音も含む
  • 各成分の振幅・位相と時間変化が音色に関わる

連続信号のフーリエ表現には、周期性や収束についての条件もある。

小野原彩香 / 北星学園大学
音声処理と認識|第4回

DFTの定義と周波数ビン間隔

x[n]:N個の標本。X[k]:複素係数。|X[k]|:未正規化の振幅スペクトルで、元波形の振幅と同じ数値ではない。

fkは非負周波数側の対応。np.fft.fftの後半は負の周波数で、np.fft.fftfreqを使って対応させる。

  • ビン間隔と実際の分解能は別。分解能には窓長・窓形状なども関わる
  • 短い窓にゼロを補ってFFT長だけ増やしても、元の観測時間は増えない
  • 直接計算のO(N²)を、FFTではO(N log N)へ短縮する

NumPy FFTの定義・周波数・正規化

小野原彩香 / 北星学園大学
音声処理と認識|第4回

FFTをPythonで計算してみよう

import numpy as np
import matplotlib.pyplot as plt

fs, duration = 44100, 0.1
t = np.linspace(0, duration, int(fs * duration), endpoint=False)
x = np.sin(2*np.pi*440*t) + 0.5*np.sin(2*np.pi*880*t)

X = np.fft.fft(x)
N = len(x)
freqs = np.fft.fftfreq(N, d=1/fs)
positive = freqs > 0

plt.figure(figsize=(10, 4))
plt.plot(freqs[positive], np.abs(X[positive]))
plt.xlabel("周波数 (Hz)")
plt.ylabel("|X[k]|(未正規化)")
plt.title("FFTによる周波数スペクトル(440 Hz + 880 Hz)")
plt.xlim(0, 2000)
plt.grid(True)
plt.tight_layout()
plt.show()

N=4,410点、ビン間隔は10 Hz。440 Hzと880 Hzのピークを確認する。

小野原彩香 / 北星学園大学
音声処理と認識|第4回

短時間フーリエ変換(STFT)

全区間の振幅スペクトルだけでは、変化の時刻を直接読み取りにくい。複素DFT全体は逆変換できるので、時間情報を完全に失うわけではない。

m:フレーム番号、H:ホップ。ここでは窓長=FFT長=N。

設定役割・例
窓長周波数の分離と時間変化の追いやすさに関わる
FFT長周波数ビン間隔fs/Nを決める
ホップ表示するフレーム中心の間隔を決める

44.1 kHzなら窓1,024点=23.2 ms、ホップ512点=11.6 ms(50%重複)。

式は左端基準。librosaの既定center=Trueは中心をmHへ合わせる。公式STFT

小野原彩香 / 北星学園大学
音声処理と認識|第4回

窓関数:漏れとピーク幅のトレードオフ

時間領域で窓を掛けると、周波数領域で窓のスペクトルとの畳み込みになる。端の重みを抑えることで、サイドローブを低くできる。

上は対称形Hann。librosaのwindow="hann"は通常、分母Nの周期形を使う。Hammingの端は0にならない。

同じ窓長での傾向主ローブ・サイドローブ
矩形主ローブが狭い。サイドローブが高い
Hann・Hamming端の重みを抑え、主ローブ幅と漏れのバランスを取る
Blackmanサイドローブを抑えるが、主ローブは広がる

ビンに一致する純音なら、矩形窓でも他のDFTビンへの漏れは出ない。SciPyのHann窓

小野原彩香 / 北星学園大学
音声処理と認識|第4回

スペクトログラム:周波数軸と色の尺度を分ける

表示する量設定
横軸時間(秒)
縦軸周波数:線形Hz、対数周波数、メルなど
色振幅・パワーの線形値、または基準値に対するdB

  • 低域を広く表示するのは周波数軸の設定
  • 強弱の広い範囲を表示するのは色のdBの設定
  • amplitude_to_db(abs(D), ref=np.max)は最大値を0 dBとする。既定では80 dB下で切り詰める

この相対dBは音圧レベル(dB SPL)ではない。別々に最大値正規化した図から絶対的な音量差は読めない。

小野原彩香 / 北星学園大学
音声処理と認識|第4回

有声音・無声音・非発話区間の見え方

種類例スペクトログラムの手がかり
有声音母音の多く、/m/・/n/狭帯域では調波。広帯域では周期的な縦縞も
無声音子音/s/・[ɕ]、/t/・/k/の閉鎖・破裂摩擦は雑音状、破裂は短いバースト、閉鎖は低エネルギー
声がない区間発話の間など環境雑音が残る場合もある

「さ」全体は子音と母音を含むので、一律に無声音としない。色の暗さだけで無声・非発話を区別できない。

  • 有声音のF0は、準周期的な声帯振動の繰り返し周波数
  • 十分長い窓・線形Hz軸では、調波がほぼF0間隔に並ぶ
  • F0が動けば縞も曲がる。/s/の高域成分も話者・調音・録音帯域で変わる
小野原彩香 / 北星学園大学
音声処理と認識|第4回

日本語母音のフォルマント:実習用の設定例

フォルマントはスペクトル包絡の共鳴の山。個々の調波やF0とは区別する。

母音F1(Hz)F2(Hz)調音の目安
あ /a/8001200大きく開く
い /i/3002500前舌で狭める
う /u/4001400唇を強く突き出さない。標準語では[ɯ]など
え /e/5001900開口は中間程度
お /o/500800唇を丸める

値は合成母音の模式的な設定。日本語話者の平均値・判定基準ではない。

  • F1はおおむね開口が大きいほど高く、F2は前舌で高い傾向
  • 声道形状との一対一の対応ではなく、話者・文脈で変わる

東京外国語大学:日本語の「う」

小野原彩香 / 北星学園大学
音声処理と認識|第4回

演習1:スペクトログラムの描画(Colab)

import librosa, librosa.display
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import Audio, display

# LibriSpeechの英語朗読音声。初回はダウンロード
y, sr = librosa.load(librosa.example("libri1"), sr=None)
display(Audio(y, rate=sr))

n_fft, hop_length = 2048, 512
D = librosa.stft(y, n_fft=n_fft, hop_length=hop_length,
                 window="hann")
S_db = librosa.amplitude_to_db(np.abs(D), ref=np.max)
fig, ax = plt.subplots(figsize=(12, 4))
img = librosa.display.specshow(
    S_db, sr=sr, hop_length=hop_length, x_axis="time",
    y_axis="log", ax=ax, cmap="magma")
ax.set_title("スペクトログラム(対数周波数軸・相対dB)")
fig.colorbar(img, ax=ax, format="%+.0f dB")
plt.tight_layout()
plt.show()

窓時間=n_fft/sr。上の時間設定は、理論の25 ms窓の例とは異なる。

小野原彩香 / 北星学園大学
音声処理と認識|第4回

演習2:調波の強調で母音の違いを表す

本文のPython実習・セル4でmake_vowelを定義してから、次を実行する。共鳴の包絡Eで、F0の整数倍の成分を重み付けする。

vowels = {"あ": [800,1200,2600], "い": [300,2500,3200],
          "う": [400,1400,2400], "え": [500,1900,2700],
          "お": [500,800,2400]}
f0, sr, n_fft, hop = 150, 22050, 1024, 256
fig, axes = plt.subplots(1, 5, figsize=(18, 4))
for ax, (name, fmts) in zip(axes, vowels.items()):
    sig = make_vowel(f0, fmts, sr=sr)
    D = librosa.stft(sig, n_fft=n_fft, hop_length=hop)
    S_db = librosa.amplitude_to_db(np.abs(D), ref=np.max)
    librosa.display.specshow(S_db, sr=sr, hop_length=hop,
        x_axis="time", y_axis="hz", ax=ax, cmap="magma")
    ax.set_title(name); ax.set_ylim(0, 5000)
plt.tight_layout(); plt.show()

自然音声を完全に再現するモデルではない。全母音でF0=150 Hz、各音・各図を個別にピーク正規化する。

小野原彩香 / 北星学園大学
音声処理と認識|第4回

演習3:ブラウザ録音を変換して可視化する

本文のセル5でrecord_jsを定義する。これは3秒録音の完了後に解決するPromiseを返す。

from google.colab import output
print("マイクの許可後、3秒間話してください")
audio_data = output.eval_js(record_js)
続くセル6の処理目的
データURLのbase64をバイトへ戻すブラウザの録音データを保存
FFmpegで内容を自動判別して変換22,050 Hz・モノラルのPCM WAVへ
librosaで読み込み、STFTを計算波形・スペクトログラム・メル表示を比較

録音形式をWAVやWebMに固定して解釈しない。JavaScriptは実際のMIME形式を使い、finallyでマイクを停止する。

マイクを使えない場合はWAVをアップロードし、y, sr = librosa.load("ファイル名.wav", sr=None)から分析を実行できる。

小野原彩香 / 北星学園大学
音声処理と認識|第4回

演習課題(授業中)

課題1:発話サンプルを読み取る

  1. libri1の音声を聞き、有声・無声の区間候補を時刻で示す
  2. 十分長い窓・線形Hz軸で、調波の間隔からF0を目視推定する
  3. 同じ窓長でwindow="boxcar"とwindow="hann"を比較する

課題2(任意):自分の声で比較

  1. 「あ」と「い」を約1秒ずつ発声して録音する
  2. F1・F2の付近をスペクトル包絡の山として観察する
  3. スクリーンショットに矢印を加えて共有する

個々の調波とフォルマントを混同しない。表示条件も結果と一緒に記録する。

小野原彩香 / 北星学園大学
音声処理と認識|第4回

次回(第5回)の予告

形式:動画回(非同期)

テーマ:MFCC

従来の音声認識で広く使われてきた特徴量を学ぶ。
対数メルスペクトログラムとの違いも確認する。

予習課題(やれる範囲で)

  • 「メル尺度」「ケプストラム」を調べる
  • 第5回の動画教材を視聴してからテストに取り組む
小野原彩香 / 北星学園大学
音声処理と認識|第4回

第4回のまとめ

概念ポイント
DFT・FFT正弦波・余弦波の成分へ分解し、FFTで効率よく計算
STFT窓を掛けた短い区間ごとにDFTし、時間変化を表示
窓とFFT長実際の分解能と、FFTビンの刻みを区別
窓関数サイドローブの漏れを抑えるが、主ローブは広がる
スペクトログラム時間・周波数・色の強度。周波数軸とdB色は別
有声音・無声音周期性、摩擦、閉鎖、破裂などの手がかりを読む
フォルマント共鳴の包絡の山。F0や個々の調波とは別

提出:演習のスクリーンショットを翌週末23:59までにGoogle Driveまたはメールで提出。

小野原彩香 / 北星学園大学

この回はZoom演習回。Google Colabのコードを実際に動かしながら進める。 Zoom参加できない場合はメールで演習結果を送ってもらっても構わない。

プリズムの比喩で周波数分解を説明する。基音が最も強いとは限らず、有限長のDFTと一般の連続信号の表現条件は区別する。

明るさはこの図の最大値に対する相対dB。周波数軸の対数と色のdBを分ける。libri1は英語発話で、trumpetを有声/無声の発話分類に使わない。

本文セル5のrecord_jsを定義し、output.eval_jsで録音完了を待つ。実MIMEのデータをFFmpegでPCM WAVへ変換し、セル6で可視化する。録音できない場合は手元の音声をアップロードして分析する。