【语音合成】声码器griffin-lim算法实现

姓名:成杰     学号:21021210653    学院:电子工程学院

转自:https://zhuanlan.zhihu.com/p/489694627

【嵌牛导读】

在做语音任务时,如语音合成,语者变换等,通常都是生成或修改目标信号的时频谱。 因缺少相位信息,无法直接从时频谱还原回声音频号,且由于每一帧(frame)时频谱与相邻帧的时频谱在时间上有所重叠,因此具有相互关系,对应的相位在帧与帧之间也应有相关系性,故无法直接使用任意随机的相位数值来重建信号。

然而直接从时频谱估计相位并不容易,Griffin-Lim算法使用了迭代的方式,从给定时频谱去重建相位信息,其迭代目标是让重建信号的时频谱与给定的时频谱的均方误差越小越好。

【嵌牛鼻子】

Griffin-Lim算法、声码器、语音合成

【嵌牛提问】

常见的声码器算法有哪些?griffin-lim算法的原理是什么?

【嵌牛正文】 

一、算法思想

griffin-lim重建语音信号需要使用到幅度谱和相位谱。而MEL谱当中是不含相位信息的,因此griffin-lim在重建语音波形的 时候只有MEL谱可以利用,但是通过一些运算,我们可以利用帧与帧之间的关系估计出相位信息,从而重建语音波形。

这里的MEL谱可以看做是实部,而相位信息可以看做是虚部,通过对实部和虚部的运算,得到最终的结果。

griffin-lim算法及 vocoder声码器

blog.csdn.net/CSDN_71560364126/article/details/103968034

二、算法步骤

随机初始化一个相位谱

创建一个复数矩阵,将已知的幅度谱作为实层,用噪声随机初始化虚层。(至此, 已有振幅信息, 但是没有相位信息)

2. 用这个相位谱与已知的幅度谱(来自MEL谱)经过ISTFT(逆傅里叶变换)合成新的语音波形

对复数矩阵进行ISTFT。(至此,仅依靠幅度谱得到一个初步的时域信号)

3. 用合成语音做STFT, 得到新的幅度谱和新的相位谱

对上一步得到的时域信号进行STFT,得到一个复数矩阵。(获取小部分的,不准确的相位信息)

4. 丢弃新的幅度谱,用已知幅度谱与新的相位谱合成新的语音

上一步得到的复数矩阵,是从一不准确的时域信号,得到了一个振幅与相位。用已知的振幅替换该矩阵的实部。(至此,有了已知振幅和初步准确的相位)

5. 重复步骤2~4多次,直至合成的语音达到满意的效果或者迭代次数达到设定的上限

迭代上述步骤

三、算法实现

S是欲产生信号的时频谱,n_iter是算法迭代次数,n_fft是频格(frequency bin)大小,hop_length是窗函数每次移动的长度,window是短时距傅里叶变换的窗函数类型。

def GLA(S, n_iter = 100, n_fft = 2048, hop_length = None, window = 'hann'):

hop_length = n_fft//4 if hop_length is None else hop_length

phase = np.exp(2j*np.pi*np.random.rand(*S.shape))

for i in range(n_iter):

xi = np.abs(S).astype(np.complex)*phase

signal = librosa.istft(xi, hop_length = hop_length, window = window)

next_xi = librosa.stft(signal, n_fft = n_fft, hop_length = hop_length, window = window)

phase = np.exp(1j*np.angle(next_xi))

xi = np.abs(S).astype(np.complex)*phase

signal = librosa.istft(xi, hop_length = hop_length, window = window)

return signal

四、实验仿真

从音频波形提取Mel频谱:

对音频信号预加重、分帧和加窗

对每帧信号进行短时傅立叶变换STFT,得到短时幅度谱

短时幅度谱通过Mel滤波器组得到Mel频谱

从Mel频谱重建音频波形

Mel频谱转换成幅度谱

griffin_lim声码器算法重建波形

去加重

import librosa

import numpy as np

import matplotlib.pyplot as plt

from playsound import playsound

n_fft, hop_length, win_length = 720, 160, 720

def _griffin_lim(S, gl_iters):

    angles = np.exp(2j * np.pi * np.random.rand(*S.shape))

    S_complex = np.abs(S).astype(np.complex128)

    y = _istft(S_complex * angles)

    for i in range(gl_iters):

        angles = np.exp(1j * np.angle(_stft(y)))

        y = _istft(S_complex * angles)

    return y

def _stft(y):

    return librosa.stft(y=y, n_fft=n_fft, hop_length=hop_length, win_length=win_length)

def _istft(y):

    return librosa.istft(y, hop_length=hop_length, win_length=win_length)

AudioData, sr = librosa.load('bluesky3.wav', sr=16000, mono=True)  # 使用文件采样率

D = librosa.stft(AudioData, n_fft=n_fft, hop_length=hop_length, win_length=win_length)

spect, phase = librosa.magphase(D)  # 语音信息域幅度和相位3密切关联

gl_iters = 120

ReAudio = _griffin_lim(D, gl_iters)

plt.rcParams['figure.figsize'] = (10.0, 8.0)

plt.figure()

plt.subplot(2, 1, 1)

plt.plot(AudioData)

plt.xlabel('t/s')

plt.title('original signal')

plt.subplot(2, 1, 2)

plt.plot(ReAudio)

plt.xlabel('t/s')

plt.title('reconstructed signal')

plt.show()

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容