如何使用Python从mp3文件中提取原始数据?

7
我有一个与使用Python进行音频数据分析相关的作业。我想知道是否有好的模块可以用来从mp3文件中提取原始数据。我的意思是原始数据,而不是元数据、id3标签。
我知道如何使用wave模块处理.wav文件。我可以使用readframes获取原始数据。但我不知道如何处理mp3文件。我在谷歌和stackoverflow上搜索了很多,找到了eyeD3。但不幸的是,文档令人沮丧,并且现在的版本是0.7.1,与我能在互联网上找到的大多数示例不同。
是否有好的模块可以从mp3中提取原始数据?如果有好的eyeD3文档,那也好。

请查看此链接:https://dev59.com/wnA75IYBdhLWcg3w49UR 。显然,最简单的方法是使用外部程序将mp3转换为wav。 - Jakub M.
2
“原始数据”这个词很令人困惑。如果你说“原始数据”,我认为你想要获取文件的字节(可以使用open('your.mp3', 'rb')来获取)。但我认为你不需要这种类型的原始数据。 - Kritzefitz
我想要原始数据——文件的字节。但是,并非文件的所有字节都是音乐内容。还有一些标签和可能是其他东西。所以我想知道是否有任何模块可以提取它。@IchUndNichtDu - zhangyangyu
4个回答

21

如果我理解你的问题,你可以尝试使用pydub(我写的一个库)来获取音频数据,方法如下:

from pydub import AudioSegment

sound = AudioSegment.from_mp3("test.mp3")

# sound._data is a bytestring
raw_data = sound._data

我收到了以下错误信息:File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/json/decoder.py", line 355, in raw_decode raise JSONDecodeError("Expecting value", s, err.value) from None json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0) - bobsmith76
我解决了这个问题,并在这里描述了解决方案 https://github.com/jiaaro/pydub/issues/450 - bobsmith76
不支持mp4格式? - WestCoastProjects
@StephenBoesch ffmpeg肯定支持mp4,所以它应该支持 - 但你可能需要指定它应该使用哪种编解码器。 - Jiaaro
如何转换为float16?除以255还是float16.max?谢谢。 - thecr0w

5
有几个类似的问题在stackoverflow上出现。这里有不同的使用情况。
1. 用户想要将.mp3文件转换为PCM文件,例如.wav文件。 2. 用户想要访问.mp3文件中的原始数据(即不将其视为压缩的PCM)。这里的用例是理解MP3和AAC等压缩方案的工作原理。
本答案针对第二种情况,但我没有可共享或指向的工作代码。
像MP3这样的压缩方案通常在频域中工作。作为一个简化的例子,您可以每次取1024个样本的.wav文件块,使用FFT变换每个块的1024个样本,并存储它们。粗略地说,有损压缩然后从频域中丢弃信息以允许更小的编码。
如果你想要从.mp3转换为.wav,使用纯Python实现是非常不切实际的。但是,如果你想探索.mp3和相关压缩方案的工作原理,并拥有一些易于调整的东西,即使代码运行比ffmpeg慢1000倍,也可以很有用,特别是如果代码以一种允许源代码阅读者看到.mp3压缩如何工作的方式编写。例如,参见http://bugra.github.io/work/notes/2014-07-12/discre-fourier-cosine-transform-dft-dct-image-compression/,其中有一个IPython工作簿,介绍了频域变换在图像压缩方案(如JPEG)中的使用方法。对于学习压缩的人来说,类似MP3压缩等的东西将会非常有用。
一个 .mp3 文件基本上是一系列 MP3 帧,每个帧都有头部和数据组件。因此,首要任务是编写一个 Python 类(或类)来表示它们,并从 .mp3 文件中读取它们。首先以二进制模式读取文件(即 f = open(filename,"rb"),然后 data = f.read() -- 在现代计算机上,鉴于典型的 5 分钟歌曲在 .mp3 格式下大约为 5MB,你可以一次性读取整个文件)。可能还值得编写一个更简单(但效率远低于 MP3 和 AAC 等方案)的编码方案来探索其工作原理,逐步添加这些方案使用的技巧。例如,将 PCM 输入文件分成 1024 个样本块,使用 FFT 或 DCT 等方法进行转换,然后再转换回去,看看如何恢复原始数据。然后探索如何从频率变换版本中丢弃数据,并查看将其转换回 PCM 数据时会产生什么影响。最终结果起初会非常糟糕,但通过看到问题,以及看到 MP3 和 AAC 等方案的处理方式,你可以了解为什么这些压缩方案采用这种方式处理。
简而言之,如果您的使用场景是“完成任务”,那么您可能不想使用Python。另一方面,如果您的使用场景是“学习如何完成任务”,那就不同了。(粗略地说,在90年代的Pentium 100上使用优化的汇编语言可以做到的事情,在现代Core i5上使用Python大致可以以相同的性能完成 - 大约有100倍的原始性能因素,并且使用Python会有类似的减速)。

4
我使用了Jiaaro的答案中的pydub,但我想为这个问题添加一些代码,可以从MP3文件中提取PCM数据。下面是一个注释齐全的完整程序,用于读取MP3文件,将PCM数据提取到有符号整数列表中,然后使用matplotlib绘制它。当然,需要安装pydub和matplotlib。
from pydub import AudioSegment
from matplotlib import pyplot as plt

# This will open and read the audio file with pydub.  Replace the file path with
# your own file.
audio_file = AudioSegment.from_file("./2021-02-23-22:00:11-edited.mp3")

# Set up a list for us to dump PCM samples into, and create a 'data' variable
# so we don't need to type audio_file._data again
data = audio_file._data
pcm16_signed_integers = []

# This loop decodes the bytestring into PCM samples.
# The bytestring is a stream of little-endian encoded signed integers.
# This basically just cuts each two-byte sample out of the bytestring, converts
# it to an integer, and appends it to the list of samples.
for sample_index in range(len(data)//2):
    sample = int.from_bytes(data[sample_index*2:sample_index*2+2], 'little', signed=True)
    pcm16_signed_integers.append(sample)

# Now plot the samples!
plt.plot(pcm16_signed_integers)
plt.show()

这是我的绘图结果(我放大了一个好的部分): 使用Matplotlib绘制的音频数据图 是的,这个图表是从上面的代码生成的:D

太好了!我终于找到了 MP3 原始数据的一个用途! - undefined

3
您尝试过以读取二进制模式打开文件吗?
f = open("test.mp3", "rb")
first16bytes = f.read(16)
etc...

1
我非常确定OP想要的是编码在mp3中的音频数据。在这种情况下,在读取之前需要对mp3进行解码。 - Jiaaro
@Jiaaro,你应该发布一个解释的答案。 - Stephan
你如何解释前16个字节代表什么?通常我习惯于在二维频率随时间表示中看到,那么这对于单个字节的维度来说是如何读取的呢? - bmc
甚至没有解决MP3解码问题。在Python 3中,打开和读取文件是你学习的第一件事情之一。我认为这个答案需要一个关于如何实际解码文件的部分。我刚刚测试了Jiaaro答案中的pydub,它工作得很好。但它与当前代码不兼容。也许还有其他好的库存在,你可以提到它们来为这里的答案做出贡献? - Nuclear_Man_D

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