Прерывистый звук из-за разделения и последующего объединения стереоканалов .wav

В настоящее время я работаю над обработкой файлов .wav с помощью python, используя Pyaudio для потоковой передачи звука и библиотеку волн python для загрузки данных файла. Позже я планирую включить обработку отдельных стереоканалов в отношении амплитуды сигнала и панорамирования стереосигнала, но сейчас я просто пытаюсь разделить два канала волнового файла и сшить их обратно. вместе - надеюсь, в итоге получим данные, идентичные входным данным.

Ниже мой код. Метод getRawSample отлично работает, и я могу передавать аудио через эту функцию. Проблема в моем методе getSample. Где-то вдоль линии, где я разделяю два канала звука и соединяю их вместе, звук искажается. Я даже закомментировал ту часть, где я выполняю настройку амплитуды и панорамирования, так что теоретически это данные на входе -> данные на выходе.
Ниже приведен пример моего кода:

class Sample(threading.Thread) :

def __init__(self, filepath, chunk):
    super(Sample, self).__init__()
    self.CHUNK = chunk
    self.filepath = filepath
    self.wave = wave.open(self.filepath, 'rb')
    self.amp = 0.5 # varies from 0 to 1
    self.pan = 0 # varies from -pi to pi
    self.WIDTH = self.wave.getsampwidth()
    self.CHANNELS  = self.wave.getnchannels()
    self.RATE = self.wave.getframerate()
    self.MAXFRAMEFEEDS = self.wave.getnframes()/self.CHUNK  # maximum even number of chunks
    self.unpstr = '<{0}h'.format(self.CHUNK*self.WIDTH)  # format for unpacking the sample byte string
    self.pckstr = '<{0}h'.format(self.CHUNK*self.WIDTH)  # format for unpacking the sample byte string

    self.framePos = 0  # keeps track of how many chunks of data fed

#  panning and amplitude adjustment of input sample data

def panAmp(self, data, panVal, ampVal):  # when panning, using constant power panning
    [left, right] = self.getChannels(data)
    #left = np.multiply(0.5, left) #(np.sqrt(2)/2)*(np.cos(panVal) + np.sin(panVal))
    #right = np.multiply(0.5, right)  # (np.sqrt(2)/2)*(np.cos(panVal) - np.sin(panVal))
    outputList = self.combineChannels(left, right)
    dataResult = struct.pack(self.pckstr, *outputList)
    return dataResult

def getChannels(self, data):
    dataPrepare = list(struct.unpack(self.unpstr, data))
    left = dataPrepare[0::self.CHANNELS]
    right = dataPrepare[1::self.CHANNELS]
    return [left, right]

def combineChannels(self, left, right):
    stereoData = left
    for i in range(0, self.CHUNK/self.WIDTH):
        index = i*2+1
        stereoData = np.insert(stereoData, index, right[i*self.WIDTH:(i+1)*self.WIDTH])
    return stereoData

def getSample(self, panVal, ampVal):
    data = self.wave.readframes(self.CHUNK)
    self.framePos += 1
    if self.framePos > self.MAXFRAMEFEEDS:  # if no more audio samples to process
        self.wave.rewind()
        data = self.wave.readframes(self.CHUNK)
        self.framePos = 1
    return self.panAmp(data, panVal, ampVal)

def getRawSample(self):  # for debugging, bypasses pan and amp functions
    data = self.wave.readframes(self.CHUNK)
    self.framePos += 1
    if self.framePos > self.MAXFRAMEFEEDS:  # if no more audio samples to process
        self.wave.rewind()
        data = self.wave.readframes(self.CHUNK)
        self.framePos = 1
    return data

Я подозреваю, что ошибка в том, как я сшиваю левый и правый канал, но не уверен. Я загружаю проект 16-битными файлами .wav 44100 кГц. Ниже приведена ссылка на аудиофайл, чтобы вы могли прослушать полученный аудиовыход. Первая часть запускает два файла (оба двухканальные) с помощью метода getSample, а следующая часть запускает те же файлы с помощью метода getRawSample.

https://dl.dropboxusercontent.com/u/24215404/pythonaudiosample.wav

Судя по звуку, как было сказано ранее, кажется, что стереофайл искажается. Глядя на форму сигнала вышеприведенного файла, кажется, что правый и левый каналы абсолютно одинаковы после прохождения метода getSample.

При необходимости я также могу опубликовать свой код, включая основную функцию. Надеюсь, мой вопрос не слишком расплывчатый, но я благодарен за любую помощь или вклад!


person Morten B.    schedule 14.08.2016    source источник


Ответы (1)


Как это часто бывает, я заснул и проснулся на следующий день с решением.
Проблема была в функции CombineChannels. Ниже приведен рабочий код:

   def combineChannels(self, left, right):
    stereoData = left
    for i in range(0, self.CHUNK):
        index = i*2+1
        stereoData = np.insert(stereoData, index, right[i:(i+1)])
    return stereoData

Изменения

  • Для границ цикла: поскольку у меня есть 1024 элемента (столько же, сколько размер моего фрагмента) в списках left и right, мне, конечно, нужно перебрать каждый из них.
  • index: определение индекса остается прежним
  • стереодата: Опять же, здесь я помню, что я работаю со списками, каждый из которых содержит кадр аудио. Код в вопросе предполагает, что мой список хранится в виде строки байтов, но это, конечно, не так. И как видите, получившийся код намного проще.
person Morten B.    schedule 15.08.2016