7

我正在尝试编写一个程序来显示 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 秒的范围内。这里发生了什么?

另外,我怎么知道字节是有符号的还是无符号的?

非常感谢!

4

6 回答 6

20

感谢您的帮助!我得到了它的工作,我会在这里发布解决方案供大家使用,以防其他可怜的灵魂需要它:

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
于 2010-02-09T06:18:05.923 回答
10

“双通道”表示立体声,因此将每个通道的持续时间相加是没有意义的——因此您偏离了两倍(2.18 秒,而不是 4.37)。至于签名,例如解释here,我引用:

8 位样本存储为无符号字节,范围从 0 到 255。16 位样本存储为 2 的补码有符号整数,范围从 -32768 到 32767。

这是 WAV 格式规范的一部分(实际上是其超集 RIFF),因此不依赖于您用于处理 WAV 文件的库。

于 2010-02-09T05:15:36.373 回答
4

我知道答案已经被接受了,但是我不久前用音频做了一些事情,你必须解开 wave 做这样的事情。

pcmdata = wave.struct.unpack("%dh"%(wavedatalength),wavedata)

此外,我使用的一个包称为 PyAudio,尽管我仍然必须使用 wave 包。

于 2010-02-09T05:52:49.890 回答
2

每个样本为 16 位,有 2 个通道,因此帧占用 4 个字节

于 2010-02-09T05:17:05.000 回答
2

持续时间只是帧数除以每秒帧数。根据您的数据,这是:96333 / 44100 = 2.18 seconds

于 2010-02-09T05:21:32.013 回答
2

此答案的基础上,您可以通过使用numpy.fromstringnumpy.fromfile获得良好的性能提升。另请参阅此答案

这是我所做的:

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 跳过标题。我还没试过这个。

于 2015-07-25T11:16:16.747 回答