在 python 中使用 scipy 和 librosa 读取 wav 文件

Posted

技术标签:

【中文标题】在 python 中使用 scipy 和 librosa 读取 wav 文件【英文标题】:Reading a wav file with scipy and librosa in python 【发布时间】:2019-02-01 15:21:55 【问题描述】:

我正在尝试使用 scipy 文件夹在 Python 中加载 .wav 文件。我的最终目标是创建该音频文件的频谱图。读取文件的代码可以总结如下:

import scipy.io.wavfile as wav
(sig, rate) = wav.read(_wav_file_)

对于某些 .wav 文件,我收到以下错误:

WavFileWarning:无法理解块(非数据),跳过它。 WavFileWarning) ** ValueError: 不完整的 wav 块。

因此,我决定使用 librosa 来读取文件:

import librosa
(sig, rate) = librosa.load(_wav_file_, sr=None)

这在所有情况下都可以正常工作,但是,我注意到频谱图的颜色有所不同。然而,虽然它是相同的确切数字,但颜色却以某种方式颠倒了。更具体地说,我注意到当保持相同的函数计算规格并仅更改我阅读.wav 的方式时,存在这种差异。知道什么可以产生这种东西吗?两种方法读取.wav 文件的方式是否存在默认差异?

编辑:

(rate1, sig1) = wav.read(spec_file) # rate1 = 16000
sig, rate = librosa.load(spec_file) # rate 22050
sig = np.array(α*sig, dtype = "int16") 

几乎可行的方法是将 sig 的结果与常数 α alpha 相乘,这是来自 scipy wavread 的信号的最大值与来自 librosa 的信号之间的比例。虽然信号速率不同。

【问题讨论】:

【参考方案1】:

这听起来像是一个量化问题。如果波形文件中的样本存储为float,而librosa 只是执行直接转换为int,小于1 的值将被截断为0。很可能这就是sig 是一个数组的原因全零。 float 必须缩放以将其映射到 int 的范围内。例如,

>>> a = sp.randn(10)
>>> a
array([-0.04250369,  0.244113  ,  0.64479281, -0.3665814 , -0.2836227 ,
       -0.27808428, -0.07668698, -1.3104602 ,  0.95253315, -0.56778205])

将 a 转换为类型 int 而不进行缩放

>>> a.astype(int)
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])

将 a 转换为 int 并缩放为 16 位整数

>>> b = (a* 32767).astype(int)
>>> b
array([ -1392,   7998,  21127, -12011,  -9293,  -9111,  -2512, -42939,
        31211, -18604])

将缩放的int 转换回float

>>> c = b/32767.0
>>> c
array([-0.04248177,  0.24408704,  0.64476455, -0.36655782, -0.28360851,
       -0.27805414, -0.0766625 , -1.31043428,  0.9525132 , -0.56776635])

cb 由于量化为int,仅等于大约 3 或 4 位小数。

如果 librosa 返回 float,您可以将其缩放为 2**15 并将其转换为 int 以获得与 scipy wave reader 返回的相同范围的值。由于 librosa 返回一个float,因此这些值可能会在一个比[-32768, +32767] 中的16 位整数更小的范围内,例如[-1, +1]。所以你需要缩放一个来获得匹配的范围。例如,

sig, rate = librosa.load(spec_file, mono=True)
sig = sig × 32767

【讨论】:

但为了进行缩放,我需要从每个数据集中找到最小值和最大值。对吗?这对我来说是不可能的。 比例因子很可能是恒定的。如果不是,则波形文件的体积将随着读取的每个数据块而变化。 数据的来源是什么以及用于创建 wav 文件的内容是什么? 很遗憾,我无法访问此信息。 wav 文件是数据库的一部分,可在此处找到:zenodo.org/record/1188976#.XFmYoVxKi73 是的,我试过了,但有点武断。我只是缩放,但在 20-30 wav 文件中找到了两种情况下的最大值和最小值。【参考方案2】:

如果您自己不想进行量化,那么您可以使用pylab 使用pylab.specgram 函数来为您完成。您可以查看函数内部,看看它如何使用vminvmax

您的帖子(至少对我而言)并不完全清楚您想要实现的目标(因为您事先既没有示例输入文件也没有任何脚本)。但无论如何,为了检查波形文件的频谱图是否有显着差异,具体取决于从任何读取函数返回的信号数据是float32int的情况,我测试了以下3个函数。

Python 脚本:

_wav_file_ = "africa-toto.wav"

def spectogram_librosa(_wav_file_):
    import librosa
    import pylab
    import numpy as np
    
    (sig, rate) = librosa.load(_wav_file_, sr=None, mono=True,  dtype=np.float32)
    pylab.specgram(sig, Fs=rate)
    pylab.savefig('spectrogram3.png')

def graph_spectrogram_wave(wav_file):
    import wave
    import pylab
    def get_wav_info(wav_file):
        wav = wave.open(wav_file, 'r')
        frames = wav.readframes(-1)
        sound_info = pylab.fromstring(frames, 'int16')
        frame_rate = wav.getframerate()
        wav.close()
        return sound_info, frame_rate
    sound_info, frame_rate = get_wav_info(wav_file)
    pylab.figure(num=3, figsize=(10, 6))
    pylab.title('spectrogram pylab with wav_file')
    pylab.specgram(sound_info, Fs=frame_rate)
    pylab.savefig('spectrogram2.png')


def graph_wavfileread(_wav_file_):
    import matplotlib.pyplot as plt
    from scipy import signal
    from scipy.io import wavfile
    import numpy as np   
    sample_rate, samples = wavfile.read(_wav_file_)   
    frequencies, times, spectrogram = signal.spectrogram(samples,sample_rate,nfft=1024)
    plt.pcolormesh(times, frequencies, 10*np.log10(spectrogram))
    plt.ylabel('Frequency [Hz]')
    plt.xlabel('Time [sec]')
    plt.savefig("spectogram1.png")
    

spectogram_librosa(_wav_file_)
#graph_wavfileread(_wav_file_)
#graph_spectrogram_wave(_wav_file_)
产生了以下 3 个输出:

除了大小和强度的微小差异之外,无论是读取方法、库还是数据类型,这似乎都非常相似,这让我有点疑问,出于什么目的需要输出“完全相同”以及应该如何精确他们是。

虽然librosa.load() 函数提供了dtype 参数,但我确实觉得很奇怪,但无论如何只能使用float 值。在这方面谷歌搜索导致我只有这个 issue 这没有太大帮助,这个 issue 说这就是它与 librosa 保持一致的方式,因为在内部它似乎只使用浮点数。

【讨论】:

我想读取音频,然后按照以下示例计算频谱图:haythamfayek.com/2016/04/21/…。我注意到 librosa 和 scipy waveread 的结果颜色有所不同。【参考方案3】:

补充一下,Librosa 有一个实用程序可以将整数数组转换为浮点数。

float_audio = librosa.util.buf_to_float(sig)

在制作 Pydub 音频片段的频谱图时,我使用它取得了巨大的成功。请记住,它的参数之一是每个样本的字节数。默认为 2。您可以在 documentation here 中了解更多信息。这是source code:

def buf_to_float(x, n_bytes=2, dtype=np.float32):
    """Convert an integer buffer to floating point values.
    This is primarily useful when loading integer-valued wav data
    into numpy arrays.
    See Also
    --------
    buf_to_float
    Parameters
    ----------
    x : np.ndarray [dtype=int]
        The integer-valued data buffer
    n_bytes : int [1, 2, 4]
        The number of bytes per sample in `x`
    dtype : numeric type
        The target output type (default: 32-bit float)
    Returns
    -------
    x_float : np.ndarray [dtype=float]
        The input data buffer cast to floating point
    """

    # Invert the scale of the data
    scale = 1./float(1 << ((8 * n_bytes) - 1))

    # Construct the format string
    fmt = '<i:d'.format(n_bytes)

    # Rescale and format the data buffer
    return scale * np.frombuffer(x, fmt).astype(dtype)

【讨论】:

以上是关于在 python 中使用 scipy 和 librosa 读取 wav 文件的主要内容,如果未能解决你的问题,请参考以下文章

使用 scipy 在 python 中构建和更新稀疏矩阵

使用空间和时间变量在 python(scipy) 中进行聚类

结合scipy.linalg在Python中使用线性系统

使用 scipy.signal 在 Python 中进行卷积和反卷积

使用 pyUno 将文本写入 Libre/OpenOffice Writer

Python地理数据处理环境的搭建和准备(numpy,scipy,matplotlib)