Интерполяция стерео в моноволну в питоне

Я пытаюсь открыть стереопоток и преобразовать его в моно, используя модуль wave в питоне. До сих пор мне удавалось записать один (левый или правый) канал из 16-битного стереофайла с прямым порядком байтов:

LEFT, RIGHT = 0, 1
def mono_single(cont, chan=LEFT):
    a = iter(cont)
    mono_cont = ''
    if chan:
        a.next(); a.next()
    while True:
        try:                          
            mono_cont += a.next() + a.next()
            a.next(); a.next()
        except StopIteration:
            return mono_cont

stereo = wave.open('stereofile.wav', 'rb')
mono = wave.open('monofile.wav', 'wb')
mono.setparams(stereo.getparams())
mono.setnchannels(1)
mono.writeframes(mono_single(stereo.readframes(stereo.getnframes())))
mono.close()

Это работает, как и ожидалось. Проблема возникает, когда я пытаюсь микшировать два стереоканала в один моноканал. Я думал, что простого среднего между левым и правым было бы достаточно, и вот что я пробовал до сих пор:

def mono_mix(cont):
    a = iter(cont)
    mono_cont = ''
    while True:
        try:
            left = ord(a.next()) + (ord(a.next()) << 8)
            right = ord(a.next()) + (ord(a.next()) << 8)
            value = (left + right) / 2
            mono_cont += chr(value & 255) + chr(value >> 8)
        except StopIteration:
            return mono_cont

stereo = wave.open('stereofile.wav', 'rb')
mono = wave.open('monofile.wav', 'wb')
mono.setparams(stereo.getparams())
mono.setnchannels(1)
mono.writeframes(mono_mix(stereo.readframes(stereo.getnframes())))
mono.close()

То, что я получаю от этого, является "потрескавшейся" версией источника. Я пробовал разные комбинации (возможно, я неправильно понял всю эту вещь), но пока безуспешно.


person musicamante    schedule 27.03.2017    source источник


Ответы (2)


Готовы ли вы использовать внешние библиотеки?
Если да, вы можете легко сделать это с помощью pydub очень несколько строк следующим образом,

from pydub import AudioSegment
mysound = AudioSegment.from_wav("/input_path/infile.wav")
mysound = mysound.set_channels(1)
mysound.export("/output_path/outfile.wav", format="wav")
person Anil_M    schedule 27.03.2017
comment
Спасибо, я не знал, что pydub может это сделать. На самом деле, похоже, что он использует модуль audioop, о котором я не знал, поэтому я думаю, что просто проверю его код и напишу свою собственную функцию (я бы предпочел избегать использования pydub, так как я он нужен только для этого случая). - person musicamante; 28.03.2017

Оказывается, я не знал о встроенном модуле audioop (спасибо Anil_M ответ. Кроме того, я ошибся как при преобразовании стереоформата , так и при написании (я должен был использовать структуру) .

Это делает все абсолютно проще:

stereo = wave.open('stereofile.wav', 'rb')
mono = wave.open('monofile.wav', 'wb')
mono.setparams(stereo.getparams())
mono.setnchannels(1)
mono.writeframes(audioop.tomono(stereo.readframes(float('inf')), stereo.getsampwidth(), 1, 1))
mono.close()

Затем вы можете выбрать один канал, изменив последние 2 параметра (1, 0 для левого, 0, 1 для правого) или даже используя 1.414 для равной мощности вместо равной амплитуды.

person musicamante    schedule 27.03.2017
comment
Гений. Большое спасибо. Можно ли здесь использовать временный файл? Мне нужно преобразовать стерео в моно, так как Google STT API не принимает стерео. Я бы предпочел не писать на диск. Использование wave.open, похоже, требует реальных файлов на диске. - person Lee Melbourne; 26.11.2020