我正在尝试编写一个显示PCM数据的程序。我一直非常沮丧地试图找到一个具有正确抽象级别的库,但我找到了python wave库并且一直在使用它。但是,我不确定如何解释数据。
wave.getparams函数返回(2个通道,2个字节,44100 Hz,96333帧,无压缩,无压缩)。这一切看起来都很愉快,但后来我尝试打印一个帧:'\ xc0 \ xff \ xd0 \ xff'这是4个字节。我想一个帧可能是2个样本,但模糊不会在那里结束。
96333帧* 2个样本/帧*(1 / 44.1k秒/样本)= 4.3688秒
然而,iTunes报告时间接近2秒,基于文件大小和比特率的计算在2.7秒的范围内。这是怎么回事?
此外,我如何知道字节是有符号还是无符号?
非常感谢!
答案 0 :(得分:18)
感谢您的帮助!我得到了它的工作,我会在这里发布解决方案供所有人使用,以防其他一些可怜的灵魂需要它:
import wave
import struct
def pcm_channels(wave_file):
"""Given a file-like object or file path representing a wave file,
decompose it into its constituent PCM data streams.
Input: A file like object or file path
Output: A list of lists of integers representing the PCM coded data stream channels
and the sample rate of the channels (mixed rate channels not supported)
"""
stream = wave.open(wave_file,"rb")
num_channels = stream.getnchannels()
sample_rate = stream.getframerate()
sample_width = stream.getsampwidth()
num_frames = stream.getnframes()
raw_data = stream.readframes( num_frames ) # Returns byte data
stream.close()
total_samples = num_frames * num_channels
if sample_width == 1:
fmt = "%iB" % total_samples # read unsigned chars
elif sample_width == 2:
fmt = "%ih" % total_samples # read signed 2 byte shorts
else:
raise ValueError("Only supports 8 and 16 bit audio formats.")
integer_data = struct.unpack(fmt, raw_data)
del raw_data # Keep memory tidy (who knows how big it might be)
channels = [ [] for time in range(num_channels) ]
for index, value in enumerate(integer_data):
bucket = index % num_channels
channels[bucket].append(value)
return channels, sample_rate
答案 1 :(得分:9)
“双声道”意味着立体声,因此总和每个声道的持续时间是没有意义的 - 所以你关闭了两倍(2.18秒,而不是4.37)。至于签名,如例如here所解释,我引用:
8位样本存储为无符号 字节,范围从0到255. 16位 样本存储为2的补码 有符号整数,范围从-32768 至32767。
这是WAV格式规范的一部分(实际上是其超集RIFF),因此不依赖于您用来处理WAV文件的库。
答案 2 :(得分:4)
我知道答案已经被接受了,但是我前段时间做了一些关于音频的事情,你必须解开波浪做这样的事情。
pcmdata = wave.struct.unpack("%dh"%(wavedatalength),wavedata)
另外,我使用的一个包叫做PyAudio,虽然我仍然需要使用wave包。
答案 3 :(得分:2)
每个样本为16位且有2个通道,因此帧需要4个字节
答案 4 :(得分:2)
持续时间只是帧数除以每秒帧数。根据您的数据,这是:96333 / 44100 = 2.18 seconds
。
答案 5 :(得分:2)
在this answer的基础上,使用numpy.fromstring或numpy.fromfile可以提升效果。另请参阅this answer。
这是我做的:
def interpret_wav(raw_bytes, n_frames, n_channels, sample_width, interleaved = True):
if sample_width == 1:
dtype = np.uint8 # unsigned char
elif sample_width == 2:
dtype = np.int16 # signed 2-byte short
else:
raise ValueError("Only supports 8 and 16 bit audio formats.")
channels = np.fromstring(raw_bytes, dtype=dtype)
if interleaved:
# channels are interleaved, i.e. sample N of channel M follows sample N of channel M-1 in raw data
channels.shape = (n_frames, n_channels)
channels = channels.T
else:
# channels are not interleaved. All samples from channel M occur before all samples from channel M-1
channels.shape = (n_channels, n_frames)
return channels
如果需要将数据复制到内存中,则为shape赋予新值将引发错误。这是一件好事,因为你想要使用数据(使用更少的时间和总体内存)。如果可能,ndarray.T函数也不会复制(即返回视图),但我不确定你确保它不会复制的方式。
使用np.fromfile直接从文件中读取会更好,但您必须使用自定义dtype跳过标题。我还没试过这个。