はじめに
音声信号の分析において、声帯の振動(音源)と声道の形状(フィルタ)を分離することは基本的な課題です。ソース・フィルタモデルによれば、音声信号は声帯パルスと声道の伝達関数の畳み込みとして表されます。この畳み込みを周波数領域では積として扱え、対数をとることで加算に変換できます。これがケプストラム分析の中心的なアイデアです。
**ケプストラム(Cepstrum)**は 1963 年に Bogert, Healy, Tukey によって提案された造語で、「spectrum(スペクトル)」のアナグラムです。ケプストラム特有の用語(ケフレンシー、リフタリング等)もすべてスペクトル用語のアナグラムになっており、「スペクトルのスペクトル」というユニークな概念を表現しています。
本記事では、ケプストラムの数学的定義から、スペクトル包絡推定・ピッチ推定・MFCC(メル周波数ケプストラム係数)への応用まで、Pythonで体系的に解説します。
ソース・フィルタモデル
音声生成の数学的モデル
音声信号 \(s[n]\) は以下のソース・フィルタモデルで記述されます。
\[s[n] = e[n] * h[n] \tag{1}\]ここで:
- \(e[n]\) :音源信号(声帯パルス列または摩擦ノイズ)
- \(h[n]\) :声道インパルス応答(声道フィルタ)
- \(*\) :畳み込み演算
周波数領域での表現
畳み込みはフーリエ変換を通じて乗算に変換されます。
\[S(f) = E(f) \cdot H(f) \tag{2}\]対数をとると乗算が加算に変わります。
\[\log |S(f)| = \log |E(f)| + \log |H(f)| \tag{3}\]ここで:
- \(\log |S(f)|\) :観測された対数振幅スペクトル
- \(\log |E(f)|\) :音源の対数スペクトル(高速変動成分:ハーモニクス)
- \(\log |H(f)|\) :声道の対数スペクトル包絡(低速変動成分)
この加算関係に逆フーリエ変換を適用して、ケプストラムを定義します。
ケプストラムの定義
実数ケプストラム(Real Cepstrum)
信号 \(x[n]\) の実数ケプストラムは以下のように定義されます。
\[c[q] = \mathcal{F}^{-1}\{\log |\mathcal{F}\{x[n]\}|\} \tag{4}\] \[= \mathcal{F}^{-1}\{\log |X(f)|\} \tag{5}\]ここで \(q\) は**ケフレンシー(quefrency)**と呼ばれるパラメータで、スペクトルの「周波数」に相当します(単位は秒)。
具体的な計算手順は以下のとおりです。
- 信号 \(x[n]\) のDFTを計算:\(X[k] = \text{DFT}\{x[n]\}\)
- 振幅スペクトルを計算:\(|X[k]|\)
- 対数をとる:\(\log |X[k]|\)
- 逆DFTを計算:\(c[q] = \text{IDFT}\{\log |X[k]|\}\)
複素ケプストラム(Complex Cepstrum)
より一般的な複素ケプストラムは位相情報も含みます。
\[\hat{c}[q] = \mathcal{F}^{-1}\{\log X(f)\} = \mathcal{F}^{-1}\{\log |X(f)| + j \angle X(f)\} \tag{6}\]複素ケプストラムは位相の巻き戻し(unwrapping)が必要で計算が複雑ですが、完全な逆変換(元の信号の再構成)が可能です。
ケフレンシーの物理的意味
ケフレンシー \(q\) [秒] は、対数スペクトルの「周期」に対応します。
- 低ケフレンシー成分(\(q\) が小さい):対数スペクトルの緩やかな変動 → 声道スペクトル包絡 \(\log |H(f)|\)
- 高ケフレンシー成分(\(q = 1/f_0\) ):音源ハーモニクスによる細かな変動 → 基本周波数 \(f_0\) に対応するピーク
この分離を利用して、ケプストラムの適切な範囲を選択することで音源と声道特性を分離できます。
リフタリング(Liftering)
ケプストラム領域での窓処理を**リフタリング(liftering)**と呼びます(filtering のアナグラム)。
\[\tilde{c}[q] = c[q] \cdot l[q] \tag{7}\]\(l[q]\) はリフタ(lifter)と呼ばれる窓関数です。
- ローパスリフタ(低ケフレンシーのみ通過):声道スペクトル包絡を抽出
- ハイパスリフタ(高ケフレンシーのみ通過):音源成分(ハーモニクス)を抽出
ローパスリフタを適用した後に逆変換すると対数スペクトル包絡が、ハイパスリフタを適用すると音源の対数スペクトルが得られます。
Python実装
基本的なケプストラム計算
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import lfilter
def compute_real_cepstrum(signal, n_fft=None):
"""
実数ケプストラムの計算。
Parameters
----------
signal : np.ndarray
入力信号
n_fft : int, optional
FFTの点数(省略時は信号長の次の2の累乗)
Returns
-------
cepstrum : np.ndarray
実数ケプストラム
"""
if n_fft is None:
n_fft = 2 ** int(np.ceil(np.log2(len(signal))))
# ステップ1: DFT
X = np.fft.fft(signal, n=n_fft)
# ステップ2: 振幅スペクトルの対数
log_spec = np.log(np.abs(X) + 1e-10)
# ステップ3: 逆DFT(実部のみ取得)
cepstrum = np.fft.ifft(log_spec).real
return cepstrum
# --- 合成音声信号の生成 ---
fs = 8000
T = 0.05 # 50 ms のフレーム
t = np.arange(0, T, 1/fs)
f0 = 150 # 基本周波数 [Hz]
# 声帯パルス列(グロッタルパルスの近似)
glottal = np.zeros_like(t)
period_samples = int(fs / f0)
for i in range(0, len(t), period_samples):
if i + 30 < len(t):
glottal[i:i+30] = np.exp(-np.arange(30) * 0.15)
# 声道フィルタ: AR(4) モデル(母音「あ」の近似)
# フォルマント F1=800Hz, F2=1200Hz を持つ
ar_coeffs = [1, -2.2, 2.8, -1.8, 0.5] # AR多項式の係数
voiced = lfilter([1.0], ar_coeffs, glottal)
voiced = voiced / np.max(np.abs(voiced) + 1e-8)
# --- ケプストラム計算 ---
n_fft = 512
cepstrum = compute_real_cepstrum(voiced, n_fft=n_fft)
quefrency = np.arange(n_fft) / fs * 1000 # ms 単位
# 対数スペクトル
X = np.fft.fft(voiced, n=n_fft)
log_spec = np.log(np.abs(X[:n_fft//2 + 1]) + 1e-10)
freqs = np.fft.rfftfreq(n_fft, 1/fs)
# --- プロット ---
fig, axes = plt.subplots(3, 1, figsize=(12, 10))
# 時間波形
axes[0].plot(t * 1000, voiced)
axes[0].set_xlabel('Time [ms]')
axes[0].set_ylabel('Amplitude')
axes[0].set_title(f'Synthetic Voice: F0={f0} Hz, Formants F1≈800Hz, F2≈1200Hz')
axes[0].grid(True, alpha=0.3)
# 対数振幅スペクトル
axes[1].plot(freqs, log_spec)
axes[1].set_xlabel('Frequency [Hz]')
axes[1].set_ylabel('Log Magnitude')
axes[1].set_title('Log Amplitude Spectrum')
axes[1].set_xlim(0, 4000)
axes[1].grid(True, alpha=0.3)
# ケプストラム
axes[2].plot(quefrency[:n_fft//2], cepstrum[:n_fft//2])
axes[2].axvline(x=1000/f0, color='r', linestyle='--',
label=f'1/F0 = {1000/f0:.1f} ms')
axes[2].set_xlabel('Quefrency [ms]')
axes[2].set_ylabel('Cepstral Amplitude')
axes[2].set_title('Real Cepstrum: Pitch peak visible at 1/F0')
axes[2].set_xlim(0, 20)
axes[2].legend()
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
ケプストラムプロットのケフレンシー \(1/f_0 = 1000/150 \approx 6.7\) ms 付近にピークが現れ、これが基本周波数 \(f_0\) に対応します。
リフタリングによるスペクトル包絡抽出
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import lfilter
def lifter_cepstrum(cepstrum, cutoff_ms, fs, n_fft):
"""
ローパスリフタリングによるスペクトル包絡抽出。
Parameters
----------
cepstrum : np.ndarray
入力ケプストラム(長さ n_fft)
cutoff_ms : float
ローパスリフタのカットオフ [ms]
fs : float
サンプリング周波数
n_fft : int
FFTの点数
Returns
-------
envelope_log : np.ndarray
対数スペクトル包絡(長さ n_fft//2 + 1)
"""
cutoff_samples = int(cutoff_ms * fs / 1000)
# ローパスリフタ
lifter = np.zeros(n_fft)
lifter[:cutoff_samples] = 1.0
lifter[-cutoff_samples+1:] = 1.0 # 対称成分
# リフタリング
liftered = cepstrum * lifter
# 逆DFT → 対数スペクトル包絡
envelope_log = np.fft.fft(liftered, n=n_fft).real
return envelope_log[:n_fft//2 + 1]
# --- 上のコードに続けて実行 ---
cutoff_list = [2.0, 3.5, 5.0] # ms 単位のカットオフ
fig, axes = plt.subplots(len(cutoff_list), 1, figsize=(12, 10))
for ax, cutoff_ms in zip(axes, cutoff_list):
envelope = lifter_cepstrum(cepstrum, cutoff_ms, fs, n_fft)
ax.plot(freqs, log_spec, alpha=0.5, label='Log spectrum', color='steelblue')
ax.plot(freqs, envelope, linewidth=2, label=f'Envelope (cutoff={cutoff_ms} ms)',
color='red')
ax.set_xlabel('Frequency [Hz]')
ax.set_ylabel('Log Magnitude')
ax.set_title(f'Spectral Envelope Extraction via Low-pass Liftering ({cutoff_ms} ms)')
ax.set_xlim(0, 4000)
ax.legend()
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
カットオフが短すぎると包絡が平滑すぎて細部が失われ、長すぎるとハーモニクス成分が漏れ込みます。音声処理では一般的に 3〜4 ms 程度のカットオフが使われます。
ピッチ推定へのケプストラムの応用
import numpy as np
import matplotlib.pyplot as plt
def cepstrum_pitch_detection(signal, fs, fmin=60, fmax=600):
"""
ケプストラムによるピッチ(F0)検出。
Parameters
----------
signal : np.ndarray
入力音声フレーム
fs : float
サンプリング周波数 [Hz]
fmin, fmax : float
探索するF0の範囲 [Hz]
Returns
-------
f0 : float or None
推定F0 [Hz]
cepstrum : np.ndarray
ケプストラム
quefrency_ms : np.ndarray
ケフレンシー [ms]
"""
n_fft = 2 ** int(np.ceil(np.log2(len(signal))))
# ケプストラム計算
X = np.fft.fft(signal * np.hanning(len(signal)), n=n_fft)
log_spec = np.log(np.abs(X) + 1e-10)
cepstrum = np.fft.ifft(log_spec).real
# ケフレンシーの範囲を F0 範囲に変換
q_min = int(fs / fmax)
q_max = int(fs / fmin)
q_max = min(q_max, n_fft // 2)
# ピーク検出
pitch_region = cepstrum[q_min:q_max]
peak_idx = np.argmax(pitch_region) + q_min
f0 = fs / peak_idx
quefrency_ms = np.arange(n_fft) / fs * 1000
return f0, cepstrum, quefrency_ms
# --- 異なるF0での推定テスト ---
fs = 16000
T = 0.04 # 40 ms フレーム
t = np.arange(0, T, 1/fs)
test_f0s = [120, 180, 250] # Hz
fig, axes = plt.subplots(len(test_f0s), 2, figsize=(14, 10))
for i, f0_true in enumerate(test_f0s):
np.random.seed(i)
# 合成音声生成
period = int(fs / f0_true)
glottal = np.zeros_like(t)
for k in range(0, len(t), period):
if k + 20 < len(t):
glottal[k:k+20] = np.exp(-np.arange(20) * 0.2)
ar = [1, -1.8, 1.2, -0.5]
voice = lfilter([1.0], ar, glottal)
voice = voice / (np.max(np.abs(voice)) + 1e-8)
voice += 0.02 * np.random.randn(len(t))
f0_est, cep, q_ms = cepstrum_pitch_detection(voice, fs)
n_fft = len(cep)
axes[i, 0].plot(t * 1000, voice)
axes[i, 0].set_xlabel('Time [ms]')
axes[i, 0].set_ylabel('Amplitude')
axes[i, 0].set_title(f'Voice Frame: True F0 = {f0_true} Hz')
axes[i, 0].grid(True, alpha=0.3)
axes[i, 1].plot(q_ms[:n_fft//4], cep[:n_fft//4])
axes[i, 1].axvline(x=1000/f0_true, color='g', linestyle=':',
label=f'True 1/F0 = {1000/f0_true:.1f} ms')
axes[i, 1].axvline(x=1000/f0_est, color='r', linestyle='--',
label=f'Estimated F0 = {f0_est:.1f} Hz')
axes[i, 1].set_xlabel('Quefrency [ms]')
axes[i, 1].set_ylabel('Cepstral Amplitude')
axes[i, 1].set_title(f'Cepstrum Pitch Detection: Est={f0_est:.1f} Hz')
axes[i, 1].set_xlim(0, 30)
axes[i, 1].legend(fontsize=9)
axes[i, 1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
MFCC(メル周波数ケプストラム係数)
MFCC は現代の音声認識システムで標準的な特徴量であり、ケプストラム分析とメルスケールフィルタバンクを組み合わせたものです。
MFCCの計算手順は以下のとおりです。
- プリエンファシスフィルタ:\(y[n] = x[n] - \alpha x[n-1]\) (\(\alpha \approx 0.97\) )
- フレーム分割・窓関数(Hamming窓)の適用
- FFTによるパワースペクトルの計算
- メルフィルタバンク(三角フィルタを対数周波数軸上で均等配置)の適用
- フィルタバンク出力の対数をとる
- **離散コサイン変換(DCT)**で係数を計算
MFCCはケプストラムの「メルスケール版」と考えることができます。
\[\text{MFCC}[m] = \sum_{k=1}^{K} \log E_k \cdot \cos\left(m \left(k - \frac{1}{2}\right)\frac{\pi}{K}\right) \tag{9}\]ここで \(E_k\) は第 \(k\) メルフィルタバンクのエネルギー、\(K\) はフィルタバンクの数、\(m\) は次数です。
import numpy as np
import matplotlib.pyplot as plt
def hz_to_mel(hz):
"""Hzをメルに変換"""
return 2595 * np.log10(1 + hz / 700)
def mel_to_hz(mel):
"""メルをHzに変換"""
return 700 * (10 ** (mel / 2595) - 1)
def compute_mfcc(signal, fs, n_mfcc=13, n_filters=26, n_fft=512,
hop_length=160, fmin=80, fmax=None):
"""
MFCCの計算。
Parameters
----------
signal : np.ndarray
入力音声信号
fs : float
サンプリング周波数 [Hz]
n_mfcc : int
MFCC次元数
n_filters : int
メルフィルタバンクの数
n_fft : int
FFTの点数
hop_length : int
フレームシフト(サンプル数)
fmin, fmax : float
メルフィルタバンクの周波数範囲 [Hz]
Returns
-------
mfcc : np.ndarray
MFCC行列 (n_mfcc, n_frames)
"""
if fmax is None:
fmax = fs / 2
N = len(signal)
n_frames = 1 + (N - n_fft) // hop_length
# --- プリエンファシス ---
pre_emphasis = 0.97
signal_pe = np.append(signal[0], signal[1:] - pre_emphasis * signal[:-1])
# --- メルフィルタバンクの構築 ---
mel_min = hz_to_mel(fmin)
mel_max = hz_to_mel(fmax)
mel_points = np.linspace(mel_min, mel_max, n_filters + 2)
hz_points = mel_to_hz(mel_points)
bin_points = np.floor((n_fft + 1) * hz_points / fs).astype(int)
filter_bank = np.zeros((n_filters, n_fft // 2 + 1))
for m in range(1, n_filters + 1):
for k in range(bin_points[m-1], bin_points[m]):
filter_bank[m-1, k] = (k - bin_points[m-1]) / (bin_points[m] - bin_points[m-1])
for k in range(bin_points[m], bin_points[m+1] + 1):
filter_bank[m-1, k] = (bin_points[m+1] - k) / (bin_points[m+1] - bin_points[m])
# --- フレームごとの処理 ---
window = np.hamming(n_fft)
mfcc = np.zeros((n_mfcc, n_frames))
for i in range(n_frames):
start = i * hop_length
frame = signal_pe[start:start + n_fft]
if len(frame) < n_fft:
frame = np.pad(frame, (0, n_fft - len(frame)))
# パワースペクトル
power_spec = np.abs(np.fft.rfft(frame * window)) ** 2
# メルフィルタバンク適用
mel_energy = np.dot(filter_bank, power_spec)
log_mel_energy = np.log(mel_energy + 1e-10)
# DCT(MFCCの計算)
K = n_filters
for m in range(n_mfcc):
mfcc[m, i] = np.sum(
log_mel_energy * np.cos(m * (np.arange(K) + 0.5) * np.pi / K)
)
return mfcc
# --- テスト信号でMFCCを計算 ---
fs = 16000
duration = 1.5
t = np.arange(0, duration, 1/fs)
np.random.seed(42)
# 母音的な信号(フォルマント変化あり)
from scipy.signal import lfilter, butter
def make_vowel(t, fs, f0, f1, f2, noise_std=0.01):
"""フォルマントを持つ合成音声"""
period = int(fs / f0)
glottal = np.zeros(len(t))
for k in range(0, len(t), period):
if k + 25 < len(t):
glottal[k:k+25] = np.exp(-np.arange(25) * 0.15)
# バンドパスフィルタでフォルマント模擬
def bp(f_c, bw, sig):
b, a = butter(2, [f_c - bw/2, f_c + bw/2], btype='band', fs=fs)
return lfilter(b, a, sig)
v = bp(f1, 200, glottal) + 0.6 * bp(f2, 200, glottal)
v /= np.max(np.abs(v)) + 1e-8
v += noise_std * np.random.randn(len(v))
return v
# 3段階で変化する信号
t1 = t[t < 0.5]
t2 = t[(t >= 0.5) & (t < 1.0)]
t3 = t[t >= 1.0]
v1 = make_vowel(t1, fs, f0=120, f1=730, f2=1090) # 「あ」
v2 = make_vowel(t2, fs, f0=120, f1=270, f2=2290) # 「い」
v3 = make_vowel(t3, fs, f0=120, f1=400, f2=800) # 「う」
speech = np.concatenate([v1, v2, v3])
# MFCC計算
mfcc = compute_mfcc(speech, fs, n_mfcc=13, n_fft=512, hop_length=160)
n_frames = mfcc.shape[1]
time_axis = np.arange(n_frames) * 160 / fs
# プロット
fig, axes = plt.subplots(3, 1, figsize=(12, 10))
axes[0].plot(t[:len(speech)], speech, linewidth=0.5)
axes[0].axvline(x=0.5, color='r', linestyle='--', alpha=0.7)
axes[0].axvline(x=1.0, color='r', linestyle='--', alpha=0.7)
axes[0].set_xlabel('Time [s]')
axes[0].set_ylabel('Amplitude')
axes[0].set_title('Speech Signal: Vowel "a"→"i"→"u" Transition')
axes[0].grid(True, alpha=0.3)
img = axes[1].imshow(mfcc, aspect='auto', origin='lower',
extent=[0, time_axis[-1], 0, 13],
cmap='RdBu_r', interpolation='nearest')
axes[1].axvline(x=0.5, color='y', linestyle='--', alpha=0.8)
axes[1].axvline(x=1.0, color='y', linestyle='--', alpha=0.8)
axes[1].set_xlabel('Time [s]')
axes[1].set_ylabel('MFCC coefficient index')
axes[1].set_title('MFCC: Changes with vowel transitions visible')
plt.colorbar(img, ax=axes[1], label='Coefficient value')
# 各時刻のMFCCベクトル比較
frame_a = int(0.25 * fs / 160)
frame_i = int(0.75 * fs / 160)
frame_u = int(1.25 * fs / 160)
axes[2].plot(range(1, 14), mfcc[:, frame_a], 'o-', label='"a" (t=0.25s)')
axes[2].plot(range(1, 14), mfcc[:, frame_i], 's-', label='"i" (t=0.75s)')
axes[2].plot(range(1, 14), mfcc[:, frame_u], '^-', label='"u" (t=1.25s)')
axes[2].set_xlabel('MFCC index')
axes[2].set_ylabel('Coefficient value')
axes[2].set_title('MFCC vectors for different vowels: distinguishable patterns')
axes[2].legend()
axes[2].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
異なる母音は異なるMFCCパターンを示し、音声認識の特徴量として有効であることがわかります。
ケプストラム分析の実用的なまとめ
ケプストラムと関連手法の比較
| 手法 | ケフレンシー/係数 | 目的 | 主な用途 |
|---|---|---|---|
| 実数ケプストラム | 全ケフレンシー | F0検出・包絡分離 | ピッチ推定 |
| LPCケプストラム | 低次係数 | 声道モデルの係数 | 音声符号化 |
| MFCC | 低次DCT係数 | 音声の知覚特徴 | 音声認識 |
適切な手法の選択
| 目標 | 推奨手法 | 理由 |
|---|---|---|
| F0(ピッチ)推定 | ケプストラム法 / YINアルゴリズム | ケフレンシーピークが明確 |
| スペクトル包絡推定 | ローパスリフタリング | 低ケフレンシーに声道情報が集中 |
| 音声認識特徴量 | MFCC(+ デルタ・デルタデルタ) | 知覚的スケール + 効率的な表現 |
| 音楽ピッチ解析 | ケプストラム + ハーモニシティ | 倍音構造の把握 |
ケプストラムの応用分野
ケプストラム分析は音声処理に留まらず、幅広い分野で活用されています。
機械診断: 回転機械の振動信号にケプストラム分析を適用することで、歯車の欠陥やベアリングの損傷を示すサイドバンド成分を抽出できます。ケフレンシーピークの位置が欠陥の繰り返し周期に対応します。
地震解析: 地震波のデコンボルーションに利用され、震源特性と伝播特性の分離に応用されます。
エコー除去: 音響エコーの遅延と振幅をケプストラムから推定し、除去フィルタを設計します。
まとめ
- ケプストラムは対数スペクトルの逆フーリエ変換であり、「スペクトルのスペクトル」という概念を持つ
- ソース・フィルタモデルに基づき、ケプストラムの低ケフレンシー成分は声道スペクトル包絡、高ケフレンシー成分は音源ハーモニクスに対応する
- リフタリング(ケプストラム領域での窓処理)によって音源と声道特性を分離でき、スペクトル包絡推定に有効である
- ケプストラムのピッチ推定は \(1/f_0\) に対応するケフレンシーピークを検出することで実現できる
- MFCCはケプストラム分析にメルスケールとDCTを組み合わせた音声認識の標準特徴量である
- ケプストラム分析は音声処理のみならず、機械診断・地震解析・エコー除去など幅広い分野で応用される
関連記事
- 高速フーリエ変換(FFT)の仕組みとPython実装 - ケプストラム計算の基礎となるFFT/DFTを解説しています。
- 窓関数とパワースペクトル密度(PSD)の理論とPython実装 - スペクトル解析の基礎とPSD推定手法を解説しています。
- 短時間フーリエ変換(STFT)の理論とPython実装 - 時間変化する信号のスペクトログラム解析を解説しています。
- 自己相関・相互相関の理論とPython実装 - ケプストラムと関連するF0推定手法(YIN)との比較になります。
- ウェーブレット変換の理論とPython実装 - 時間-周波数解析の別アプローチを解説しています。
- ARIMAモデルによる時系列予測 - スペクトル分析と補完的な時系列モデリング手法です。
- 適応フィルタの理論とPython実装 - ケプストラムのエコー除去応用と関連するフィルタ設計を解説しています。
参考文献
- Bogert, B. P., Healy, M. J. R., & Tukey, J. W. (1963). “The quefrency alanysis of time series for echoes: Cepstrum, pseudo-autocovariance, cross-cepstrum, and saphe cracking.” Proceedings of the Symposium on Time Series Analysis, 209-243.
- Oppenheim, A. V., & Schafer, R. W. (2004). “From frequency to quefrency: A history of the cepstrum.” IEEE Signal Processing Magazine, 21(5), 95-106.
- de Cheveigné, A., & Kawahara, H. (2002). “YIN, a fundamental frequency estimator for speech and music.” Journal of the Acoustical Society of America, 111(4), 1917-1930.
- Davis, S., & Mermelstein, P. (1980). “Comparison of parametric representations for monosyllabic word recognition in continuously spoken sentences.” IEEE Transactions on Acoustics, Speech, and Signal Processing, 28(4), 357-366.
- Python Speech Features (MFCC library)
- Librosa: MFCC implementation