声音生成项目(3)——VariantAutoencoder(VAE)中使用生成好的模型进行声音生成

概述

  • 这部分主要是在已经训练好的模型的基础上,利用模型生成声音,主要是两个py文件,分别是soundgenerator.py和generate.py,
    • 前一个文件是声明一个SoundGenerator类,实现如下功能
      • 将频谱图中的db转为振幅
      • 将频谱图转为波形图
    • 后一个是具体调用的已经训练好的模型和SoundGenerator类,进行声音生成的文件。
  • 下面是针对各个模块进行的说明,并展示最后的执行效果。

一、soundgenerator.py文件

soundgenerator.py实现代码

import librosa

from preprocess import MinMaxNormaliser


class SoundGenerator:
    """ Soundgenerator负责从频谱图生成对应的声音 """

    def __init__(self, vae, hop_length):
        self.vae = vae
        self.hop_length = hop_length
        self._min_max_normaliser = MinMaxNormaliser(0, 1)

    def generate(self, spectrograms, min_max_values):
        generated_spectrograms, latent_representations = \
            self.vae.reconstruct(spectrograms)
        signals = self.convert_spectrograms_to_audio(generated_spectrograms, min_max_values)
        return signals, latent_representations

    def convert_spectrograms_to_audio(self, spectrograms, min_max_values):
        signals = []
        for spectrogram, min_max_value in zip(spectrograms, min_max_values):
            # reshape the log spectrogram
            log_spectrogram = spectrogram[:, :, 0]
            # apply denormalisation
            denorm_log_spec = self._min_max_normaliser.denormalise(
                log_spectrogram, min_max_value["min"], min_max_value["max"])
            # log spectrogram -> spectrogram
            spec = librosa.db_to_amplitude(denorm_log_spec)
            # apply Griffin-Lim
            signal = librosa.istft(spec, hop_length=self.hop_length)
            # append signal to "signals"
            signals.append(signal)
        return signals

一、convert_spectrogram_to_audio方法

def convert_spectrograms_to_audio(self, spectrograms, min_max_values):
        signals = []
        for spectrogram, min_max_value in zip(spectrograms, min_max_values):
            log_spectrogram = spectrogram[:, :, 0]	# 对频谱图进行调整
            denorm_log_spec = self._min_max_normaliser.denormalise(
                log_spectrogram, min_max_value["min"], min_max_value["max"])	# 对数据进行反正则化,进行恢复
            spec = librosa.db_to_amplitude(denorm_log_spec)	# 将分贝图转成振幅
            signal = librosa.istft(spec, hop_length=self.hop_length)	# 使用反短傅立叶变化,将数据恢复到频域
            signals.append(signal)	# 将数据加载signal中,并进行返回
        return signals
  • 这部分是负责将频谱图转成音频文件,基本流程图下
    • 对生成的数据,进行逆正则化,还原成原来的范围下的数据
    • 将分贝频域图,转成振幅的数据模式librosa.db_to_amplitude
    • 将时频图还原成,线性的,时间域的波形图
librosa.db_to_amplitude

参考连接

  • 功能描述:将dB尺度下的频谱图转变成振幅频谱图,使用分贝来衡量振幅
  • 原理:下面的公式知道就行了,不用记着
    • d b t o a m p l i t u d e ( S d b )   = 10.0 ∗ ∗ ( 0.5 ∗ S d b / 10 + l o g 10 ( r e f ) ) db_to_amplitude(S_db) ~= 10.0**(0.5 * S_db/10 + log10(ref)) dbtoamplitude(Sdb) =10.0(0.5Sdb/10+log10(ref))
  • 输入:
    • 有分贝构成的数组
  • 输出
    • 是一个np.ndarray
    • 线性振幅频谱图
librosa.istft
  • 功能描述:
    • 短时傅立叶变换的逆函数,将时频领域的频谱图转成时间序列的频域图
  • 输入
    • stft_matrix:需要转换的频谱图数组
    • hop_length:窗口移动的步长
  • 输出
    • ynp.ndarray
    • 有原来的stft_matrix重建之后的,时间序列的信号

generate方法

def generate(self, spectrograms, min_max_values):
    generated_spectrograms, latent_representations = \
        self.vae.reconstruct(spectrograms)
    signals = self.convert_spectrograms_to_audio(generated_spectrograms, min_max_values)
    return signals, latent_representations
  • 这是生成函数的总函数,具体流程如下
    • 调用vae模型的重建方法,根据潜在特征模型,生成新的频谱图
    • 然后调用上一节将的函数,将频谱图转成波形,返回波形图和对应的特征表示

二、generate.py文件

实现代码

  • 调用函数如下
    • load_fsdd:加载频谱文件
    • select_spectrograms:选择频谱文件
    • save_signals:保存生成的文件
    • main主要的执行函数
import os
import pickle

import numpy as np
import soundfile as sf

from soundgenerator import SoundGenerator
from vae import VAE
from train_vae import SPECTROGRAMS_PATH


HOP_LENGTH = 256
SAVE_DIR_ORIGINAL = "samples/original/"
SAVE_DIR_GENERATED = "samples/generated/"
MIN_MAX_VALUES_PATH = "/home/valerio/datasets/fsdd/min_max_values.pkl"


def load_fsdd(spectrograms_path):
    x_train = []
    file_paths = []
    for root, _, file_names in os.walk(spectrograms_path):
        for file_name in file_names:
            file_path = os.path.join(root, file_name)
            spectrogram = np.load(file_path) # (n_bins, n_frames, 1)
            """  这部分不是很懂,需要了解一下,确定具体的一些要求  """
            x_train.append(spectrogram)
            file_paths.append(file_path)
    x_train = np.array(x_train)
    x_train = x_train[..., np.newaxis] # -> (3000, 256, 64, 1)
    return x_train, file_paths


def select_spectrograms(spectrograms,
                        file_paths,
                        min_max_values,
                        num_spectrograms=2):
    sampled_indexes = np.random.choice(range(len(spectrograms)), num_spectrograms)
    sampled_spectrogrmas = spectrograms[sampled_indexes]
    file_paths = [file_paths[index] for index in sampled_indexes]
    sampled_min_max_values = [min_max_values[file_path] for file_path in
                           file_paths]
    print(file_paths)
    print(sampled_min_max_values)
    return sampled_spectrogrmas, sampled_min_max_values


def save_signals(signals, save_dir, sample_rate=22050):
    for i, signal in enumerate(signals):
        save_path = os.path.join(save_dir, str(i) + ".wav")
        sf.write(save_path, signal, sample_rate)


if __name__ == "__main__":
    # initialise sound generator
    vae = VAE.load("model")
    sound_generator = SoundGenerator(vae, HOP_LENGTH)

    # load spectrograms + min max values
    with open(MIN_MAX_VALUES_PATH, "rb") as f:
        min_max_values = pickle.load(f)

    specs, file_paths = load_fsdd(SPECTROGRAMS_PATH)

    # sample spectrograms + min max values
    sampled_specs, sampled_min_max_values = select_spectrograms(specs,
                                                                file_paths,
                                                                min_max_values,
                                                                5)

    # generate audio for sampled spectrograms
    signals, _ = sound_generator.generate(sampled_specs,
                                          sampled_min_max_values)

    # convert spectrogram samples to audio
    original_signals = sound_generator.convert_spectrograms_to_audio(
        sampled_specs, sampled_min_max_values)

    # save audio signals
    save_signals(signals, SAVE_DIR_GENERATED)
    save_signals(original_signals, SAVE_DIR_ORIGINAL)

load_fsdd函数说明

def load_fsdd(spectrograms_path):
    x_train = []
    file_paths = []
    for root, _, file_names in os.walk(spectrograms_path):
        for file_name in file_names:
            file_path = os.path.join(root, file_name)
            spectrogram = np.load(file_path) # (n_bins, n_frames, 1)
            x_train.append(spectrogram)
            file_paths.append(file_path)
    x_train = np.array(x_train)
    x_train = x_train[..., np.newaxis] # -> (3000, 256, 64, 1)
    return x_train, file_paths
  • 这部分是用来加载频谱图的,spectrograms_path保存的是频谱文件的地址,并将单个数据合并成数据集。
  • 因为处理之后的频谱图是二维的,[n_bins,n_frames],但是模型是需要输入三维的数据,所以增加一个最后的维度,为1,充当channel数据
  • 同时获取对应频谱文件的保存路径。

select_spectrogram函数说明

def select_spectrograms(spectrograms,
                        file_paths,
                        min_max_values,
                        num_spectrograms=2):
    """
    在加载的频谱文件数据spectrograms中,随机抽取num_specctrograms个频谱,
    并返回对应的min_max_values,以便进行逆正则化
    :param spectrograms:处理过之后的频谱文件
    :param file_paths:每一个频谱图的对应的文件路径,用于获取对应的最大最小值
    :param min_max_values:每一个频谱文件对应的最值
    :param num_spectrograms:需要提取的频谱图数量
    :return:
    """
    sampled_indexes = np.random.choice(range(len(spectrograms)), num_spectrograms)  # 随机采样
    sampled_spectrogrmas = spectrograms[sampled_indexes]    # 获取抽样之后的数据图
    file_paths = [file_paths[index] for index in sampled_indexes]   # 获取对应索引的文件路径
    sampled_min_max_values = [min_max_values[file_path] for file_path in    # 获取对应频谱图的最大最小值索引
                           file_paths]
    print(file_paths)
    print(sampled_min_max_values)
    return sampled_spectrogrmas, sampled_min_max_values # 返回采样之后频谱图以及对应的最值
  • 在频谱图中随机抽样特定数量的样本数据,用于生成模型,并返回对应样本的最值,以便进行逆正则化

save_signals函数说明

import soundfile as sf
def save_signals(signals, save_dir, sample_rate=22050):
    """
    将波形信号保存为对应wav文件
    :param signals:生成的波形信号
    :param save_dir:保存的路径
    :param sample_rate:采样率
    :return:
    """
    for i, signal in enumerate(signals):
        save_path = os.path.join(save_dir, str(i) + ".wav")
        sf.write(save_path, signal, sample_rate)
  • 这部分是将数据保存为对应的波形图,用到了soundfile包,这个python自带的包,用于保存对应的数据

main函数说明

"""
主要步骤
1、初始化一个sound generator实例
2、加载对应文件下的频谱图文件和最值文件
3、从频谱图和最值文件进行对应采样
4、生成与采样样例针对的音频
5、将生成的频谱图转成波形图
6、保存对应的音频信号
"""
# 初始化对应
vae = VAE.load("model")
sound_generator = SoundGenerator(vae, HOP_LENGTH)

# load spectrograms + min max values
with open(MIN_MAX_VALUES_PATH, "rb") as f:
    min_max_values = pickle.load(f)

specs, file_paths = load_fsdd(SPECTROGRAMS_PATH)

# sample spectrograms + min max values
sampled_specs, sampled_min_max_values = select_spectrograms(specs,
                                                            file_paths,
                                                            min_max_values,
                                                            5)

# generate audio for sampled spectrograms
# 这里不仅仅生成了对应的信号,还返回了对应的特征空间,所以需要提前保存一下
signals, _ = sound_generator.generate(sampled_specs,
                                      sampled_min_max_values)

# convert spectrogram samples to audio
# 注意这里是原来的频谱图也转成了对应的声音,主要是和生成的声音进行对比
original_signals = sound_generator.convert_spectrograms_to_audio(
    sampled_specs, sampled_min_max_values)

# save audio signals
save_signals(signals, SAVE_DIR_GENERATED)
save_signals(original_signals, SAVE_DIR_ORIGINAL)
  • 这个main函数串联起了soundgenerator.py文件和generate.py文件,包含了完整的生成函数的流程

在这里插入图片描述

三、运行异常

File Not Found

  • 代码中,需要在当前的工程项目中创建两个文件

KeyError: ‘/root/PycharmProjects/VAEGenerate/Mycode/fsdd/spectrogram/6_nicolas_15.wav.npy’

在这里插入图片描述
在这里插入图片描述

  • 这个字典是有问题的,说明是preprocess的问题,所以重新回过去修改代码。preprocess pipeline中save_feature中并没有返回对应的路径。

在这里插入图片描述
在这里插入图片描述

  • 运行成功,生成对应的数据

在这里插入图片描述

总结

  • 这部分已经根据给的声音成功生成了相关的声音,如果只讲这个,估计时间不够,所以我还需要将这部分的代码应用到对应的自动编码器中,然后对比一下对应的生成效果,同时,还需要看一下相关的理论知识,尝试使用不同的特征进行生成。
  • 我在怀疑自己,这样写有什么意义?本来就不是那么复杂的东西,不过我自己太差了,还是得好好看吧。那我花那么久的时间,去实现这个,真的有意义吗?自己的研究方向又是什么那?哎,现在就是我想做,那就做。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值