一种简单的方法是使用pydub 库,通过python -m pip install pydub 安装一次。我编写的下一个函数接受 MP3 字节(或任何其他音频格式)并返回 WAV 字节(或由format = '...' 参数表示的任何其他格式),函数不使用任何文件系统(全部在内存中完成)。
Try it online!
# Needs: python -m pip install pydub
# Convert
def conv_aud(data, format = 'wav'):
import pydub, io
inp, out = io.BytesIO(data), io.BytesIO()
pydub.AudioSegment.from_file(inp).export(out, format = format)
return out.getvalue()
# Test
with open('test.mp3', 'rb') as f:
print(conv_aud(f.read())[:32].hex().upper())
我的test.mp3 的输出:
5249464624D0110357415645666D7420100000000100020044AC000010B10200
另一种方法是在我的下一个解决方案中使用 FFMpeg。
我写了下一个函数conv_aud(),它可以将任何音频格式转换为任何其他格式,实际上它也可以转换视频。
默认情况下,如果未提供格式,它会将 MP3 转换为 WAV。
函数使用FFMpeg,安装一次。此外,如果它位于不在 PATH 系统变量中的 dir 内,那么您应该将参数 ffmpeg = 'c:/path/to/ffmpeg.exe' 提供给函数 conv_aud()。
函数接受输入音频的字节数据或输入文件的 str 路径或打开以供读取的任何类似文件的对象。它还接受 ifmt 参数用于输入格式字符串,如'mp3' 和/或接受ofmt 参数用于输出格式字符串,如'wav'。
函数返回WAV的字节数据。
函数使用中间临时目录和退出时删除的文件。如果出现错误,该函数还会打印来自 FFMpeg 的错误输出。如果有错误,则函数是静默的,不打印任何内容。
代码末尾有test()函数,用于测试使用test.mp3文件作为输入调用函数的不同方式。
Try it online!
# Converts any audio/video format (MP3 by default) to any other format (WAV by default).
# Input to function either "bytes" data or "str" path or read-opened "file" object.
# "ifmt" is input format (taken from input path by default or MP3 if not provided)
# "ofmt" is output format, WAV by default
# "ffmpeg" should contain path to ffmpeg executable
# Function returns converted audio bytes.
def conv_aud(data, ifmt = None, ofmt = 'wav', *, ffmpeg = 'ffmpeg'):
import tempfile, subprocess, secrets, traceback
inp_path = None
if type(data) is str:
inp_path, data = data, None
elif type(data) is not bytes:
data = data.read()
assert inp_path is not None or type(data) is bytes, (inp_path, type(data))
if inp_path is not None:
ifmt_ = inp_path[inp_path.rfind('.') + 1:].lower()
assert ifmt is None or ifmt.lower() == ifmt_, (ifmt, ifmt_)
ifmt = ifmt_
elif ifmt is None:
ifmt = 'mp3'
assert ifmt is not None and ofmt is not None, (ifmt, ofmt)
with tempfile.TemporaryDirectory() as td:
if data is not None:
inp_path = str(td) + '/' + secrets.token_hex(8).upper() + '.' + ifmt.lower()
with open(inp_path, 'wb') as f:
f.write(data)
out_path = str(td) + '/' + secrets.token_hex(8).upper() + '.' + ofmt.lower()
try:
with open(str(td) + '/out', 'wb') as fout, open(str(td) + '/err', 'wb') as ferr:
subprocess.run([ffmpeg, '-i', inp_path, out_path], check = True, stdout = fout, stderr = ferr)
good = True
except:
with open(str(td) + '/out', 'rb') as fout, open(str(td) + '/err', 'rb') as ferr:
for f, n in [(fout, ' stdout '), (ferr, ' stderr ')]:
print('-' * 20 + n + '-' * 20 + '\n', f.read().decode('utf-8', 'replace'), sep = '', end = '')
print('-' * 50)
good = False
if not good:
assert False, 'FFMpeg returned error!'
with open(out_path, 'rb') as f:
return f.read()
# Testing conv_aud
def test():
for inp in [open('test.mp3', 'rb').read(), 'test.mp3', open('test.mp3', 'rb')]:
print(conv_aud(inp)[:32].hex().upper())
if __name__ == '__main__':
test()
我的test.mp3 文件的输出:
5249464654D0110357415645666D7420100000000100020044AC000010B10200
5249464654D0110357415645666D7420100000000100020044AC000010B10200
5249464654D0110357415645666D7420100000000100020044AC000010B10200