Python: mp3 to alsaaudio через ffmpeg pipe и wave.open (f, 'r')

Я пытаюсь декодировать mp3 в wav, используя ffmpeg:

import alsaaudio import wave from subprocess import Popen, PIPE with open('filename.mp3', 'rb') as infile: p=Popen(['ffmpeg', '-i', '-', '-f', 'wav', '-'], stdin=infile, stdout=PIPE) ... 

Затем я хочу перенаправить данные из p.stdout.read () в wave.open (файл, r), чтобы использовать readframes (n) и другие методы. Но я не могу, потому что «файл» в wave.open (файл, «r») может быть только именем файла или указателем открытого файла.

  ... file = wave.open(p.stdout.read(),'r') card='default' device=alsaaudio.PCM(card=card) device.setchannels(file.getnchannels()) device.setrate(file.getframerate()) device.setformat(alsaaudio.PCM_FORMAT_S16_LE) device.setsetperiodsize(320) data = file.readframes(320) while data: device.write(data) data = file.readframes(320) Переговорные  ... file = wave.open(p.stdout.read(),'r') card='default' device=alsaaudio.PCM(card=card) device.setchannels(file.getnchannels()) device.setrate(file.getframerate()) device.setformat(alsaaudio.PCM_FORMAT_S16_LE) device.setsetperiodsize(320) data = file.readframes(320) while data: device.write(data) data = file.readframes(320) по  ... file = wave.open(p.stdout.read(),'r') card='default' device=alsaaudio.PCM(card=card) device.setchannels(file.getnchannels()) device.setrate(file.getframerate()) device.setformat(alsaaudio.PCM_FORMAT_S16_LE) device.setsetperiodsize(320) data = file.readframes(320) while data: device.write(data) data = file.readframes(320) 

Я получил:

 TypeError: file() argument 1 must be encoded string without NULL bytes, not str 

Так можно ли обрабатывать данные из p.stdout.read () методом wave.open ()? Создание временного файла .wav не является решением.

Извините за мой английский. Благодарю.

ОБНОВИТЬ

Спасибо PM 2Ring за хит io.BytesIO.

Однако полученный код не работает.

 import alsaaudio import wave from subprocess import Popen, PIPE with open('sometrack.mp3', 'rb') as infile: p=Popen(['ffmpeg', '-i', '-', '-f','wav', '-'], stdin=infile , stdout=PIPE , stderr=PIPE) fobj = io.BytesIO(p.stdout.read()) fwave = wave.open(fobj, 'rb') 

Трассировка:

 File "./script.py", line x, in <module> fwave = wave.open(fobj, 'rb') File "/usr/lib/python2.7/wave.py", line x, in open return Wave_read(f) File "/usr/lib/python2.7/wave.py", line x, in __init__ self.initfp(f) File "/usr/lib/python2.7/wave.py", line x, in initfp raise Error, 'not a WAVE file' wave.Error: not a WAVE file 

Из /usr/lib/python2.7/wave.py:

 ... self._file = Chunk(file, bigendian = 0) if self._file.getname() != 'RIFF': raise Error, 'file does not start with RIFF id' if self._file.read(4) != 'WAVE': raise Error, 'not a WAVE file' ... 

Ошибка была выполнена из-за «плохого» объекта self._file.

Внутри /usr/lib/python2.7/chunk.py я нашел источник проблемы:

 ... try: self.chunksize = struct.unpack(strflag+'L', file.read(4))[0] except struct.error: raise EOFError ... 

Потому что struct.unpack (strflag + 'L', file.read (4)) [0] возвращает 0. Но эта функция работает правильно.

Как указано здесь :

«5-8 байтов – Размер файла (целое число). Размер общего файла – 8 байтов, в байтах (32-разрядное целое). Как правило, вы должны заполнить это после создания». Вот почему мой скрипт не работает. wave.open и другие функции не могут обрабатывать мой файловый объект, потому что self.chunksize = 0. Похоже, ffmpeg не может вставить размер файла при использовании PIPE.

РЕШЕНИЕ

Это просто. Я изменил функцию init класса Chunk:

После:

 ... try: self.chunksize = struct.unpack(strflag+'L', file.read(4))[0] except struct.error: raise EOFError ... 

До:

 ... try: self.chunksize = struct.unpack(strflag+'L', file.read(4))[0] currtell = file.tell() if self.chunksize == 0: file.seek(0) file.read(currtell) self.chunksize = len(file.read())-4 file.seek(0) file.read(currtell) except struct.error: raise EOFError ... 

Конечно, редактирование оригинального модуля плохое. Поэтому я создал пользовательские вилки для 2 классов Chunk и Wave_read.

Рабочий, но нестабильный полный код вы можете найти здесь .

Извините за мой ужасный английский.

Благодарю.

Как вы упомянули, file arg to wave.open(file,'r') может быть только именем файла или указателем открытого файла. Но p.stdout.read() – это содержимое файла p.stdout в виде строки. Однако вы не можете просто передать файл p.stdout в wave.open() потому что wave.open() нуждается в p.stdout для wave.open() wave.open() объекте, и вы обычно не можете seek() на трубе.

Но что вы можете сделать, это обернуть байты, которые вы читаете из p.stdout.read() в объекте io.BytesIO . Полученный объект будет вести себя как файл, и он будет доступен для поиска.

FWIW, не рекомендуется использовать file в качестве имени переменной, так как это имя встроенного типа в Python 2; Я предполагаю, что это нормально использовать Python 3, но я все равно избегаю этого.

Предупреждение: непроверенный код Python 2.6+

 fbytes = io.BytesIO(p.stdout.read()) fwave = wave.open(fbytes, 'r') 

Если вы конвертируете свой аудиофайл в raw audio вместо wav, используя -f s16le вместо -f wav вам может не понадобиться wave.open вообще, и вы можете напрямую wave.open ALSA. Я только пробовал это с PyAudio, и это сработало.