《语音信号处理试验教程》(梁瑞宇等)的代码主要是Matlab实现的,现在Python比较热门,所以把这个项目大部分内容写成了Python实现,大部分是手动写的。使用CSDN博客查看帮助文件:
Python语音基础操作–2.1语音录制,播放,读取
Python语音基础操作–2.2语音编辑
Python语音基础操作–2.3声强与响度
Python语音基础操作–2.4语音信号生成
Python语音基础操作–3.1语音分帧与加窗
Python语音基础操作–3.2短时时域分析
Python语音基础操作–3.3短时频域分析
Python语音基础操作–3.4倒谱分析与MFCC系数
Python语音基础操作–4.1语音端点检测
Python语音基础操作–4.2基音周期检测
Python语音基础操作–4.3共振峰估计
Python语音基础操作–5.1自适应滤波
Python语音基础操作–5.2谱减法
Python语音基础操作–5.4小波分解
Python语音基础操作–6.1PCM编码
Python语音基础操作–6.2LPC编码
Python语音基础操作–6.3ADPCM编码
Python语音基础操作–7.1帧合并
Python语音基础操作–7.2LPC的语音合成
Python语音基础操作–10.1基于动态时间规整(DTW)的孤立字语音识别试验
Python语音基础操作–10.2隐马尔科夫模型的孤立字识别
Python语音基础操作–11.1矢量量化(VQ)的说话人情感识别
Python语音基础操作–11.2基于GMM的说话人识别模型
Python语音基础操作–12.1基于KNN的情感识别
Python语音基础操作–12.2基于神经网络的情感识别
Python语音基础操作–12.3基于支持向量机SVM的语音情感识别
Python语音基础操作–12.4基于LDA,PCA的语音情感识别
代码可在Github上下载:busyyang/python_sound_open
语音合成主要可分为波形合成法,参数合成法,规则合成法。
分帧处理后,需要将多个帧合并起来,涉及的操作有去创函数和去交叠操作。方法为:
常用的三种数据叠加方法为重叠相加法,重叠存储法,线性比例重叠相加法。
假设信号长度为 N 1 N_1 N1,窗函数 h ( n ) h(n) h(n)的长度为 N N N,每帧的信号 x i ( n ) x_i(n) xi(n)长度为 M M M,将每帧信号与窗函数后面补零,使得长度都为 N + M − 1 N+M-1 N+M−1,得到 x ^ i ( n ) \hat x_i(n) x^i(n)和 h ^ ( n ) \hat h(n) h^(n)。计算 x ^ i ( n ) \hat x_i(n) x^i(n)和 h ^ ( n ) \hat h(n) h^(n)的卷积结果,可以使用FFT和IFFT来计算结果。卷积结果 y ( n ) y(n) y(n)的长度为N+M-1,把重叠部分相加,与不重叠部分共同构成输出。把重叠相加推广到时域,信号 x ( n ) x(n) x(n)是分帧的,每帧为:
x i ( m ) = { x ( n ) , ( i − 1 ) Δ L + 1 ⩽ n ⩽ i Δ L + L 0 , 其 他 , m ∈ [ 1 , L ] x_i(m)=\left \{\begin{array}{ll} x(n)&,(i-1)\Delta L+1 \leqslant n \leqslant i\Delta L +L\\ 0&,其他 \end{array} \right.,m\in [1,L] xi(m)={x(n)0,(i−1)ΔL+1⩽n⩽iΔL+L,其他,m∈[1,L]
y i − 1 ( m ) y_{i-1}(m) yi−1(m)与 y i ( m ) y_{i}(m) yi(m)有M个样点重叠,重叠部分为 ( i − 1 ) Δ L + 1 到 ( i − 1 ) Δ L + M (i-1)\Delta L+1 到 (i-1)\Delta L +M (i−1)ΔL+1到(i−1)ΔL+M,输出为:
y ( n ) = { y ( n ) , n ⩽ ( i − 1 ) Δ L y ( n ) + y i ( m ) , ( i − 1 ) Δ L + 1 ⩽ n ⩽ ( i − 1 ) Δ L + M , M ∈ [ 1 , M ] y i ( m ) , ( i − 1 ) Δ L + 1 + M ⩽ n ⩽ ( i − 1 ) Δ L + L , M ∈ [ M + 1 , L ] y(n)=\left \{\begin{array}{ll} y(n)&,n\leqslant (i-1)\Delta L\\ y(n)+y_i(m)&,(i-1)\Delta L+1 \leqslant n \leqslant (i-1)\Delta L +M,M\in[1,M]\\ y_i(m)&,(i-1)\Delta L+1+M \leqslant n \leqslant (i-1)\Delta L+L,M\in[M+1,L] \end{array} \right. y(n)=⎩⎨⎧y(n)y(n)+yi(m)yi(m),n⩽(i−1)ΔL,(i−1)ΔL+1⩽n⩽(i−1)ΔL+M,M∈[1,M],(i−1)ΔL+1+M⩽n⩽(i−1)ΔL+L,M∈[M+1,L]
重叠存储法是分帧数据在前面补零达到N+M-1的长度。FFT后只取后面M个样本点,输出到输出序列上。
y ( n ) = { y ( n ) , n ⩽ ( i − 1 ) Δ L y i ( m ) , ( i − 1 ) Δ L + 1 ⩽ n ⩽ ( i − 1 ) Δ L + M , M ∈ [ 1 , Δ L ] y(n)=\left \{\begin{array}{ll} y(n)&,n\leqslant (i-1)\Delta L\\ y_i(m)&,(i-1)\Delta L+1 \leqslant n \leqslant (i-1)\Delta L +M,M\in[1,\Delta L]\\ \end{array} \right. y(n)={y(n)yi(m),n⩽(i−1)ΔL,(i−1)ΔL+1⩽n⩽(i−1)ΔL+M,M∈[1,ΔL]
如果前一帧和下一帧的变化较大,那么用重叠相加法就不合适了。可以将重叠部分进行比例计权后相加。假设重叠部分长度为M,两个斜三角的窗函数 w 1 ( n ) = ( n − 1 ) / M w_1(n)=(n-1)/M w1(n)=(n−1)/M和 w 2 ( n ) = ( M − n ) / M w_2(n)=(M-n)/M w2(n)=(M−n)/M, n ∈ [ 1 , M ] n\in [1,M] n∈[1,M]。
如果前一帧的重叠部分为 y 1 y_1 y1,后一帧的重叠部分为 y 2 y_2 y2。那么 y ( n ) = y 1 ( n ) w 1 ( n ) + y 2 ( n ) w 2 ( n ) y(n)=y_1(n)w_1(n)+y_2(n)w_2(n) y(n)=y1(n)w1(n)+y2(n)w2(n)。
y ( n ) = { y ( n ) , n ⩽ ( i − 1 ) Δ L y ( n ) w 2 ( n ) + y i ( m ) w 1 ( n ) , ( i − 1 ) Δ L + 1 ⩽ n ⩽ ( i − 1 ) Δ L + M , M ∈ [ 1 , M ] y i ( m ) , ( i − 1 ) Δ L + 1 + M ⩽ n ⩽ ( i − 1 ) Δ L + L , M ∈ [ M + 1 , L ] y(n)=\left \{\begin{array}{ll} y(n)&,n\leqslant (i-1)\Delta L\\ y(n)w_2(n)+y_i(m)w_1(n)&,(i-1)\Delta L+1 \leqslant n \leqslant (i-1)\Delta L +M,M\in[1,M]\\ y_i(m)&,(i-1)\Delta L+1+M \leqslant n \leqslant (i-1)\Delta L+L,M\in[M+1,L] \end{array} \right. y(n)=⎩⎨⎧y(n)y(n)w2(n)+yi(m)w1(n)yi(m),n⩽(i−1)ΔL,(i−1)ΔL+1⩽n⩽(i−1)ΔL+M,M∈[1,M],(i−1)ΔL+1+M⩽n⩽(i−1)ΔL+L,M∈[M+1,L]
import numpy as np
def Filpframe_OverlapA(x, win, inc):
"""
基于重叠相加法的信号还原函数
:param x: 分帧数据
:param win: 窗
:param inc: 帧移
:return:
"""
nf, slen = x.shape
nx = (nf - 1) * inc + slen
frameout = np.zeros(nx)
x = x / win
for i in range(nf):
start = i * inc
frameout[start:start + slen] += x[i, :]
return frameout
def Filpframe_OverlapS(x, win, inc):
"""
基于重叠存储法的信号还原函数
:param x: 分帧数据
:param win: 窗
:param inc: 帧移
:return:
"""
nf, slen = x.shape
nx = (nf - 1) * inc + slen
frameout = np.zeros(nx)
x = x / win
for i in range(nf):
frameout[slen + (i - 1) * inc:slen + i * inc] += x[i, slen - inc:]
return frameout
def Filpframe_LinearA(x, win, inc):
"""
基于比例重叠相加法的信号还原函数
:param x: 分帧数据
:param win: 窗
:param inc: 帧移
:return:
"""
nf, slen = x.shape
nx = (nf - 1) * inc + slen
frameout = np.zeros(nx)
overlap = len(win) - inc
x = x / win
w1 = [i / overlap for i in range(overlap)]
w2 = [i / overlap for i in range(overlap - 1, -1, -1)]
for i in range(nf):
if i == 0:
frameout[:slen] = x[i, :]
else:
M = slen + (i - 1) * inc
y = frameout[M - overlap:M] * w2 + x[i, :overlap] * w1
xn = x[i, overlap:]
yy = np.hstack((y, xn))
frameout[M - overlap:M - overlap + slen] += yy
return frameout
from chapter2_基础.soundBase import *
from chapter7_语音合成.flipframe import *
from chapter3_分析实验.C3_1_y_1 import enframe
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
data, fs = soundBase('C7_1_y.wav').audioread()
wlen = 256
wnd = np.hamming(wlen)
overlap = 100
f = enframe(data, wnd, overlap)
plt.figure(figsize=(14, 12))
# 7.1.1
fn_overlap = Filpframe_OverlapA(f, wnd, overlap)
plt.subplot(3, 2, 1)
plt.plot(data / np.max(np.abs(data)), 'k')
plt.title('原始信号')
plt.subplot(3, 2, 2)
plt.title('还原信号-重叠相加法')
plt.plot(fn_overlap / np.max(np.abs(fn_overlap)), 'c')
# 7.1.2
fn_s = Filpframe_OverlapS(f, wnd, overlap)
plt.subplot(3, 2, 3)
plt.plot(data / np.max(np.abs(data)), 'k')
plt.title('原始信号')
plt.subplot(3, 2, 4)
plt.title('还原信号-重叠存储法')
plt.plot(fn_s / np.max(np.abs(fn_s)), 'c')
# 7.1.3
fn_l = Filpframe_LinearA(f, wnd, overlap)
plt.subplot(3, 2, 5)
plt.plot(data / np.max(np.abs(data)), 'k')
plt.title('原始信号')
plt.subplot(3, 2, 6)
plt.title('还原信号-线性叠加法')
plt.plot(fn_l / np.max(np.abs(fn_l)), 'c')
plt.savefig('images/flipframe.png')
plt.close()