Reading *.wav files in Python

I need to analyze sound written in a .wav file. For that I need to transform this file into set of numbers (arrays, for example). I think I need to use the wave package. However, I do not know how exactly it works. For example I did the following:

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

As a result of this code I expected to see sound pressure as function of time. In contrast I see a lot of strange, mysterious symbols (which are not hexadecimal numbers). Can anybody, pleas, help me with that?

300312 次浏览

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

每个 文件返回一个由两个项组成的元组: 第一个是每秒样本中的 抽样率,第二个是包含从文件中读取的所有数据的 numpy数组:

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

使用 struct模块,可以获取波帧(在 -32768和32767之间的 2's complementary二进制帧(即 0x80000x7FFF))。这读取一个 MONO,16位,WAVE 文件。我发现 本网页在阐述这个问题时非常有用:

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]))

This snippet reads 1 frame. To read more than one frame (e.g., 13), use

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

您可以使用 尖端科学实验室模块来完成这项工作,它需要 NumPy 和 SciPy 以及 libsndfile 才能运行。

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

from scikits.audiolab import wavread


filename = "testfile.wav"


data, sample_frequency,encoding = wavread(filename)

现在你有波形数据了

if its just two files and the sample rate is significantly high, you could just interleave them.

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)

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

一个简单的处理例子是:

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大小。

读取 wav 的不同 Python 模块:

There is at least these following libraries to read wave audio files:

最简单的例子:

这是 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

我需要读取一个1通道24位 WAV 文件。Nak上面的帖子非常有用。然而,正如上面所提到的 basj24位并不简单。我最终使用下面的代码片段使它工作起来:

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之间的结果,则需要一些额外的伸缩。也许你们有人会觉得这个有用

恕我直言,将声音文件中的音频数据导入 NumPy 数组的最简单方法是 声音文件:

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

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

有许多声音文件库可用,我已经写了 概述,您可以看到一些利弊。 It also features a page explaining 如何读取与 wave模块的24位 wav 文件.

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

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/

附言。这个例子是关于读取 wav 文件的,但是 PyDub 可以处理很多开箱即用的格式。需要注意的是,它基于本地 Python wav 支持和 ffmpeg,因此必须安装 ffmpeg,并且许多 pydub 功能依赖于 ffmpeg 版本。通常,如果 ffmpeg 可以做到这一点,那么 pydub 也可以(这非常强大)。

非免责声明: 我与该项目没有关系,但我是一个重度用户。

下面是一个 Python 3解决方案,它使用了内置的 wave 模块[1] ,适用于 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)

注意,我已经尝试使它可读而不是快速。我发现一次读取所有数据的速度几乎快了两倍。例如。

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 声音文件的数量级比 CPython 快2倍(但是很难达到这个速度)。

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

亲爱的,据我所知,你正在进入一个叫做数字信号处理(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)验证此语句。如果您真的想更多地了解这些奇怪的字符,请单击 给你

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

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

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

在这两种选择中,从文件中获得的样本都在 线性脉冲编码调制(LPCM)中表示


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

我将把这部分留给你:)但是 这是一本好书带你通过 DSP。不幸的是,我不知道 Python 的好书,它们通常都是很糟糕的书... ... 但是不要担心,这个理论可以用任何编程语言以同样的方式应用,只要你领域的语言。

不管你读什么书,坚持读经典作家的作品,比如普罗阿基斯、奥本海姆等等... ... 不要在意他们使用的语言编程。有关使用 Python 的 DPS 音频的更实用指南,请参阅 本页。

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是样本总数。