【项目实战】AUTOVC 代码解析 —— make_spect.py

AUTOVC 代码解析 —— make_spect.py

  简介

       本项目一个基于 AUTOVC 模型的语音转换项目,它是使用 PyTorch 实现的(项目地址)。
       
        AUTOVC 遵循自动编码器框架,只对自动编码器损耗进行训练,但它引入了精心调整的降维和时间下采样来约束信息流,这个简单的方案带来了显著的性能提高。(详情请参阅 AUTOVC 的详细介绍)。
       
       由于 AUTOVC 项目较大,代码较多。为了方便学习与整理,将按照工程文件的结构依次介绍。
       
       本文将介绍项目中的 make_spect.py 文件:读取音频文件,并转换为 Mel 频谱保存。
       

  函数解析

      butter_highpass

          该函数的作用是: 创建一个高通滤波器,得到滤波器系数:分子(’ b ‘)和分母(’ a ')多项式

          输入参数:

		cutoff	:	滤波器截止频率
		fs		:	样本采样率
		order	:	滤波器阶数

          输出参数:

		b, a	:	IIR 滤波器的分子(' b ')和分母(' a ')多项式

          代码详解:

		def butter_highpass(cutoff, fs, order=5):
		    # 取一半的 fs 标量,fs 是采样率, nyq 与尼奎斯特定理有关
		    nyq = 0.5 * fs
		    # cutoff 是截止频率,需要标准化(我也不懂,想问问神奇海螺)
		    normal_cutoff = cutoff / nyq
		    
		    # 设计一个 n 阶数字或模拟巴特沃斯滤波器并返回滤波器系数,其中:
		    #  order 是 int 型整数,代表设计的滤波器的阶数;
		    #  normal_cutoff 为临界频率,这里作为高通滤波器的标量,对于数字滤波器, normal_cutoff 与 fs (采样率)的单位相同。
		    # 默认情况下, fs 是 2 个半周期/样本,所以这些从 0 到 1 标准化,其中 1 是尼奎斯特频率。(因此, normal_cutoff 是半周期/样本。)(我也没看懂,等待大佬相助);
		    #  btype 为可选过滤器的类型,在这里默认为 'high' ,选择高通滤波器;
		    #  analog 为滤波器返回标志:当为 True 时,返回一个模拟滤波器,否则返回一个数字滤波器。这里代表设计的是一个数字滤波器;
		    # 综上所述,这里创建了一个 order 阶临界频率为 normal_cutoff 的高通滤波器,并返回了 IIR 滤波器的分子(' b ')和分母(' a ')多项式。
		    b, a = signal.butter(order, normal_cutoff, btype='high', analog=False)
		    
		    # 返回IIR 滤波器的分子(' b ')和分母(' a ')多项式
		    return b, a

       

      pySTFT

          该函数的作用是: 实现给定样本片段的短时傅里叶变换

          输入参数:

		x			:	音频样本
		fft_length	:	傅里叶变换的长度
		hop_length	:	跳跃的长度

          输出参数:

		np.abs(result)	:	傅里叶变换的结果

          代码详解:

		def pySTFT(x, fft_length=1024, hop_length=256):
		    
		    # 填补样本,前后长度为 fft_length//2 ,以反射的方式填充
		    x = np.pad(x, int(fft_length//2), mode='reflect')
		    
		    # 重叠部分的长度
		    noverlap = fft_length - hop_length
		    # 计算样本 x 分割后的形状;
		    # 每隔 hop_length 在样本 x 上取 fft_length 个数据,共有 (x.shape[-1]-noverlap)//hop_length 个这样的数据组;
		    # 至于为什么前面要加上 x.shape[:-1] (计算结果为'()'),是为了保留原有的维度,仅对最后一个维度做更改
		    shape = x.shape[:-1]+((x.shape[-1]-noverlap)//hop_length, fft_length)
		    # 计算每次取上述形状的数据需要跳过的字节长度;
		    #  x.strides 是 x 数组中元素间的距离,单位为字节,与元素类型有关。运行中元素类型为 float64 ,x.strides 为 8 
		    # 在外部,每隔 hop_length 个元素跳跃一次;在内部,每隔 1 个元素跳跃一次
		    strides = x.strides[:-1]+(hop_length*x.strides[-1], x.strides[-1])
		    # 组装上述样本、形状与距离参数,执行样本数据的分割,获得 result ,形状为 shape 
		    result = np.lib.stride_tricks.as_strided(x, shape=shape,
		                                             strides=strides)
		    
		    # 创建一个长度为 fft_length 的 hann 周期窗口,准备与' ifftshift '一起使用,并乘以FFT的结果
		    fft_window = get_window('hann', fft_length, fftbins=True)
		    # 计算实际输入的一维离散傅里叶变换,并转置数组;
		    # 这个函数通过一种称为快速傅立叶变换(FFT)的高效算法计算实值数组的一维 n 点离散傅立叶变换(DFT)
		    result = np.fft.rfft(fft_window * result, n=fft_length).T
		    
		    # 将傅里叶变换的结果取绝对值并返回
		    return np.abs(result) 

       

      main

          该函数的作用是: 计算音频文件目录下音频文件的 Mel 频谱,并保存在对应的频谱目录

          输入参数:

          输出参数:

          代码详解:

		# 创建一个 Mel 过滤器组
		# 这将产生一个线性变换矩阵来将 FFT 箱投射到梅尔频率箱上
		mel_basis = mel(16000, 1024, fmin=90, fmax=7600, n_mels=80).T
		# 设计一个最小等级的标量
		min_level = np.exp(-100 / 20 * np.log(10))
		# 调用 butter_highpass 创建一个高通滤波器,得到滤波器系数:分子(' b ')和分母(' a ')多项式
		b, a = butter_highpass(30, 16000, order=5)
		
		# 音频文件目录
		rootDir = './wavs'
		# 频谱图目录
		targetDir = './spmel'
		
		# 获取音频文件目录及该目录下的子目录列表
		dirName, subdirList, _ = next(os.walk(rootDir))
		# 打印音频文件目录名
		print('Found directory: %s' % dirName)
		
		# 按顺序遍历音频文件子目录
		for subdir in sorted(subdirList):
		    # 打印当前目录
		    print(subdir)
		    # 若目标频谱图子目录不存在,则创建对应目录
		    if not os.path.exists(os.path.join(targetDir, subdir)):
		        os.makedirs(os.path.join(targetDir, subdir))
		    
		    # 访问当前音频文件子目录,获取该目录下的所有文件名
		    _,_, fileList = next(os.walk(os.path.join(dirName,subdir)))
		    # 同一个音频文件子目录下,设定生成的随机数序列相同
		    prng = RandomState(int(subdir[1:])) 
		    # 顺序遍历所有的文件名
		    for fileName in sorted(fileList):
		        # 以 NumPy 数组的形式从声音文件中提供音频数据
		        x, fs = sf.read(os.path.join(dirName,subdir,fileName))
		        # 信号滤波,消除漂移噪声
		        y = signal.filtfilt(b, a, x)
		        # 为了模型的鲁棒性,添加一点随机噪声
		        wav = y * 0.96 + (prng.rand(y.shape[0])-0.5)*1e-06
		        # 调用 pySTFT 进行短时傅里叶变化后转置数组,计算频谱
		        D = pySTFT(wav).T
		        
		        # 转换为 mel 并标准化
		        D_mel = np.dot(D, mel_basis)
		        D_db = 20 * np.log10(np.maximum(min_level, D_mel)) - 16
		        S = np.clip((D_db + 100) / 100, 0, 1)
		        
		        # 在指定目录下保存 mel 频谱
		        np.save(os.path.join(targetDir, subdir, fileName[:-4]),
		                S.astype(np.float32), allow_pickle=False)    
		        

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值