1. 语音信号基础
语音学习笔记,来源于视频👉语音信号处理基础
语音信号处理的一系列操作
周期性的信号可以表达为三角函数
Y
(
n
)
=
A
s
i
n
(
ω
∗
x
+
φ
)
Y(n)=Asin(\omega*x+\varphi)
Y(n)=Asin(ω∗x+φ)
三个参数决定形状:
A
:幅度;
φ
:相位;
ω
:与周期成线性倒数
A:幅度;\varphi:相位;\omega :与周期成线性倒数
A:幅度;φ:相位;ω:与周期成线性倒数
- 上图操作过程的解读
- FFT变换之后得到STFT谱,可以得到两种特征
- 幅度特征
- 相位特征
- 对STFT谱取绝对值或平方值(抹除相位特征)->得幅度谱
(论文中会写:spectrum/ amplitude spectrum/ magnitude spectrum)
(有的文章会用STFT谱直接指代幅度谱,看有没有特别说明对相位进行的处理) - Melspectrum取对数是对数梅尔谱,有的文章可能会用Melspectrum指代Log melspectrum
- Fbank谱提取的特征更加高维
- FFT变换之后得到STFT谱,可以得到两种特征
- NT
- 现在谱的提取非常成熟,不需要掌握所有数学计算,只需要知道用到的时候他做了些什么
- 主要使用amp spec和melspec,通常可以用librosa或torchaudio库进行提取
- Fbank、MFCC蕴含信息较少,不适用于大数据时代。但有些任务有本身特殊性,还会用到MFCC,如情感语音转换任务
语音信号概念
时域信号:一位的信号,Y表示幅度,X表示采样点
信号长度(singal)=采样时间(time)采样率(Sample rate)
(如 一条语音,长度=2s,采样率=16000HZ(采样间隔为1/16000 s),则时间表示法在计算机中表示为(216000)的一维向量;也可以说这条语音的长度为32000(采样点表示法)但此时必须确定采样率,才能确定实际时长)
👉在语音表示的数据量不变的情况下,采样率增大,则采样时间减小
人类感受声音的过程主要是在感知声音的不同成分的频率高低以及强度
人类听觉感知范围:200hz-2Whz
语音范围:200hz-8000hx
采样定理+人类语音信号范围——>所以大部分情况下采样率设置为16000hz或22000hz
eg. 信号10sin(x)+0.1sin(100x)+0.01sin(1000x) 主要感知振幅为10,频率为1的10sin(x)信号
- 对于一条语音不能只得到一组幅度和相位:海森堡测不准原理
- 所以不能对太长(50ms以上)或太短(10ms-)的语音信号做傅里叶变换,否则得到的数据不准确
- 对一段语音信号,需要进行分帧(类似于一维卷积)
- 将窗(一般设置为25ms或1024采样点)从时间0开始截取语音信号,提取幅度和相位,移动窗(移动的幅度称帧移hop size/hop length),再次提取(但是两个窗之间会有重叠)
- 帧数=语音信号长度//hop_length + 1 (//:整除)
- NT:帧数与窗长没有关系,只和帧移有关
- 一个语音可以由一个一维向量变为二维矩阵
- 如何规定基础信号频率,这“很多组”幅度和相位究竟是哪些信号
自己设定,代码中通常是"n_fft"参数设定
频率组数=n_fft//2+1 (傅里叶算法可知)
多少组幅度和相位其实就代表可以得到多少组特征的维度
设n_fft=256,实际上就是设定了n_fft//2+1=129组不同频率的参数(根据傅里叶算法算出来) - 频率组的频率
根据窗的长度确定频率组间隔,从0开始线性增加信号分量的频率
频率依次为:1/T, 2/T, 3/T …, 129/T(接上面的129的例子)
eg.窗长=320,SP(采样率)=16000
该窗时间长度:T=320/16000=0.02s(信号长度=采样时间采样率,故采样时间=信号长度/采样率)
故频率组的频率即为:50hz,100hz,150hz,…,(12950)hz
通过设定不同的参数,规定了不同频率的频率组,也确定了频率的大小
接下来用Fourier算法,根据这些频率求出其幅度和相位
代码实现
语音信号特征获取流程
- 一条语音信号(时域),已知采样率SR,时间长度Time
- 设定win length,Hop length,n_fft(希望得到多少组频率)
- 频域信号(2维),其帧数=(SR*Time)//hop+1,其维度=n_fft//2+1,张量形状=(维度,帧数)
import torch
import torchaudio
wavform = torch.rand(1,16000) # 这里设置1代表一条语音,batch size
# 这里也可以不设置采样率16000,因为后面提取过程中都使用采样点表示法的话,就不需要考虑采样率了
# 一般是使用“采样点表示法”
# NT:excepted 0< win_length <= n_fft
# 实例化可以提取stft的函数stft_function
stft_function = torchaudio.transforms.Spectrogram(n_fft=350, win_length=320, hop_length=160)
# n_fft设置每个小窗的语音可以得到多少组频率,窗长win_length(每一个频率的大小,每两个频率的间隔=320/(1/16000)),帧移hop_length
STFT_spec = stft_function(wavform)
print(STFT_spec.shape) # torch.Size([1, 176, 101])
# 176=350//2+1
# 101 = 16000//160 + 1
这样处理出来的数据,就是深度学习的输入
频谱图
- 不同语音任务中使用的谱是不同的
(这是使用谱训练,也可是使用波形训练)
梅尔谱
在幅度谱的基础上,乘以一个“梅尔变换”,得到一个80维度的梅尔谱
这种谱的80个频率组更接近人耳的听觉感知范围,但是相应的,蕴含的语音信息比幅度谱要少一些。
故在一些面向人类的语音任务中较为常用
NT:深度学习中的梅尔谱大多数情况下指对数梅尔谱
- 傅里叶谱(幅度谱):一个等间隔提取的谱(如10hz 20hz 30hz…)
- 梅尔谱:对数增长组(10hz 15hz 17hz 18hz…),更符合人耳耳蜗结构
语音降噪不一定在人类听觉范围,所以不需要变成梅尔谱
- 大部分论文中,幅度谱的维度n_fft=1024(即特征的维度n_dim=513)
- 梅尔谱的维度n_mel=80
- 故可以看出,幅度谱携带的信息比梅尔谱要多,但也携带了比较多的噪声,mel谱转化为人类听觉范围,80的频率可以描述人类发出的声音了
代码
'''
提取mel谱经典代码
训练效果不太好
'''
import librosa
import soundfile
import numpy as np
from matplotlib import pyplot as plt
import scipy.signal as siganl
import copy
# 设定参数
sr = 16000 # Sample rate # 采样率
n_fft = 1024 # fft points(samples) # 希望提取的频率的组数 1024即513组频率
frame_shift = 0.0125 # seconds # 时间表示法
frame_length = 0.05 # seconds
hop_length = int(sr*frame_shift) # samples 帧移 # 采样点表示法
win_length = int(sr*frame_length) # samples 窗长
n_mels = 80 # Number of Mel banks to generate 梅尔谱的频率组数
power = 1.2 # Exponent for amplifying the predicted magnitude
n_iter=100 # Number of inversion interations
preemphasis = .97 # or None ## 预加重参数
max_db = 100
ref_db = 20
top_db = 15
def get_spectrograms(fpath):
'''
Return normalized log(melspectrogram) and log(magnitude) from 'sound_file'.
Args:
sound_file: A string. The full path of sound file
Return:
mel: A 2d array of shape(T, n_mels) <- Transpoesd
mag: A 2d array of shape(T, 1+n_fft//2) <- Transposed
'''
# Loading sound file
y, sr = librosa.load(fpath,sr=22050) # 读取语音信号波形(可以自己设置采样率)
# y:一维时域信号
plt.figure()
plt.title("Oringin: wacform")
plt.plot(y)
plt.show()
# Trimming 头尾静音消除
y, _ = librosa.effects.trim(y, top_db=top_db) # 切除top_db强度以下的噪音
# Preemphasis 预加重 (预加重算法->经典信号处理算法)
y = np.append(y[0], y[1:]-preemphasis*y[:-1])
# stft
linear = librosa.stft(
y=y
,n_fft=n_fft
,hop_length = hop_length
,win_length=win_length
)
# 得到短时傅里叶谱,包括幅度和相位信息,通常将 相位 去掉
# linear中,每一个 元素 都是 复数 a+bj ->和傅里叶有关 -> 复数变实数,取绝对值abs->幅度
# magnitude spectrogram 幅度
mag = np.abs(linear) # (1+n_fft//2, T)
# mel spectrogram
mel_basis = librosa.filters.mel(sr, n_fft, n_mels) # 提取出来是一个梅尔谱矩阵(n_mels, 1+n_fft//2)
mel = np.dot(mel_basis, mag) # mel谱 = mel谱矩阵*幅度谱矩阵
# 这两步原理和信号处理算法有关,深度学习先不用管
# to decibel 梅尔谱变分贝单位,即取对数
mel = 20*np.log10(np.maximum(1e-5,mel))
mag = 20*np.log10(np.maximum(1e-5,mag))
# 下面的内容在不同的代码中会有不同的实现
# normalize 归一化
mel = np.clip((mel-ref_db+max_db)/max_db,1e-8,1) # 第二第三个参数设置最小最大值
mag = np.clip((mag-ref_db+max_db)/max_db,1e-8,1)
# Transpose
mel = mel.T.astype(np.float32) # (T, n_mels)
mag = mag.T.astype(np.float32) # (T, 1+n_fft//2)
return mel,mag
不过这段代码在实际训练中效果不是很好,tacotron中的提取在训练中效果更好