尝试将mp3文件转换为Numpy数组,但ffmpeg卡住了。

3

我正在使用Scikit-learn开发一个音乐分类方法,其中第一步是将音乐文件转换为numpy数组。

在尝试从Python脚本中调用ffmpeg失败后,我决定直接将文件进行管道传输:

FFMPEG_BIN = "ffmpeg"
cwd = (os.getcwd())
dcwd = (cwd + "/temp")
if not os.path.exists(dcwd): os.makedirs(dcwd)

folder_path = sys.argv[1]
f = open("test.txt","a")

for f in glob.glob(os.path.join(folder_path, "*.mp3")):
    ff = f.replace("./", "/")
    print("Name: " + ff)
    aa = (cwd + ff)

    command = [ FFMPEG_BIN,
        '-i',  aa,
        '-f', 's16le',
        '-acodec', 'pcm_s16le',
        '-ar', '22000', # ouput will have 44100 Hz
        '-ac', '1', # stereo (set to '1' for mono)
        '-']

    pipe = sp.Popen(command, stdout=sp.PIPE, bufsize=10**8)
    raw_audio = pipe.proc.stdout.read(88200*4)
    audio_array = numpy.fromstring(raw_audio, dtype="int16")
    print (str(audio_array))
    f.write(audio_array + "\n")

问题是,当我运行文件时,它启动了ffmpeg,但没有任何动作:
[mp3 @ 0x1446540] Estimating duration from bitrate, this may be inaccurate
Input #0, mp3, from '/home/don/Code/Projects/MC/Music/Spaz.mp3':
  Metadata:
    title           : Spaz
    album           : Seeing souns
    artist          : N*E*R*D
    genre           : Hip-Hop
    encoder         : Audiograbber 1.83.01, LAME dll 3.96, 320 Kbit/s, Joint Stereo, Normal quality
    track           : 5/12
    date            : 2008
  Duration: 00:03:50.58, start: 0.000000, bitrate: 320 kb/s
    Stream #0:0: Audio: mp3, 44100 Hz, stereo, s16p, 320 kb/s
Output #0, s16le, to 'pipe:':
  Metadata:
    title           : Spaz
    album           : Seeing souns
    artist          : N*E*R*D
    genre           : Hip-Hop
    date            : 2008
    track           : 5/12
    encoder         : Lavf56.4.101
    Stream #0:0: Audio: pcm_s16le, 22000 Hz, mono, s16, 352 kb/s
    Metadata:
      encoder         : Lavc56.1.100 pcm_s16le
Stream mapping:
  Stream #0:0 -> #0:0 (mp3 (native) -> pcm_s16le (native))
Press [q] to stop, [?] for help

它只是挂在那里,比歌曲时间更长地停留着。我在这里做错了什么?


你为什么要计算88200*4 - Padraic Cunningham
这就是代码示例所言。 - Rich
代码来自哪里? - Padraic Cunningham
在这里:https://zulko.github.io/blog/2013/10/04/read-and-write-audio-files-in-python-using-ffmpeg/ - Rich
2个回答

3
我建议您使用pymedia、audioread或decoder.py。还有pyffmpeg和类似的模块可以做到您想要的。请查看pypi.python.org。
当然,这些都不能帮助您将数据转换为numpy数组。
无论如何,以下是使用管道将其粗略处理成ffmpeg的方法:
from subprocess import Popen, PIPE
import numpy as np

def decode (fname):
    # If you are on Windows use full path to ffmpeg.exe
    cmd = ["./ffmpeg.exe", "-i", fname, "-f", "wav", "-"]
    # If you are on W add argument creationflags=0x8000000 to prevent another console window jumping out
    p = Popen(cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
    data = p.communicate()[0]
    return np.fromstring(data[data.find("data")+4:], np.int16)

以下是基本用法:

正常情况下,ffmpeg的输出是16位音频。因此它应该能够正常工作。但是如果您进行某些操作,您需要知道numpy没有int24,因此您将被迫进行一些位操作,并将24位音频表示为32位音频。最好不要使用24位音频,保持简单,让世界变得更美好。:D

如果您需要更高级的内容,我们可以在评论区进一步探讨代码的优化。


不,除了numpy之外,所有都是标准库。我现在会进行检查。 - Dalen
我现在有一个40.7 MB的文本文件。谢谢,你真的帮了我。 - Rich
我应该得到什么? - Rich
从decode()函数中,您会得到一个dtype为numpy.int16的numpy.ndarray()实例。您是如何将其保存为txt文件的? - Dalen
好的,我使用了np.savetxt("array.txt", a)。它目前已经有500 MB大小并且还在增长中。:( - Rich
显示剩余14条评论

2
这是我所使用的:它使用pydub(它使用ffmpeg)和scipy
完整设置(在Mac上,其他系统可能有所不同):
pip install scipy
pip install pydub
brew install ffmpeg  # Or probably "sudo apt-get install ffmpeg on linux"

然后读取mp3文件:

import tempfile
import os
import pydub
import scipy
import scipy.io.wavfile


def read_mp3(file_path, as_float = False):
    """
    Read an MP3 File into numpy data.
    :param file_path: String path to a file
    :param as_float: Cast data to float and normalize to [-1, 1]
    :return: Tuple(rate, data), where
        rate is an integer indicating samples/s
        data is an ndarray(n_samples, 2)[int16] if as_float = False
            otherwise ndarray(n_samples, 2)[float] in range [-1, 1]
    """

    path, ext = os.path.splitext(file_path)
    assert ext=='.mp3'
    mp3 = pydub.AudioSegment.from_mp3(file_path)
    _, path = tempfile.mkstemp()
    mp3.export(path, format="wav")
    rate, data = scipy.io.wavfile.read(path)
    os.remove(path)
    if as_float:
        data = data/(2**15)
    return rate, data

感谢詹姆斯·汤普森的博客提供的帮助。


1
你需要使用 os.close(_) (并且可能需要将 _ 重命名为 fd)来关闭临时文件描述符。否则,当在 for 循环中运行时,最终会出现 [Errno 24] Too many open files 错误。 - Matthew D. Scholefield

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接