end2end-asr-pytorch - audio processing - speech signal processing
end2end-asr-pytorch - audio processing - speech signal processing
https://github.com/gentaiscool/end2end-asr-pytorch
采样频率/取样频率是每秒钟采集声音样本的次数。采样频率越高,声音质量越好,声音还原越真实,同时占用资源越多。
采样位数/量化精度/采样值/取样值是采样样本幅度量化,用来衡量声音波动变化的一个参数。数值越大,分辨率越高,发出声音的能力越强。
采样数据记录的是振幅,采样精度取决于采样位数的大小:
1 字节 (8 bit) 记录 256 = 2^8
个等级,振幅划分成 256 个等级。
2 字节 (16 bit) 记录 65536 = 2^16
个等级 。
4 字节 (32 bit) 振幅细分到 4294967296 = 2^32
个等级。
音频单通道和双通道是声音通道的数目。单声道的声音只能使用一个喇叭发声,立体声可以使两个喇叭都发声 (左右声道),更能感受到空间效果。
音频帧记录了一个声音单元,单帧长度为采样位数和通道数的乘积。
交错模式数据以连续帧的方式存放,首先记录帧 1 的左声道样本和右声道样本,依次记录帧 2、3、4、… 的左声道样本和右声道样本。
非交错模式首先记录的是一个周期内所有帧的左声道样本,再记录所有右声道样本。
比特率是每秒的传输速率 bps (位速或比特率)。
1. normalization (归一化) + multiple channels average (多通道均值)
end2end-asr-pytorch/utils/audio.py
https://github.com/gentaiscool/end2end-asr-pytorch/blob/master/utils/audio.py
def load_audio(path):
sound, _ = torchaudio.load(path, normalization=True)
sound = sound.numpy().T
if len(sound.shape) > 1:
if sound.shape[1] == 1:
sound = sound.squeeze()
else:
sound = sound.mean(axis=1) # multiple channels, average
return sound
load(filepath, out=None, normalization=True, channels_first=True, num_frames=0, offset=0, signalinfo=None, encodinginfo=None, filetype=None)
Loads an audio file from disk into a tensor. - 将音频文件从磁盘加载到张量中。
1.1 Args
filepath (str or pathlib.Path)
: Path to audio file. - 音频文件的路径。
out (torch.Tensor, optional)
: An output tensor to use instead of creating one. (Default: None
).
normalization (bool, number, or callable, optional)
: If boolean True
, then output is divided by 1 << 31
= 2147483648
= 2^31
(assumes signed 32-bit audio), and normalizes to [-1, 1]
.
If number
, then output is divided by that number. - 输出除以该数字。
If callable
, then the output is passed as a parameter to the given function, then the output is divided by the result. (Default: True
)
channels_first (bool)
: Set channels first or length first in result. (Default: True
) - channels first - [C x L]
.
num_frames (int, optional)
: Number of frames to load. num_frames = 0
to load everything after the offset. (Default: 0
)
offset (int, optional)
: Number of frames from the start of the file to begin data loading. (Default: 0
)
signalinfo (sox_signalinfo_t, optional)
: A sox_signalinfo_t type, which could be helpful if the audio type cannot be automatically determined. (Default: None
)
encodinginfo (sox_encodinginfo_t, optional)
: A sox_encodinginfo_t type, which could be set if the audio type cannot be automatically determined. (Default: None
)
filetype (str, optional)
: A filetype or extension to be set if sox cannot determine it automatically. (Default: None
)
1.2 Returns
Tuple[torch.Tensor, int]
: An output tensor of size [C x L]
or [L x C]
(Default: channels first) where L
is the number of audio frames and C
is the number of channels. An integer which is the sample rate of the audio (as listed in the metadata of the file).
如果所要提取的语音特征不区分声道,则必须将多声道的语音转换成单声道。转换成单声道语音,需要计算多声道语音数据平均值即可。单声道的语音,不需要做转换。
2. pre-emphasis (预加重)
预加重的目的是只保留一定频率范围的信号,这个过程起到了高通滤波器的作用。高通滤波器对高频信号有着很好的放大作用,而且会大幅度压缩低频信号的幅度。同时,还会产生一个相位滞后的效应,这个对高频信号尤为明显。这个过程会在一定程度上消除唇齿效应。
语音处理通常使用 0.9 - 0.97,pre-emphasis = 0.97 比较常用。
3. 重采样
语音信号可能来自不同的设备,它们在录制的时候所设置的参数也不尽相同,最重要的一个就是采样率。
4. 语音信号分帧
hop_length - 帧移
win_length - 窗长
语音分析使用短时分析技术。语音信号是随时间变化的,它是一个非平稳态过程,不能用处理平稳信号的数字信号处理技术对其进行分析处理。由于不同的语音是由人的口腔肌肉运动构成声道某种形状而产生的响应,而这种口腔肌肉运动相对于语音频率来说是非常慢的。虽然语音信号具有时变特性,但是在一个短时间范围内 (10ms - 30ms),其特性基本保持不变,相对稳定,因而可以看作是一个准稳态过程,即语音信号具有短时平稳性。
语音信号处理常常要弄清楚语音中各个频率成分的分布。傅里叶变换要求输入信号是平稳的,输入不平稳的信号,得到的结果没有什么意义。语音在宏观上来看是不平稳的,但是从微观上来看,在比较短的时间内可以看成平稳的,就可以截取出来做傅里叶变换。
从整体上看,这段语音信号不平稳。红框框出来的部分是一帧,在这一帧内部的信号可以看成平稳的。
宏观上,帧长必须足够短来保证帧内信号是平稳的。口型的变化是导致信号不平稳的原因,所以在一帧的期间内口型不能有明显变化,一帧的长度应当小于一个音素的长度。正常语速下,音素的持续时间大约是 50~200 ms,帧长一般小于 50 ms。
微观上,帧长必须包括足够多的振动周期。傅里叶变换要分析频率,只有重复足够多次才能分析频率。语音的基频,男声在 100 Hz 左右,女声在 200 Hz 左右,换算成周期就是 10 ms 和 5 ms。一帧要包含多个周期,一般取至少 20 ms。
语音信号的分析和处理必须建立在短时的基础上,进行短时分析,将语音信号分为一段一段来分析其特征参数,其中每一段称为一帧,帧长一般取为 10ms - 30ms。对于整体的语音信号来讲,分析出的是由每一帧特征参数组成的特征参数时间序列。
为了使帧与帧之间平滑过渡,保持其连续性,分帧一般采用交叠分段的方法。前一帧和后一帧的交叠部分称为帧移,帧移与帧长的比值一般为 1/2。
语音数据不同于视频数据,语音数据原本没有帧的概念,为了传输与存储,采集的音频数据都是一段一段的。为了程序能够进行批量处理,会根据指定的长度 (时间段或者采样数) 进行分段,结构化为编程的数据结构,就是分帧。
由于常用的信号处理方法都要求信号是连续的,信号开始到结束,中间不能有断开。进行采样或者分帧后数据都断开了,所以要在帧与帧之间保留重叠部分数据,以满足连续的要求,重叠部分数据就是帧移。在分帧的时候,不要背靠背地截取,而是相互重叠一部分。相邻两帧的起始位置的时间差叫做帧移 (stride)。
语音信号是一个随时间变化的随机序列,从全局来看它并不是一个平稳随机过程。在较短的时间内,可以认为它是一个近似平稳的随机过程。可以按照帧长,把一个离散序列进行分组,每一组就是一帧。为了保证语音信号的连续性,一般相邻两帧之间还要有一定的重叠,重叠部分一般占帧长的 1/3 - 1/2。
每帧长度为 wlen
,后一帧对前一帧的位移量为 inc
,则重叠部分overlap = wlen - inc
。
5. time-domain windowing (时域加窗)
每一帧信号,在做傅里叶变换之前,要先进行加窗操作。加窗就是一帧信号与一个窗函数相乘,加窗是为了进行傅里叶展开。加窗的目的是让一帧信号的幅度在两端渐变到 0,渐变对傅里叶变换有好处,可以提高变换结果 (频谱) 的分辨率
加窗使全局更加连续,避免出现吉布斯现象/吉布斯效应 (Gibbs phenomenon)。加窗时,原本没有周期性的语音信号呈现出周期函数的部分特征。
加窗的代价是一帧信号两端的部分被削弱了,没有像中央的部分那样得到重视。弥补的办法是,帧不要背靠背地截取,要相互重叠一部分。相邻两帧的起始位置的时间差叫做帧移,常见的取法是取为帧长的一半。
将语音信号分帧后,需要对每一帧信号进行分析处理。对每一帧,选择一个窗函数,窗函数的宽度就是帧长。加窗就相当于把每一帧里面对应的元素变成它与窗序列对应元素的乘积。窗函数一般具有低通特性,加窗函数的目的是减少频域中的泄漏。
5.
对一帧信号做傅里叶变换,得到的结果叫频谱,就是下图中的蓝线。
上图中的横轴是频率,纵轴是幅度。频谱上就能看出这帧语音在 480 Hz 和 580 Hz 附近的能量比较强。语音的频谱,常常呈现出精细结构和包络两种模式。
精细结构就是蓝线上的一个个小峰,它们在横轴上的间距就是基频,它体现了语音的音高 - 峰越稀疏,基频越高,音高也越高。
包络是连接这些小峰峰顶的平滑曲线 (红线),它代表了口型,发的是哪个音。包络上的峰叫共振峰,图中能看出四个,在 500 Hz、1700 Hz、2450 Hz、3800 Hz 附近。对每一帧信号都做这样的傅里叶变换,就可以知道音高和口型随时间的变化情况,用于识别出一句话说的是什么。
在语音学中,一般只把声带叫声源,口腔和鼻腔都是声道。声带的振动模式是比较固定的,语音的包络主要由声道决定,共振峰反映声道的形状。
还没有评论,来说两句吧...