Python Wave 字节数据

Posted

技术标签:

【中文标题】Python Wave 字节数据【英文标题】:Python Wave byte data 【发布时间】:2013-12-19 09:08:35 【问题描述】:

我正在尝试从 .wav 文件中读取数据。

import wave
wr = wave.open("~/01 Road.wav", 'r')
# sample width is 2 bytes
# number of channels is 2
wave_data = wr.readframes(1)
print(wave_data)

这给出了:

b'\x00\x00\x00\x00'

这是歌曲的“第一帧”。这4个字节显然对应每帧的(2个通道*2字节样本宽度)字节,但是每个字节对应的是什么?

特别是,我正在尝试将其转换为单振幅信号。

【问题讨论】:

【参考方案1】:

如果您想了解“帧”是什么,您必须阅读波形文件格式的标准。例如:https://web.archive.org/web/20140221054954/http://home.roadrunner.com/~jgglatt/tech/wave.htm

来自该文档:

旨在“播放”的采样点,即同时发送到数模转换器(DAC) 的采样点统称为采样帧。在我们的立体波形示例中,每两个采样点组成另一个采样帧。这在下面的立体声示例中进行了说明。

sample       sample              sample
frame 0      frame 1             frame N
 _____ _____ _____ _____         _____ _____
| ch1 | ch2 | ch1 | ch2 | . . . | ch1 | ch2 |
|_____|_____|_____|_____|       |_____|_____|
 _____
|     | = one sample point
|_____|

要转换为单声道,您可以这样做,

import wave

def stereo_to_mono(hex1, hex2):
    """average two hex string samples"""
    return hex((ord(hex1) + ord(hex2))/2)

wr = wave.open('piano2.wav','r')

nchannels, sampwidth, framerate, nframes, comptype, compname =  wr.getparams()

ww = wave.open('piano_mono.wav','wb')
ww.setparams((1,sampwidth,framerate,nframes,comptype,compname))

frames = wr.readframes(wr.getnframes()-1)

new_frames = ''

for (s1, s2) in zip(frames[0::2],frames[1::2]):
    new_frames += stereo_to_mono(s1,s2)[2:].zfill(2).decode('hex')

ww.writeframes(new_frames)

从立体声到单声道没有明确的方法。您可以只删除一个频道。上面,我正在平均频道。这完全取决于您的应用程序。

【讨论】:

谢谢,这个链接内容丰富,阅读起来很有趣。【参考方案2】:

对于 wav 文件 IO,我更喜欢使用 scipy。读取 wav 文件可能有点矫枉过正,但通常在读取 wav 后进行下游处理会更容易。

import scipy.io.wavfile
fs1, y1 = scipy.io.wavfile.read(filename)

从这里开始,数据 y1 将有 N 个样本长,并且将有 Z 列,其中每列对应一个通道。要转换为单声道 wav 文件,您不会说您希望如何进行转换。你可以取平均值,或者任何你想要的。一般使用

monoChannel = y1.mean(axis=1)

【讨论】:

【参考方案3】:

作为对您问题的直接回答:两个字节以“通常”的方式构成一个 16 位整数值,由显式公式给出:value = ord(data[0]) + 256 * ord(data[1])。但是使用struct 模块是解码(以及稍后重新编码)此类多字节整数的更好方法:

import struct
print(struct.unpack("HH", b"\x00\x00\x00\x00"))
# -> gives a 2-tuple of integers, here (0, 0)

或者,如果我们想要一个 有符号 16 位整数(我认为是 .wav 文件中的情况),请使用 "hh" 而不是 "HH"。 (我留给你的任务是弄清楚两个字节如何准确地编码一个从 -32768 到 32767 的整数值:-)

【讨论】:

struct 模块在这种情况下非常有用。比我的答案中的十六进制/排序/解码混乱好多了。我不知道它存在。 我相信字节是小端的,这要求你使用语法struct.unpack("<hh", b"\x00\x00\x00\x00")(根据this link)。另外,根据同一个链接,16位样本宽度表示有符号数据,8位样本宽度表示无符号数据,这与您所说的一致。【参考方案4】:

另一种将 2 个字节转换为 int16 的方法,使用 numpy.fromstring()。这是一个例子: audio_sample 来自一个 wav 文件。

>>> audio_sample[0:8]
b'\x8b\xff\xe1\xff\x92\xffn\xff'

>>> x = np.fromstring(audio_sample, np.int16) 

>>> x[0:4]
array([-117,  -31, -110, -146], dtype=int16)

您可以使用 np.tobytes 转换回字节

【讨论】:

以上是关于Python Wave 字节数据的主要内容,如果未能解决你的问题,请参考以下文章

iOS开发——WAVE音频文件解析

从波形文件python读取字节

Python 3:将波形数据(字节数组)转换为浮点值的 numpy 数组

Wave文件编码:保存文件

Python 3波模块字节序..?

将字节写入波形文件?