由于好奇音频提取特征再重构音频过程中各部分数据的‘样子’,故而对开源代码提取其对音频的处理过程以及重构过程,经测试发现其仅保留了文本内容的发声特征而遗失了说话者的特征(即能够了解音频的内容而无法分辨说话者)。
通过更换对音频的处理方式以及修改FFT窗口大小等参数进行消融实验发现如下参数及处理过程对音频的提取及还原效果最好。
import numpy,wave
import numpy as np
import torch
import librosa
import librosa.display
import matplotlib.pyplot as plt
import os
import pylab
from scipy import signal
import copy
sr = 22050 # 采样率
n_fft = 2048 # fft窗口大小
hop_length = 256# 帧移长度
win_length = 1024# 窗口长度,默认n_fft
n_mels = 80 # mel滤波器个数
n_iter = 64 # 迭代次数
preemphasis = .97 # 预加重、可为None
#各音量分贝
max_db = 100
ref_db = 20
top_db = 15
def griffin_lim(spectrogram):
X_best = copy.deepcopy(spectrogram)
for i in range(n_iter):
X_t = invert_spectrogram(X_best)
est = librosa.stft(X_t, n_fft, hop_length, win_length=win_length)
phase = est / np.maximum(1e-8, np.abs(est))
X_best = spectrogram * phase
X_t = invert_spectrogram(X_best)
y = np.real(X_t)
return y
def _mel_to_linear_matrix(sr, n_fft, n_mels):
m = librosa.filters.mel(sr, n_fft, n_mels)
m_t = np.transpose(m)
p = np.matmul(m, m_t)
d = [1.0 / x if np.abs(x) > 1.0e-8 else x for x in np.sum(p, axis=0)]
return np.matmul(m_t, np.diag(d))
def invert_spectrogram(spectrogram):
return librosa.istft(spectrogram, hop_length, win_length=win_length, window="hann")
# 文件路径
testpath="./test.wav"
# 文件提取处理
speech, sr = librosa.core.load(path=testpath, sr=None, mono=True)
speech, _ = librosa.effects.trim(speech, 22) #除去音频首尾静音,第二个参数为将22分贝以下的视为静音
speech = np.append(speech[0], speech[1:] - 0.97*speech[:-1])
# 短时傅里叶变换
linear=librosa.stft(y=speech, n_fft=1024, hop_length=256)
lin_spec=np.abs(linear)
# 构建mel滤波器组
mel_filterbank = librosa.filters.mel(sr=sr, n_fft=1024, n_mels=80)
mel_spec = np.dot(mel_filterbank, lin_spec)
mel = 20 * np.log10(np.maximum(1e-5, mel_spec))
lin = 20 * np.log10(np.maximum(1e-5, lin_spec))
# 归一化
mel = np.clip((mel - ref_db + max_db) / max_db, 1e-8, 1)
lin = np.clip((lin - ref_db + max_db) / max_db, 1e-8, 1)
# 转置
mel = mel.T.astype(np.float32)
lin = lin.T.astype(np.float32)
mel = mel.T
mel = (np.clip(mel, 0, 1) * max_db) - max_db + ref_db
# 转振幅
mel = np.power(10.0, mel * 0.05)
m = _mel_to_linear_matrix(sr, n_fft, n_mels)
mag = np.dot(m, mel)
# 音频重构
wav = griffin_lim(mag)
# 滤波
wav = signal.lfilter([1], [1, -preemphasis], wav)
# 修剪音频
wav, _ = librosa.effects.trim(wav)
# 保存
librosa.output.write_wav("temp.wav", wav.astype(np.float32), sr)
实验内容记录:
如上所示,表现较好的为5-8,且就griffin_lim迭代次数而言,64次迭代与其他表现效果无异,故而上述代码采取13的参数内容。
如上所述,可以发现griffinlim编码,加窗效果会更佳,这是因为发生了吉布斯现象(Gibbs phenomenon):在不连续点处产生高频分量导致傅里叶变换后的频谱出现局部峰值。此外,由于周期信号在分帧过程中被截断,会导致频谱在整个频带你发生拖尾现象,该现象称为频谱泄漏(spectral leakage)。