Проблема с использованием Python Struct Unpack в последнее время

В сценарии Python я записываю звук с помощью следующей команды:

import subprocess
import wave
self.rec_args =['arecord', '--device=pulse', '-f', 'cd', '-t', '/home/USER/audioFile.wav')]
self.rec = subprocess.Popen(self.rec_args, shell=False)

Затем я открываю файл:

self.wave_file = wave.open('/home/USER/audioFile.wav', 'rb')

и получить количество каналов (2) и частоту дискретизации (44100) с:

self.num_channels, self.sample_rate = self.getWaveParameters(self.wave_file)

Затем я могу использовать код для getWaveIntegers, чтобы получить амплитуды звука с помощью:

wi = self.getWaveIntegers(self.wave_file, self.sample_rate, self.num_channels, 0, 20)

где clipStart = 0, а затем длина образца для анализа = 20 секунд.

########################################
    def getWaveIntegers(self, stream, sample_rate, num_channels, clipStart, timeLength):
# https://stackoverflow.com/questions/2226853/
# stream = the already opened wave file to navigate through
# clipStart = the starting position of the clip in the wave file
# timeLength = the length of time of the clip to take from the wave file
# set start of clip
        startPosition = sample_rate * clipStart # converts clipStart time to samplewidth
        stream.setpos(startPosition) # set the starting position in the wave file
# length of clip
        clipLength = sample_rate*timeLength*num_channels # timeLength in terms of channels and sample rate ####?
        clipData = stream.readframes(sample_rate*timeLength) # the clip of the wave file starting at startPosition for time in sample_rate
        integer_data = wave.struct.unpack("%dh"%(clipLength), clipData)
        channels = [ [] for time in range(num_channels) ]
        for index, value in enumerate(integer_data):
            bucket = index % num_channels
            channels[bucket].append(value)
        del clipData
# keep only left? channel
        sampleChannel = []
        for c in range(0, len(channels[0]):
           sampleChannel.append(abs(int(channels[0][c]/100)))
        return sampleChannel
########################################
    def getWaveParameters(self, stream):
# https://stackoverflow.com/questions/2226853/
        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
        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.")
        return num_channels, sample_rate#, sample_width, num_frames, total_samples, fmt # do not need these values for this code
########################################

Это работало правильно до недавнего времени. Теперь я получаю следующую ошибку:

Traceback (most recent call last):
  File "myfile.py", line 167, in getWaveIntegers
    integer_data = wave.struct.unpack("%dh"%(clipLength), clipData)
struct.error: unpack requires a buffer of 3528000 bytes

Длина клипа некоторых файлов кажется: 1764000 байт (ровно половина 3528000 байт в ошибке)

За последние пару дней я заметил обновление различных библиотек Python до следующей версии: Python 3.6:amd64 (3.6.8-1~18.04.3, 3.6.9-1~18.04)

Я попробовал пример, представленный на https://docs.python.org/3.6/library/struct.html

>>> from struct import *
>>> pack('hhl', 1, 2, 3)
b'\x00\x01\x00\x02\x00\x00\x00\x03'
>>> unpack('hhl', b'\x00\x01\x00\x02\x00\x00\x00\x03')
(1, 2, 3)

Когда я делаю это в своей системе (Python 3.6.9 (по умолчанию, 7 ноября 2019 г., 10:44:02) и [GCC 8.3.0] в Linux):

>>> from struct import *
>>> pack('hhl', 1, 2, 3)
b'\x01\x00\x02\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00'

длина в два раза больше длины в примере. С участием:

>>> unpack('hhl', b'\x00\x01\x00\x02\x00\x00\x00\x03')

Я получаю сообщение об ошибке:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
struct.error: unpack requires a buffer of 16 bytes

но, когда я делаю:

>>> unpack('hhl', b'\x01\x00\x02\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00')
(1, 2, 3)

Я получаю правильный ответ.

Я сделал что-то странное или это ошибка в обновлении Python. Честно говоря, я не совсем понимаю команду struct. Большое спасибо, что прочитали пост от болтуна вроде меня! Джон


person John    schedule 27.11.2019    source источник


Ответы (1)


Я не мог понять проблему с struct unpack, как описано выше, поэтому я запрограммировал ее и использовал numpy. Таким образом, код для getWaveIntegers должен быть таким:

########################################
import numpy
def getWaveIntegers(self, stream, sample_rate, num_channels, clipStart, timeLength):
# https://stackoverflow.com/questions/2226853/
# stream = the already opened wave file to navigate through
# clipStart = the starting position of the clip in the wave file
# timeLength = the length of time of the clip to take from the wave file
# set start of clip
    startPosition = sample_rate * clipStart # converts clipStart time to samplewidth
    stream.setpos(startPosition) # set the starting position in the wave file
# length of clip
    clipLength = sample_rate*timeLength*num_channels # timeLength in terms of channels and sample rate ####?
    clipData = stream.readframes(sample_rate*timeLength) # the clip of the wave file starting at startPosition for time in sample_rate
    signal = numpy.fromstring(clipData, "Int16")
# https://stackoverflow.com/questions/22636499/
    result = numpy.fromstring(clipData, dtype=numpy.int16)
    chunk_length = int(len(result) / num_channels)
    assert chunk_length == int(chunk_length)
    result = numpy.reshape(result, (chunk_length, num_channels))
# keep only left channel and only 10 samples per second
    leftChannel = []
    for c in range(0, len(result[:, 0]), 50):
       leftChannel.append(abs(int(result[:, 0][c]/100))) # the absolute value of the integer of the frequency divided by 100 (for diagramatic purposes)
    return leftChannel
########################################

Я надеюсь, что это поможет кому-то в будущем.

person John    schedule 27.11.2019