语音处理

语音处理(英语:Speech processing),又称语音讯号处理人声处理,是研究语音信号及其处理方法的学科。通常,这些信号是以数字形式进行处理的,因此语音处理可以被视为数字信号处理的一个特殊案例,专门应用于语音信号。语音处理涉及语音信号的获取、操控、存储、传输和输出等多个方面。不同的语音处理任务包括语音识别语音合成、说话人分离、语音增强、说话人识别等。

为单片机实验服务的语音模块

语音的相关常识

一般声音档格式

  • 取样频率:22050Hz
  • 单声道或双声道
  • 每笔资料用8个bit来表示
  • 电脑中没有经过任何压缩的声音档:*.wav

声音的频率

  • 频率范围

人耳可以辨识频率:20Hz ~ 20000Hz

说话:150~2000Hz

电话系统频域:小于3500Hz

电脑音效卡取样频率:44100Hz (最新技术可达192K)(一般用22050Hz、11025Hz 即可)

  • 超音波(ultrasound)与次声波(infrasound)

超音波(ultrasound):> 20000Hz

次声波(infrasound):< 20Hz

  • 人对于频率的分辨能力,是由于频率的"比"决定

对人类而言,300Hz和400Hz之间的差别,与3000Hz和4000Hz之间的差别是相同的

声音的波长

  • 波长较长:传播距离较远,但容易散射
  • 波长较短:衰减较快,但传播方向较接近直线

声音的速度

  • 声音在空气中传播速度: 每秒340 公尺(15°C 时)
  • 一般人,耳翼到鼓膜之间的距离: 2.7公分。所以,人类对3000Hz 左右频率的声音最敏感
  • 每增加1°C,声音的速度增加0.6 m/sec
  • 声音在水中的传播速度是1500 m/sec,在铝棒中的传播速度是5000 m/sec

分贝

  • 分贝(dB): 

其中P为音强(正比于振福的平方),C为0dB时的音强

  • 每增加10dB,音强增加10倍,振幅增加   倍;每增加3dB ,音强增加2倍,振幅增加  

音乐讯号

  • 电子琴Do 的频率:

低音Do: 131.32 Hz

中音Do: 261.63 Hz

高音Do: 523.26 Hz

更高音Do: 1046.52 Hz

  • 音乐每增加八度音,频率变为2 倍
  • 每一音阶有12个半音。增加一个半音,频率增加  倍(1.0595 倍)
  • 音乐通常会出现“和弦”(chord) 的现象。除了基频  Hz 之外,也会出现2 Hz, 3 Hz, 4 Hz , …… 的频率

语音处理的工作

  • 语音编码(Speech Coding)
  • 语音合成(Speech Synthesis)
  • 语音增强(Speech Enhancement)
  • 语音辨认(Speech Recognition)

音素→音节→词→句→整段话

  • 说话人办认(Speaker Recognition)
  • 其他:语意、语言、情绪

子音和母音

  • 母音: 依唇型而定。母音的能量大,频率偏低,时间较长,出现在子音后或独立出现
  • 子音: 在口腔,鼻腔中某些部位将气流暂时堵住后放开。子音的能量小,频率偏高,时间较短,出现在母音前

语意学的角色

  • 以“语意学”或“机率”来补足语音辨识的不足

例如:经过判定,一个声音可能是

ㄅㄧ ㄖㄢ ㄆㄧ ㄖㄢ

ㄅㄧ ㄌㄢ ㄆㄧ ㄌㄢ

这个声音是“必然”的机率比较大。

ㄅㄛ ㄅㄛ ㄆㄛ ㄆㄛ

可能是“伯伯”,也可能是“婆婆”,看上下文

  • 当前主流的语音辨识技术:

梅尔频率倒频谱(Mel-Frequency Cepstrum) + 语意分析 + 机器学习(Machine Learning)

语音档的处理(Matlab)

读取声音档

  • 电脑中,没有经过压缩的声音档都是*.wav 的型态,有经过压缩的声音档是*.mp3的型态
  • 读取: audioread (2015版本以后的Matlab,wavread 将改为audioread)
  • 例: [x, fs] = audioread('C:\WINDOWS\Media\ringin.wav');

可以将ringin.wav 以数字向量x来呈现。fs:取样频率(sampling frequency)

这个例子当中size(x) = 122868 2 (2指的是双声道(Stereo,俗称立体声)),fs = 22050

  • .wav 档中所读取的资料,值都在 -1 和 +1 之间
  • 画出声音的波型

time = [0:size(x,1)-1]/fs; (x 是用audioread 所读出的向量)

subplot(2,1,1); plot(time, x(:,1)); xlim([time(1),time(end)])

subplot(2,1,2); plot(time, x(:,2)); xlim([time(1),time(end)])

绘出频谱

X = fft(x(:,1));

X=X.';

N=length(X); N1=round(N/2);

dt=1/fs;

X1=[X(N1+1:N),X(1:N1)]*dt; (shifting for spectrum)

f=[[N1:N-1]-N,0:N1-1]/N*fs; (valid f)

plot(f, abs(X1));

声音的播放

  • sound(x): 将x以8192Hz 的频率播放
  • sound(x, fs): 将xfs Hz 的频率播放

注:x必须是1 个column (或2个columns),且x的值应该介于 -1 和 +1 之间

  • soundsc(x, fs): 自动把x的值调到 -1 和 +1 之间再播放

制作.wav档:audiowrite

audiowrite(filename, x, fs)

将数据x变成一个*.wav 档,取样频率为fsHz

注:x必须是1 个column (或2个columns),且x的值应该介于 -1 和 +1

录音的方式

基本指令说明

以下这三个指令要并用才可以录音

  • recorder = audiorecorder(Fs, nb, nch); (提供录音相关的参数)

Fs: sampling frequency

nb: using nb bits to record each data

nch: number of channels (1 or 2)

  • recordblocking(recorder, Sec); (录音的指令)

recorder: the parameters obtained by the command audiorecorder

Sec: the time length for recording

  • audioarray = getaudiodata(recorder);

(将录音的结果,变成audioarray 这个column vector,如果是双声道,则audioarray 是两个column vectors)

范例

Sec = 3; (录音的时间为三秒)

Fs = 8000; (sampling frequency 为8000 Hz)

recorder = audiorecorder(Fs, 16, 1);

recordblocking(recorder, Sec);

audioarray = getaudiodata(recorder); (录音结果为audioarray,是一个column vector (如果是双声道,则是两个column vectors))

sound(audioarray, Fs); (播放录音的结果)

t = [0:length(audioarray)-1]./Fs;

plot (t, audioarray); (将录音的结果用图画出来)

xlabel('sec','FontSize',16);

audiowrite('test.wav', audioarray, Fs) (将录音的结果存成*.wav 档)


语音档的处理(Python)

安装模组

  • pip install numpy
  • pip install scipy
  • pip install matplotlib # plot
  • pip install pipwin
  • pipwin install simpleaudio # vocal files
  • pipwin install pyaudio

读音讯档

要先import 相关模组: import wave

读取音档

wavefile = wave.open('C:/WINDOWS/Media/Alarm01.wav', 'rb')

获得音档取样频率和音讯长度:

fs =wavefile.getframerate() # sampling frequency

num_frame = wavefile.getnframes() # length of the vocal signal

>>> fs

22050

>>> num_frame

122868

读取波形与数据

要先import 相关模组: import numpy as np

  • str_data = wavefile.readframes(num_frame)
  • wave_data = np.frombuffer(str_data, dtype=np.int16) # 转成整数型态
  • wave_data = wave_data / max(abs(wave_data)) # normalization
  • n_channel = 2
  • wave_data = np.reshape(wave_data, (num_frame, n_channel)) # 若为双声道音档需要做reshape

画出音讯波形图

要先import 相关模组: import matplotlib.pyplot as plt

  • time = np.arange(0, num_frame)*1/fs
  • plt.plot(time, wave_data)
  • plt.show()

画出频谱

要先import 相关模组: from scipy.fftpack import fft

  • fft_data = abs(fft(wave_data[:,1]))/fs # only choose the 1st channel # 注意要乘上1/fs
  • n0=int(np.ceil(num_frame/2))
  • fft_data1=np.concatenate([fft_data[n0:num_frame],fft_data[0:n0]]) # 将频谱后面一半移到前面
  • freq=np.concatenate([range(n0-num_frame,0),range(0,n0)])*fs/num_frame # 频率轴跟著调整
  • plt.plot(freq,fft_data1)
  • plt.xlim(-1000,1000) # 限制频率的显示范围
  • plt.show()

播放声音

要先import 相关模组: import simpleaudio as sa

  • n_bytes =2 # using two bytes to record a data
  • wave_data = (2**15-1)* wave_data # change the range to -215 ~ 215
  • wave_data = wave_data.astype(np.int16)
  • play_obj = sa.play_buffer(wave_data, n_channel, n_bytes, fs)
  • play_obj.wait_done()

制作音档

  • f = wave.open('testing.wav', 'wb')
  • f.setnchannels(2) # 设定声道数
  • f.setsampwidth(2) # 每个samples 有几个位元组
  • f.setframerate(fs) # 设定取样频率
  • f.writeframes(wave_data.tobytes())
  • f.close()

录音

要先import 相关模组: import pyaudio

import pyaudio

pa=pyaudio.PyAudio()

fs = 44100

chunk = 1024

stream = pa.open(format=pyaudio.paInt16, channels=1,rate=fs, input=True, frames_per_buffer=chunk)

vocal=[]

count=0

时频分析结果分析

Matlab

画出时频分析结果

可采行两种方式:

  • 使用mesh 指令画出立体图(但结果不一定清楚,且执行时间较久)
  • 将amplitude 变为gray-level,用显示灰阶图的方法将结果表现出来

假设y 是时频分析计算的结果

image(abs(y)/max(max(abs(y)))*C) % C 是一个常数,可以选C=400

或image(t, f, abs(y)/max(max(abs(y)))*C)

colormap(gray(256)) % 变成gray-level 的图

set(gca,'Ydir','normal') % 若没这一行, y-axis 的方向是倒过来的

set(gca,'Fontsize',12) % 改变横纵轴数值的font sizes

xlabel('Time (Sec)','Fontsize',12) % x-axis

ylabel('Frequency (Hz)','Fontsize',12) % y-axis

title('STFT of x(t)','Fontsize',12) % title

计算程式执行时间的指令

tic (这指令如同按下码表)

toc (show 出码表按下后已经执行了多少时间)

注:通常程式执行第一次时,由于要做程式的编译,所得出的执行时间会比较长。程式执行第二次以后所得出的执行时间,是较为正确的结果

Python

事前安装模组

  • pip install numpy
  • pip install matplotlib

画出时频分析结果

假设y为时频分析结果(应为二维的矩阵数列),将y 以灰阶方式画出来

import numpy as np

import matplotlib.pyplot as plt

C = 400

y = np.abs(y) / np.max(np.abs(y)) * C

plt.imshow(y, cmap='gray', origin='lower') # 加上origin='lower' 避免上下相反

plt.xlabel('Time (Sec)')

plt.ylabel('Frequency (Hz)')

plt.show()

若要加上座标轴数值(在plt.show()之前加上以下程式码)

x_label = ['0', '10', '20', '30'] # 横轴座标值

y_label = ['-5', '0', '5'] # 纵轴座标值

plt.xticks(np.arange(0, x_max, step=int(x_max/(len(x_label)-1)), x_label)

plt.yticks(np.arange(0, y_max, step=int(y_max/(len(y_label)-1)), y_label)


语音讯号的来源

人声是由于声带震动,而产生声音。当运动肌肉挤压,使脏中的空气通过声带时,空气流动使得声带做周期性的震动,又再一次震动了空气,接著,带著动能空气离开气管到达口腔或鼻腔,在腔室中震动,最后离开在嘴唇传到人耳变成声音。

若调整口腔中舌头的位置,会产生不同种类的声音,如果舌头没有做太多的动作,空气只有在口腔中共振,接著直接流出嘴唇,会产生母音,若提起舌头,使口鼻腔相通,则会出现鼻音。

语音讯号分类

从中文发音的观点来说,声音仍可分为子音与母音,母音和子音可以用两种方式区分:

  • 发声方式:一般而言,母音跟嘴唇形状有关,而且不与鼻腔共振。相对而言,在发出子音时,就会运用到鼻腔配合发声。
  • 频谱分析:从频谱上观察可以发现子音的讯号频率较高,持续时间较短,且会在母音之前出现。而母音的频率较低,持续时间较长,在子音后或独立出现,另外,母音的能量也会比子音大。

下面列出中文注音符号中的母音、子音及其拼音。

  • 母音:ㄚ ㄛ ㄜ ㄝ ㄞ ㄟ ㄠ ㄡ ㄢ ㄣ ㄤ ㄥ ㄦ 一 ㄨ ㄩ
母音
汉语拼音 a o e e ai ei ao ou
通用拼音 a o e e ai ei ao ou
母音
汉语拼音 an en ang eng er i,y u,w yu,ju
通用拼音 an en ang eng er i,y u,w yu,ju
  • 子音:ㄅ ㄆ ㄇ ㄈ ㄉ ㄊ ㄋ ㄌ ㄍ ㄎ ㄏ ㄐ ㄑ ㄒ ㄓ ㄔ ㄕ ㄖ ㄗ ㄘ ㄙ
子音
汉语拼音 b p m f d t n
通用拼音 b p m f d t n
子音
汉语拼音 l g k h j q x
通用拼音 l g k h j q x
子音
汉语拼音 zh ch sh r z c s
通用拼音 zh ch sh r z c s

语音的架构

要分析语音讯号前,必须先了解其架构,语音的要素从小到大分别是:音素→音节词汇→句子→整段话。

音素是声音的最小单位,例如“呵”这个字的音素,就是“ㄏ”和“ㄜ”,但是音素和注音符号并不相等,例如“鸥”虽然只有“ㄡ”这个母音,但是由于是双母音,所以会把他拆成两个音素。音节在中文而言,就是只一个字,例如:“天天开心”就有四个音节。词汇是文字组成的有意义片段,各种不同的词汇集结成句子,最后变成整段话,这就是语音的架构。

语音处理方法

用麦克风或其他装置收到的类音声音讯号,经由类比数位转换装置,将资料数据化进行处理,最后再经过数位类比转换装置输出。因此,我们在处理时是针对数位讯号,语音讯号是一种离散时间讯号。其讯号处理流程如下:

  1. 收取并取样讯号:利用麦克风或各种收音装置,收取类比语音讯号,再用ADC装置(如类比数位转换卡)把类比讯号变成数位讯号,接著根据奈奎斯特理论作取样,若不符合理论则会造成讯号失真。
  2. 量化及编码:由于电脑中的记忆都是0和1,因此要将所收到的数据用一段适合的0跟1去储存,这个动作就称为量化,所使用的0与1越多,所需的记忆体越多。接著利用编码器将数值以波形呈现,因此虽然是数位讯号,但是在电脑中所见到的是类比。
  3. 讯号标准化:将语音讯号标准化,使其数值都落在同一个范围。
  4. 音框选择:由于语音讯号是一段很长的讯号,因此会针对想要处理的部分取音框。
  5. 端点侦测:端点侦测的目的是使讯号处理的范围更精确,只要设定一个音量阈值,若讯号小于阈值,则将其视为没讯号,但是若杂讯过高,则会产生误差。
  6. 去杂讯:由于杂讯多集中在高频的部分,因此利用简单的高频滤波器,就可以去掉部分杂讯。

基本处理方法

语音讯号是属于离散时间系统,因此会用离散时间的傅立叶转换去做处理,除此之外,折积窗函数都是一定会使用到的处理方法。

  • 离散时间傅立叶转换:
 
 
  • 折积:

两讯号做convolution等于,两讯号先做傅立叶转换,相乘后再做反傅立叶转换,借此可以更快速的处理讯号。

 

语音处理的应用

语音处理主要有两个目的:

  • 减少讯号杂讯,做出想要的讯号模组。
  • 进行语音辨识,使人可以利用语言与电脑沟通。


参见

参考文献

  • J. Benesty, M. M. Sondhi, Y. Huang (ed). Springer Handbook of Speech Processing. Springer, 2007. ISBN 978-3-540-49125-5.
  • J. Benesty, S. Makino, J. Chen (ed). Speech Enhancement. Springer, 2005. ISBN 978-3-540-24039-6.
  • Jian-Jiun Ding, “Advanced Digital Signal Processing”, NTU, 2021.
  • Jian-Jiun Ding, “Time Frequency Analysis and Wavelet Transforms ”, NTU, 2021.