更新:我在 cython 中编写了一个简单的演示,可以成功播放您链接的 .xm 文件。它基本上是this c demo code 的翻译。我的代码可以在this github page 上找到。为了让它在 Ubuntu 中工作,我必须安装 libxmp-dev 包。请注意,目前所有内容都是硬编码的,因此需要对其进行重构以更直接地在您的项目中使用。
这绝不是一个决定性的答案。在此过程中,我遇到了许多潜在的陷阱,这让我怀疑 pygame 是否适合这里的工作,但我将介绍到目前为止我发现的内容以及一些建议。
看起来.xm Fast Tracker MODule 格式与您典型的 wav/ogg/mp3 文件不同,它不仅可以播放一组样本数据,还可以组合不同的 MIDI乐器和样本一起创造你的音乐,就像问题中链接的(甜美的)chiptune。
事实证明,SDL/pygame 可以播放此类文件,但方式相当有限。查看pygame的music模块,有一个set_pos函数。但是,尝试使用它给了我一个pygame.error: set_pos unsupported for this codec。然而有趣的是,我可以通过使用pygame.mixer.music.play 和可选的start 关键字来解决这个问题。虽然大多数文件格式上的start 只是开始文件前几秒的偏移量(仅在歌曲的第一次播放时),但它对于MOD 文件(如问题中的.xm 文件)具有不同的含义。 Apparently,对应MOD文件中的pattern编号。因此,根据每个模式在文件中的起始位置,可以在 pygame 中使用的潜在起始点数量非常有限。
如果您想从一个特定的模式编号开始,那么下面的代码就足以循环。请注意,我使用 pygame 的事件系统来查看声音何时完成以使用适当的“模式偏移”“循环”声音文件:
import pygame
pygame.init()
pygame.mixer.music.load('zeta_force_level_2.xm')
pattern = 10
loop_event = pygame.USEREVENT + 1
pygame.mixer.music.set_endevent(loop_event)
pygame.mixer.music.play(start=pattern)
while True:
for event in pygame.event.get():
if event.type == loop_event:
pygame.mixer.music.play(start=pattern)
此时,您可能想知道这些patterns 到底是什么?如果您的系统上安装了 ffmpeg,您可以在您的文件上运行 ffprobe 并获得以下输出:
Input #0, libmodplug, from 'zeta_force_level_2.xm':
Metadata:
name : zeta force level 2
instrument : by zabutom --
: bye bye computer..
: see you in a week
sample : zeta force level 2
extra info : 20 patterns, 10 channels, 3/14 instruments, 1/14 sample
Duration: 00:01:01.00, bitrate: 3 kb/s
Stream #0:0: Audio: pcm_s16le, 44100 Hz, 2 channels, s16, 1411 kb/s
该文件中似乎有 20 种模式,您可以从中选择作为循环的起始位置。要获取有关您的特定文件的更多信息,您可以在 MilkyTracker 之类的工具中打开(并编辑!)您的文件并获得如下输出:
youtube 上有一些 MilkyTracker 的在线教程,但它看起来是一个相当复杂的软件。
似乎还有一个名为libxmp 的库及其对应的python binding。这应该处理将 MOD 文件数据“渲染”为简单 PCM 数组所需的转换,该数组可以在 pyaudio 或任何与 OpenAL 绑定的 python 库中播放。不管怎样,看起来你的工作已经完成了!