将3字节立体声WAV文件转换为numpy数组。

5
我被赋予了一个大的连续水下录音的WAV文件,我想将其转换为numpy数组进行分析。但我无法做到这一点。
到目前为止,我已经:
import numpy as np
import scipy as sp
import wave as wv
import struct

wavefile = wv.open(filename,'r')
(nchannels,sampwidth,framerate,nframes,comptype,compname) = wavefile.getparams()

// read a sample as example

wavedata =wavefile.readframes(1)

第一帧如下:'\xcd\xbc\xff@\x01\x00'。我试着使用 struct 解包,但无论我做什么都会出现以下错误:"str size does not match format"。我猜这与 Python 的 struct 无法处理 24 位数据有关。
波形文件的参数如下:
nchannels=2 sampwidth=3 framerate=48000 nframes=283516532L comptype='NONE' compname='not compressed'
有人知道如何将 24 位立体声 WAV 文件读入 numpy 数组吗?

我认为scipy将24位wav文件读入一个32位整数数组中,但它不会写入24位的wav文件。你可以一次读取字节,并将它们转换为24位的值,类似于w24 = (w8_3 << 16) | (w8_2 << 8) | w8_1这样的操作。 - Jaime
3个回答

6

以下是一个循环,用于处理任意数量通道的2、3和4字节WAV文件:

def dataFromWave(fname):
""" return list with interleaved samples """
    f = wave.open(fname, 'rb')
    chans = f.getnchannels()
    samps = f.getnframes()
    sampwidth = f.getsampwidth()
    if  sampwidth == 3: #have to read this one sample at a time
        s = ''
        for k in xrange(samps):
            fr = f.readframes(1)
            for c in xrange(0,3*chans,3):                
                s += '\0'+fr[c:(c+3)] # put TRAILING 0 to make 32-bit (file is little-endian)
    else:
        s = f.readframes(samps)
    f.close()
    unpstr = '<{0}{1}'.format(samps*chans, {1:'b',2:'h',3:'i',4:'i',8:'q'}[sampwidth])
    x = list(struct.unpack(unpstr, s))
    if sampwidth == 3:
        x = [k >> 8 for k in x] #downshift to get +/- 2^24 with sign extension
    return x

谢谢。只是出于好奇,什么需要2.6? - mtrw
我认为字符串格式是在v2.6中引入的。它不适用于我被迫使用的v2.4.3版本。 - Cnoobplusplus

2

针对类似问题,我分享我的解决方案。注意,这将把一个24位的wav文件转换为带符号的浮点数numpy数组。如果只需要转换为整数,请省略/int2float部分。

frames = wavfile.readframes(nsamples)

ch1 = np.zeros(nsamples)
ch2 = np.zeros(nsamples)
int2float = (2**23)-1

for x in np.arange(int(nsamples)):
    ch1_24bit_sample = frames[x*6:x*6+3]
    ch2_24bit_sample = frames[x*6+3:x*6+6]
    ch1_32bit_sample = bit24_2_32(ch1_24bit_sample)
    ch2_32bit_sample = bit24_2_32(ch2_24bit_sample)
    ch1[x]=struct.unpack('i',ch_32bit_sample)[0]
    ch2[x]=struct.unpack('i',ch_32bit_sample)[0]
    ch1[x]=ch1[x]/int2float
    ch2[x]=ch2[x]/int2float

def bit24_2_32(strbytes):
    if strbytes[2] < '\x80':
       return strbytes+'\x00'
    else:
       return strbytes+'\xff'

1

虽然这是一个老问题,但如果有人需要更多的选项并且没有使用外部模块的限制,那么可能可以使用 librosa

myNdArray = librosa.core.load(wav_path, sr=sample_rate)[0]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接