【问题标题】:Reading *.wav files in Python在 Python 中读取 *.wav 文件
【发布时间】:2011-01-04 20:40:11
【问题描述】:

我需要分析写在 .wav 文件中的声音。为此,我需要将此文件转换为一组数字(例如数组)。我想我需要使用wave包。但是,我不知道它是如何工作的。例如我做了以下事情:

import wave
w = wave.open('/usr/share/sounds/ekiga/voicemail.wav', 'r')
for i in range(w.getnframes()):
    frame = w.readframes(i)
    print frame

由于这段代码,我希望将声压视为时间的函数。相比之下,我看到了很多奇怪的、神秘的符号(不是十六进制数字)。有人可以帮我吗?

【问题讨论】:

    标签: python audio wav wave


    【解决方案1】:

    如果您要对波形数据执行传输,那么也许您应该使用SciPy,特别是scipy.io.wavfile

    【讨论】:

    • 好的。我刚刚安装了 SciPy,但找不到任何使用 scipy.io.wavfile 的示例。
    • 没有什么比交互式解释器更能弄清楚事情是如何工作的了!雄心勃勃!
    【解决方案2】:

    每个the documentationscipy.io.wavfile.read(somefile) 返回一个包含两个项目的元组:第一个是sampling rate,以每秒采样数为单位,第二个是一个numpy 数组,其中包含从文件中读取的所有数据:

    from scipy.io import wavfile
    samplerate, data = wavfile.read('./output/audio.wav')
    

    【讨论】:

    • 可以结合命令行转换工具打开其他格式。
    • 它严重缺乏频道数量。在不知道通道数的情况下,您应该如何处理音频?
    • 在我的计算机上抛出了一些奇怪的结构解包错误。我认为它使用的是 struct.unpack('
    • 这个库有用吗?我遇到了许多问题: scipy.io.wavfile.read('/usr/lib/python2.7/dist-packages/pygame/examples/data/house_lo.wav') -> 没有数据。 scipy.io.wavfile.read('/usr/lib/python2.7/dist-packages/pygame/examples/data/secosmic_lo.wav') -> ZeroDivisionError: 整数除法或模除以零
    • @bastibe data 是一个二维 numpy 数组,所以 data.shape 返回一个 (num_samples, num_channels) 的元组
    【解决方案3】:

    使用struct module,您可以获取波形帧(在-32768 和32767 之间的2's complementary 二进制(即0x80000x7FFF)。这将读取一个单声道、16 位、WAVE 文件. 我发现this webpage 在制定这个时非常有用:

    import wave, struct
    
    wavefile = wave.open('sine.wav', 'r')
    
    length = wavefile.getnframes()
    for i in range(0, length):
        wavedata = wavefile.readframes(1)
        data = struct.unpack("<h", wavedata)
        print(int(data[0]))
    

    这个 sn-p 读取 1 帧。要读取多于一帧(例如 13 帧),请使用

    wavedata = wavefile.readframes(13)
    data = struct.unpack("<13h", wavedata)
    

    【讨论】:

    • 如何处理24位立体声文件?
    • 这给了我错误:“struct.error: unpack requires a string argument of length 2”
    • 如果您使用非常大的音频文件运行这段代码。由于该程序需要内存,您的计算机将死机。大音频文件需要逐块处理音频文件
    • @Coder404 你可能有一个立体声文件,或者不同的位深度。
    • 对于像我一样想知道什么是 2s 互补二进制的人,请参阅此处stackoverflow.com/questions/1049722/what-is-2s-complement
    【解决方案4】:

    您可以使用scikits.audiolab 模块完成此操作。它需要 NumPy 和 SciPy 才能运行,还需要 libsndfile。

    注意,我只能让它在 Ubunutu 上运行,而不是在 OSX 上运行。

    from scikits.audiolab import wavread
    
    filename = "testfile.wav"
    
    data, sample_frequency,encoding = wavread(filename)
    

    现在你有了 wav 数据

    【讨论】:

    • scikits.audiolab 尚未更新 since 2010 可能只有 Python 2。
    【解决方案5】:

    如果它只有两个文件并且采样率非常高,您可以将它们交错。

    from scipy.io import wavfile
    rate1,dat1 = wavfile.read(File1)
    rate2,dat2 = wavfile.read(File2)
    
    if len(dat2) > len(dat1):#swap shortest
        temp = dat2
        dat2 = dat1
        dat1 = temp
    
    output = dat1
    for i in range(len(dat2)/2): output[i*2]=dat2[i*2]
    
    wavfile.write(OUTPUT,rate,dat)
    

    【讨论】:

      【解决方案6】:

      如果您想逐块处理音频,某些给定的解决方案在某种意义上是相当糟糕的,因为它们意味着将整个音频加载到内存中会产生许多缓存未命中并减慢您的程序。 python-wavefile 提供了一些 pythonic 结构,通过生成器使用高效和透明的块管理来进行 NumPy 逐块处理。其他 pythonic 的细节是文件的上下文管理器,元数据作为属性......如果你想要整个文件接口,因为你正在开发一个快速原型并且你不关心效率,整个文件接口仍然存在。

      一个简单的处理示例是:

      import sys
      from wavefile import WaveReader, WaveWriter
      
      with WaveReader(sys.argv[1]) as r :
          with WaveWriter(
                  'output.wav',
                  channels=r.channels,
                  samplerate=r.samplerate,
                  ) as w :
      
              # Just to set the metadata
              w.metadata.title = r.metadata.title + " II"
              w.metadata.artist = r.metadata.artist
      
              # This is the prodessing loop
              for data in r.read_iter(size=512) :
                  data[1] *= .8     # lower volume on the second channel
                  w.write(data)
      

      该示例重用同一个块来读取整个文件,即使在最后一个块通常小于所需大小的情况下也是如此。在这种情况下,您会得到一块块。因此,请相信返回的块长度,而不是使用硬编码的 512 大小进行任何进一步的处理。

      【讨论】:

        【解决方案7】:

        读取 wav 的不同 Python 模块:

        至少有以下这些库可以读取波形音频文件:

        最简单的例子:

        这是一个使用 SoundFile 的简单示例:

        import soundfile as sf
        data, samplerate = sf.read('existing_file.wav') 
        

        输出格式:

        警告,数据并不总是相同的格式,这取决于库。例如:

        from scikits import audiolab
        from scipy.io import wavfile
        from sys import argv
        for filepath in argv[1:]:
            x, fs, nb_bits = audiolab.wavread(filepath)
            print('Reading with scikits.audiolab.wavread:', x)
            fs, x = wavfile.read(filepath)
            print('Reading with scipy.io.wavfile.read:', x)
        

        输出:

        Reading with scikits.audiolab.wavread: [ 0.          0.          0.         ..., -0.00097656 -0.00079346 -0.00097656]
        Reading with scipy.io.wavfile.read: [  0   0   0 ..., -32 -26 -32]
        

        SoundFile 和 Audiolab 返回介于 -1 和 1 之间的浮点数(就像 matab 一样,这是音频信号的约定)。 scipy和wave返回整数,可以根据编码的位数转换成浮点数,例如:

        from scipy.io.wavfile import read as wavread
        samplerate, x = wavread(audiofilename)  # x is a numpy array of integers, representing the samples 
        # scale to -1.0 -- 1.0
        if x.dtype == 'int16':
            nb_bits = 16  # -> 16-bit wav files
        elif x.dtype == 'int32':
            nb_bits = 32  # -> 32-bit wav files
        max_nb_bit = float(2 ** (nb_bits - 1))
        samples = x / (max_nb_bit + 1)  # samples is a numpy array of floats representing the samples 
        

        【讨论】:

          【解决方案8】:

          我需要读取一个 1 通道 24 位 WAV 文件。 Nak 上面的帖子非常有用。但是,正如上面basj 所提到的,24 位并不简单。我终于用下面的 sn-p 让它工作了:

          from scipy.io import wavfile
          TheFile = 'example24bit1channelFile.wav'
          [fs, x] = wavfile.read(TheFile)
          
          # convert the loaded data into a 24bit signal
          
          nx = len(x)
          ny = nx/3*4    # four 3-byte samples are contained in three int32 words
          
          y = np.zeros((ny,), dtype=np.int32)    # initialise array
          
          # build the data left aligned in order to keep the sign bit operational.
          # result will be factor 256 too high
          
          y[0:ny:4] = ((x[0:nx:3] & 0x000000FF) << 8) | \
            ((x[0:nx:3] & 0x0000FF00) << 8) | ((x[0:nx:3] & 0x00FF0000) << 8)
          y[1:ny:4] = ((x[0:nx:3] & 0xFF000000) >> 16) | \
            ((x[1:nx:3] & 0x000000FF) << 16) | ((x[1:nx:3] & 0x0000FF00) << 16)
          y[2:ny:4] = ((x[1:nx:3] & 0x00FF0000) >> 8) | \
            ((x[1:nx:3] & 0xFF000000) >> 8) | ((x[2:nx:3] & 0x000000FF) << 24)
          y[3:ny:4] = (x[2:nx:3] & 0x0000FF00) | \
            (x[2:nx:3] & 0x00FF0000) | (x[2:nx:3] & 0xFF000000)
          
          y = y/256   # correct for building 24 bit data left aligned in 32bit words
          

          如果您需要介于 -1 和 +1 之间的结果,则需要进行一些额外的缩放。也许你们中的一些人可能会觉得这很有用

          【讨论】:

            【解决方案9】:

            恕我直言,将音频数据从声音文件获取到 NumPy 数组的最简单方法是 SoundFile

            import soundfile as sf
            data, fs = sf.read('/usr/share/sounds/ekiga/voicemail.wav')
            

            这也支持开箱即用的 24 位文件。

            有很多可用的声音文件库,我写了an overview,在那里你可以看到一些优点和缺点。 它还有一个页面解释how to read a 24-bit wav file with the wave module

            【讨论】:

            • 注意:soundfile.read() 标准化为 2^(n_bits - 1),如 Sandoval 的 scipy.io.wavfile 示例中所示
            • 但是在执行时,读取返回错误:Error opening '../audio.wav': File contains data in an unimplemented format.我要打开的文件开头为:OggS知道这里有什么问题吗?
            【解决方案10】:

            你也可以使用简单的import wavio库你还需要有一些基本的声音知识。

            【讨论】:

              【解决方案11】:

              PyDub (http://pydub.com/) 没有被提及,应该修复。 IMO 这是目前在 Python 中读取音频文件的最全面的库,尽管并非没有缺点。读取 wav 文件:

              from pydub import AudioSegment
              
              audio_file = AudioSegment.from_wav('path_to.wav')
              # or
              audio_file = AudioSegment.from_file('path_to.wav')
              
              # do whatever you want with the audio, change bitrate, export, convert, read info, etc.
              # Check out the API docs http://pydub.com/
              

              PS。该示例是关于读取 wav 文件的,但 PyDub 可以处理许多开箱即用的各种格式。需要注意的是,它基于原生 Python wav 支持和 ffmpeg,因此您必须安装 ffmpeg,并且许多 pydub 功能依赖于 ffmpeg 版本。通常如果ffmpeg可以做到,那么pydub也可以(相当强大)。

              非免责声明:我与项目无关,但我是重度用户。

              【讨论】:

                【解决方案12】:

                这是一个使用内置 wave 模块 [1] 的 Python 3 解决方案,适用于 n 个通道和 8、16、24...位。

                import sys
                import wave
                
                def read_wav(path):
                    with wave.open(path, "rb") as wav:
                        nchannels, sampwidth, framerate, nframes, _, _ = wav.getparams()
                        print(wav.getparams(), "\nBits per sample =", sampwidth * 8)
                
                        signed = sampwidth > 1  # 8 bit wavs are unsigned
                        byteorder = sys.byteorder  # wave module uses sys.byteorder for bytes
                
                        values = []  # e.g. for stereo, values[i] = [left_val, right_val]
                        for _ in range(nframes):
                            frame = wav.readframes(1)  # read next frame
                            channel_vals = []  # mono has 1 channel, stereo 2, etc.
                            for channel in range(nchannels):
                                as_bytes = frame[channel * sampwidth: (channel + 1) * sampwidth]
                                as_int = int.from_bytes(as_bytes, byteorder, signed=signed)
                                channel_vals.append(as_int)
                            values.append(channel_vals)
                
                    return values, framerate
                

                您可以将结果转换为 NumPy 数组。

                import numpy as np
                
                data, rate = read_wav(path)
                data = np.array(data)
                

                注意,我试图让它可读而不是快速。我发现一次读取所有数据的速度几乎快了 2 倍。例如

                with wave.open(path, "rb") as wav:
                    nchannels, sampwidth, framerate, nframes, _, _ = wav.getparams()
                    all_bytes = wav.readframes(-1)
                
                framewidth = sampwidth * nchannels
                frames = (all_bytes[i * framewidth: (i + 1) * framewidth]
                            for i in range(nframes))
                
                for frame in frames:
                    ...
                

                虽然python-soundfile 快了大约 2 个数量级(使用纯 CPython 很难达到这个速度)。

                [1]https://docs.python.org/3/library/wave.html

                【讨论】:

                  【解决方案13】:

                  亲爱的,据我了解,您正在寻找一个称为数字信号处理 (DSP) 的理论领域。这个工程领域来自对离散时间信号的简单分析到复杂的自适应滤波器。一个好主意是将离散时间信号视为一个向量,其中该向量的每个元素都是原始连续时间信号的采样值。获得向量形式的样本后,您可以对这个向量应用不同的数字信号技术。

                  不幸的是,在 Python 上,从音频文件迁移到 NumPy 数组向量相当麻烦,您可能会注意到...如果您不喜欢一种编程语言,我强烈建议您尝试使用 MatLab/Octave。 Matlab 使从文件中的样本访问变得简单。 audioread() 为您完成了这项任务 :) 还有很多专门为 DSP 设计的工具箱。

                  不过,如果你真的打算为此进入 Python,我会给你一步一步的指导。


                  1。获取样本

                  .wav 文件中获取样本的最简单方法是:

                  from scipy.io import wavfile
                  
                  sampling_rate, samples = wavfile.read(f'/path/to/file.wav')
                  
                  

                  或者,您可以使用wavestruct 包来获取示例:

                  import numpy as np
                  import wave, struct
                  
                  wav_file = wave.open(f'/path/to/file.wav', 'rb')
                  # from .wav file to binary data in hexadecimal
                  binary_data = wav_file.readframes(wav_file.getnframes())
                  # from binary file to samples
                  s = np.array(struct.unpack('{n}h'.format(n=wav_file.getnframes()*wav_file.getnchannels()), binary_data))
                  

                  回答你的问题:binary_data 是一个bytes 对象,它不是人类可读的,只能对机器有意义。您可以输入type(binary_data) 来验证此语句。如果你真的想了解更多关于这群奇怪的字符,请点击here

                  如果您的音频是立体声的(即有 2 个声道),您可以重塑此信号以实现与 scipy.io 获得的相同格式

                  s_like_scipy = s.reshape(-1, wav_file.getnchannels())
                  

                  每一列都是一个香奈儿。无论哪种方式,从.wav 文件中获取的样本都可用于绘制和了解信号的时间行为。

                  在这两种选择中,从文件中获取的样本都表示在 Linear Pulse Code Modulation (LPCM)


                  2。对音频样本进行数字信号处理

                  我会把这部分留给你 :) 但是this is a nice book 会带你通过 DSP。不幸的是,我不知道关于 Python 的好书,它们通常是可怕的书......但不用担心,只要你使用任何编程语言,该理论都可以以相同的方式应用。

                  无论你拿起什么书,坚持经典作者,如 Proakis、Oppenheim 等……不要在意他们使用的语言编程。有关使用 Python 进行音频 DPS 的更实用指南,请see此页面。

                  3。播放过滤后的音频样本

                  import pyaudio
                  
                  p = pyaudio.PyAudio()
                  stream = p.open(format = p.get_format_from_width(wav_file.getsampwidth()),
                                  channels = wav_file.getnchannels(),
                                  rate = wav_file.getframerate(),
                                  output = True)
                  # from samples to the new binary file
                  new_binary_data = struct.pack('{}h'.format(len(s)), *s)
                  stream.write(new_binary_data)
                  

                  其中wav_file.getsampwidth() 是每个样本的字节数,wav_file.getframerate() 是采样率。只需使用与输入音频相同的参数即可。


                  4。将结果保存在新的.wav 文件中

                  wav_file=wave.open('/phat/to/new_file.wav', 'w')
                  
                  wav_file.setparams((nchannels, sampwidth, sampling_rate, nframes, "NONE", "not compressed"))
                  
                  for sample in s:
                     wav_file.writeframes(struct.pack('h', int(sample)))
                  

                  其中nchannels 是通道数,sampwidth 是每个样本的字节数,sampling_rate 是采样率,nframes 是样本总数。

                  【讨论】:

                    猜你喜欢
                    • 2016-06-01
                    • 2022-01-23
                    • 2021-01-30
                    • 1970-01-01
                    • 2019-06-15
                    • 1970-01-01
                    • 2013-05-30
                    • 2011-04-07
                    • 2011-07-09
                    相关资源
                    最近更新 更多