我正在尝试使用ffmpeg解码mp3到wav:
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(file,r)以使用readframes(n)和其他方法。但我不能,因为wave.open(file,'r')中的'file'只能是文件名或打开文件指针。
...
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
因此可以通过wave.open()处理来自p.stdout.read()的数据吗? 制作临时.wav文件不是解决方案。
抱歉我的英文。 感谢。
更新
感谢关于io.BytesIO的 PM 2Ring 。
但是生成的代码不起作用。
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'
...
由于'bad'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。 但是这个功能正常。
按照指定here:
“5-8个字节 - 文件大小(整数) 整个文件的大小 - 8个字节,以字节为单位(32位整数)。 通常,您会在创建后填写此内容。“ 这就是我的脚本不起作用的原因。 wave.open和其他函数无法处理我的文件对象,因为self.chunksize = 0.看起来ffmpeg在使用PIPE时无法插入文件大小。
解
这很简单。 我已经改变了Chunk类的init函数:
后:
...
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
...
当然,原始模块的编辑是错误的idia。所以我为2类Chunk和Wave_read创建了自定义分支。
工作但不稳定的完整代码,您可以找到here。
对不起我糟糕的英语。
感谢。
答案 0 :(得分:0)
正如您所提到的,file
arg到wave.open(file,'r')
只能是文件名或打开文件指针。但p.stdout.read()
是文件p.stdout
的内容作为字符串。但是,您不能只将p.stdout
文件传递给wave.open()
,因为wave.open()
需要一个可搜索的类文件对象,而您通常无法seek()
1}}在管道上。
但可以做的是将您从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')
答案 1 :(得分:0)
如果您使用-f s16le
代替-f wav
将音频文件转换为原始音频而非wav,则可能根本不需要wave.open
,您可以直接转发到ALSA。我只用PyAudio尝试过它,但它确实有效。