Преобразование 3-байтового стерео WAV-файла в массив numpy

Мне дали большой WAV-файл непрерывной подводной записи, который я хотел бы преобразовать в массив для анализа. Я изо всех сил пытаюсь это сделать.

Пока у меня есть:

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'. Я попытался распаковать его с помощью структуры, но все, что я делаю, я получаю следующую ошибку: «размер str не соответствует формату». Я предполагаю, что это связано с тем, что структура Python не может обрабатывать 24-битные данные.

Параметр wave-файла выглядит следующим образом:

  • nchannels = 2
  • sampwidth = 3
  • частота кадров = 48000
  • nframes = 283516532L
  • comptype = 'НЕТ'
  • compname = 'не сжатый'

Кто-нибудь знает, как читать 24-битный стерео WAV-файл в массив numpy?


person Cnoobplusplus    schedule 31.10.2013    source источник
comment
Я думаю, scipy считывает 24-битные файлы WAV в массив 32-битных целых чисел, но не записывает 24-битные WAV-файлы. Вы всегда можете читать байты по одному, и они конвертируют их в 24-битные значения, делая что-то вроде w24 = (w8_3 << 16) | (w8_2 << 8) | w8_1   -  person Jaime    schedule 31.10.2013


Ответы (3)


Вот цикл, который обрабатывает 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
person mtrw    schedule 15.11.2013
comment
Спасибо. Просто из любопытства, что требует 2.6? - person mtrw; 15.11.2013
comment
Я думаю, строковый формат появился в версии 2.6. Это не работает с версией 2.4.3, которую я вынужден использовать. - person Cnoobplusplus; 18.11.2013

Для тех, у кого похожие проблемы, я публикую свое решение. Обратите внимание, что при этом 24-битный волновой файл преобразуется в массив чисел с плавающей запятой со знаком. Оставьте часть / 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'
person Cnoobplusplus    schedule 15.11.2013

Это старый вопрос, но если кому-то нужны дополнительные параметры и нет ограничений на использование внешних модулей, вы, вероятно, можете использовать librosa

myNdArray = librosa.core.load(wav_path, sr=sample_rate)[0]
person BaluRaman    schedule 03.05.2018