随着音频技术的发展,人们对高质量音频的需求越来越强烈,而多声道音频可以满足人们的这种需求。本节介绍基于PyTorch的音频建模技术。

9.3.1  加载音频数据源

加载音频数据,需要安装PyTorch的torchaudio库和soundfile库,在torchaudio中加载文件时,可以选择指定后端以通过torchaudio.set_audio_backend使用sox_io或SoundFile,其中Windows系统中使用SoundFile,Linux/macOS系统中使用sox_io。

导入相关库,代码如下:

# 导入相关库

import torch

import torchaudio

import soundfile

import matplotlib.pyplot as plt

torchaudio.set_audio_backend("soundfile")

torchaudio支持以WAV和MP3格式加载声音文件。我们称波形为原始音频信号。代码如下:

# 定义文件名

filename = "恭喜发财.mp3"

# 加载音频文件并获取波形和采样率

waveform, sample_rate = torchaudio.load(filename)

# 打印波形的形状

print("波形形状:{}".format(waveform.size()))

# 打印波形的采样率

print("波形采样率:{}".format(sample_rate))

# 创建一个图形

plt.figure()

# 绘制波形的时间序列

plt.plot(waveform.t().numpy())

# 显示图形

plt.show()

上述代码的主要目的是加载一个音频文件,并将其波形显示出来。

首先,定义了一个文件名filename,它指定了要加载的音频文件。然后,使用torchaudio.load函数加载音频文件,并获取波形数据waveform和采样率sample_rate。

其次,使用print函数打印出波形的形状和采样率。waveform.size表示波形数据的形状,即波形的维度。

然后,使用plt.figure创建一个图形窗口。使用plt.plot函数绘制波形的时间序列,这里使用waveform.t().numpy()来获取波形的时间序列数据。

最后,使用plt.show显示绘制的波形图形。这样,就可以直观地看到音频文件的波形。

输出的原始音频信号的参数如下:

波形形状:torch.Size([2, 8935836])

波形采样率:44100

输出的原始音频信号如图9-2所示。

图9-2  原始音频信号

9.3.2  波形变换的类型

目前,torchaudio库支持的波形转换类型如下。

  1. 重采样:将波形重采样为其他采样率。
  2. 频谱图:从波形创建频谱图。
  3. GriffinLim:使用Griffin-Lim转换从线性比例幅度谱图计算波形。
  4. ComputeDeltas:计算张量(通常是声谱图)的增量系数。
  5. ComplexNorm:计算复数张量的范数。
  6. MelScale:使用转换矩阵将正常STFT转换为Mel频率STFT。
  7. AmplitudeToDB:将频谱图从功率/振幅标度变为分贝标度。
  8. MFCC:根据波形创建梅尔频率倒谱系数。
  9. MelSpectrogram:使用PyTorch中的STFT功能从波形创建MEL频谱图。
  10. MuLawEncoding:基于mu-law压扩对波形进行编码。
  11. MuLawDecoding:解码mu-law编码的波形。
  12. TimeStretch:在不更改给定速率的音高的情况下,及时拉伸频谱图。
  13. FrequencyMasking:在频域中屏蔽频谱图应用。
  14. TimeMasking:在时域中屏蔽频谱图应用。

由于所有变换都是nn.Modules或jit.ScriptModules,它们可以用作神经网络的一部分。

9.3.3  绘制波形频谱图

首先,以对数刻度查看频谱图的对数,代码如下:

# 使用 Spectrogram 函数对波形数据进行处理,得到频谱图数据

specgram = torchaudio.transforms.Spectrogram()(waveform)

# 打印频谱图的形状,即频谱图的尺寸

print("频谱图形状:{}".format(specgram.size()))

# 创建一个新的图形窗口

plt.figure()

# 显示频谱图的对数变换结果。specgram.log2()[0,:,:].numpy()表示取频谱图的对数变换结果,并将其转换为 NumPy 数组;cmap='gray' 用于指定颜色映射为灰度色;aspect="auto" 表示自动调整图像的纵横比

plt.imshow(specgram.log2()[0,:,:].numpy(), cmap='gray', aspect="auto")

#显示绘制的图形窗口

plt.show()

上述代码首先使用torchaudio库中的Spectrogram函数将波形数据转换为频谱图。然后打印出频谱图的形状,并使用Matplotlib库绘制并显示频谱图。通过观察频谱图,可以了解信号在不同频率上的能量分布情况。

运行上述代码,输出如图9-3所示。

以对数刻度查看梅尔频谱图,代码如下:

# 使用 torchaudio.transforms.MelSpectrogram()函数对波形进行梅尔频谱图变换

specgram = torchaudio.transforms.MelSpectrogram()(waveform)

# 打印梅尔频谱图的形状

print("梅尔频谱图形状:{}".format(specgram.size()))

# 创建一个新的图形

plt.figure()

# 显示梅尔频谱图的对数变换结果

p = plt.imshow(specgram.log2()[0,:,:].detach().numpy(), cmap='viridis', aspect="auto")

# 显示图形

plt.show()

这段代码的目的是将波形数据转换为梅尔频谱图,并将其可视化显示出来,以便观察信号在梅尔频率尺度上的能量分布情况。与之前的代码类似,但是使用了MelSpectrogram函数而不是Spectrogram函数来生成梅尔频谱图。梅尔频谱图是一种特殊的频谱表示,它基于梅尔频率尺度,常用于语音处理等领域。

运行上述代码,输出如图9-4所示。

 

                  图9-3  以对数刻度查看频谱图                        图9-4  以对数刻度查看梅尔频谱图

我们可以重新采样波形,一次一个通道,代码如下:

# 计算新的采样率,将原始采样率除以15

new_sample_rate = sample_rate / 15

#选择要处理的通道,这里设置为 0

channel = 0

#使用 Resample 函数对波形进行重新采样。将原始采样率和新的采样率作为参数传递给函数,并将波形数据的指定通道转换为一维张量

transformed = torchaudio.transforms.Resample(sample_rate, new_sample_rate)(waveform[channel, :].view(1, -1))

# 打印变换后波形的形状,即尺寸信息

print("变换后波形形状:{}".format(transformed.size()))

# 创建一个新的图形窗口

plt.figure()

#绘制变换后的波形。transformed[0, :] 表示取变换后波形的第一个样本,并将其转换为 NumPy 数组进行绘图

plt.plot(transformed[0, :].numpy())

#显示绘制的图形

plt.show()

通过上述代码可以观察到重采样后波形的形状和变化。重采样操作常用于改变波形的采样率,以适应不同的需求或处理。

运行上述代码,输出如图9-5所示。

图9-5  重新采样波形

9.3.4  波形Mu-Law编码

下面介绍音频处理时的Mu-Law与反Mu-Law变换。可以基于Mu-Law编码对信号进行编码,但是要做到这一点,需要信号在-1和1之间。由于张量只是一个常规的PyTorch张量,因此我们可以在其上应用标准运算符,代码如下:

print("波形最小值:{}\n波形最大值:{}\n波形平均值:{}".format(waveform.min(), waveform.max(),waveform.mean()))

输出如下:

波形最小值:-1.0179462432861328

波形最大值:0.9967185854911804

波形平均值:-1.855347363743931e-05

由于波形不在-1和1之间,因此我们不需要对其进行归一化,代码如下:

# 定义一个名为 normalize 的函数,接受一个张量作为参数

def normalize(tensor):

    """

    对输入的张量进行归一化处理

    参数:

    tensor (Tensor):需要进行归一化的张量

    返回:

    normalized_tensor (Tensor):归一化后的张量

    """

    tensor_minusmean = tensor - tensor.mean()  # 从张量中减去其均值

    # 将减去均值后的张量除以其绝对值的最大值

    return tensor_minusmean / tensor_minusmean.abs().max()

# waveform 进行归一化处理,并将结果存储在 waveform_

waveform_ = normalize(waveform)

应用编码波形,代码如下:

#使用torchaudio库中的MuLawEncoding变换函数对waveform_进行处理

transformed = torchaudio.transforms.MuLawEncoding()(waveform_)

#打印变换后波形的形状,即尺寸信息

print("变换后波形形状: {}".format(transformed.size()))

#创建一个新的图形对象

plt.figure()

#使用 plot 函数绘制变换后波形的曲线,这里取了第一个样本

plt.plot(transformed[0,:].numpy())

#显示绘制的图形

plt.show()

通过上述代码可以观察到经过Mu-Law编码变换后波形的形状和特征。绘制波形图可以帮助用户直观地理解变换后的波形特征。运行上述代码,输出如图9-6所示。

图9-6  波形Mu-Law编码

现在解码,代码如下:

#使用torchaudio库中的MuLawDecoding解码函数对变换后的波形 transformed进行解码操作

reconstructed = torchaudio.transforms.MuLawDecoding()(transformed)

#打印解码后新波形的形状,即尺寸信息

print("新波形形状: {}".format(reconstructed.size()))

#创建一个新的图形对象

plt.figure()

#使用 plot 函数绘制解码后的新波形曲线,这里取了第一个样本

plt.plot(reconstructed[0,:].numpy())

#显示绘制的图形

plt.show()

通过这段代码可以观察到解码后新波形的形状和特征。Mu-Law编码是Mu-Law编码的逆操作,用于将编码后的波形还原为原始波形的近似。绘制波形图可以帮助用户直观地理解解码后的波形  特征。

运行上述代码,输出如图9-7所示。

图9-7  波形Mu-Law解码

9.3.5  变换前后波形的比较

为了分析波形变换前后是否存在较大差异,可以将原始波形与归一化和Mu-Law变换后的波形进行比较,代码如下:

# 计算原始波形和重构波形之间的差异

err = ((waveform - reconstructed).abs() / waveform.abs()).mean()

# 打印原始信号和重构信号之间的差异,保留两位小数

print("原始信号和重构信号之间的差异: {:.2%}".format(err))

上述代码用于评估原始波形和重构波形之间的相似度或差异程度,通过计算差异的均值并以百分比形式显示,可以更直观地了解两者之间的差异情况。

首先,通过abs()函数取两个波形的绝对值,然后相减得到差异值。

其次,将差异值除以原始波形的绝对值,得到一个相对差异的度量。

然后,对这个相对差异值进行均值计算,得到平均差异。

最后,使用格式化字符串打印平均差异,:.2%表示将差异值显示为带有两位小数的百分比形式。

运行上述代码,输出如下:

原始信号和重构信号之间的差异: 41.18%

可以看出,经过归一化和Mu-Law变换后的波形与原始波形存在较大的差异,平均差异达到了41.18%。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐