说点闲话——用C++播放音乐的奇妙经历
  • 板块灌水区
  • 楼主BigCookie233
  • 当前回复10
  • 已保存回复10
  • 发布时间2024/8/7 20:30
  • 上次更新2024/8/7 21:29:23
查看原帖
说点闲话——用C++播放音乐的奇妙经历
242291
BigCookie233楼主2024/8/7 20:30

噩梦开始的地方

今天我想尝试用 C++ 放点音乐,于是前往bdfs搜索一番,发现了一个看起来十分不错的库——ufmod——尽管年代久远(请记住这个“年代久远”),但是功能简单,配置看起来也不难(大雾)。这个库托管在 sourceforge 上,很快就下好了,一切似乎都非常顺利。

瞎子传奇

下好了源码包,我从中找到了 ufmod.lib, 看起来甚至不需要我来编译,这真是太棒了。我马上新建了一个文件夹,刚建好源文件,我却犯了难——我找不到库函数的定义,我根本不知道这些函数该如何使用。

为了找到函数的使用方法,我再次bdfs,然而,由于年代久远,网上的资料寥寥无几,我在仅有的几篇资料中注意到了两个宏:XM_FILE和XM_MEMORY。关于这两个宏的故事,我们后面再谈。

bdfs无果,我退而求其次,尝试从ufmod.lib和ufmod.dll等文件中寻找符号表,我从 github 下载了一份 dumpbin 工具,导出了库文件的符号表。这里摘录部分数据如下

    ordinal hint RVA      name

          1    0 000010AA uFMOD_GetRowOrder
          2    1 000010A6 uFMOD_GetStats
          3    2 000010BA uFMOD_GetTime
          4    3 00001100 uFMOD_GetTitle
          5    4 00001030 uFMOD_Jump2Pattern
          6    5 0000109A uFMOD_Pause
          7    6 00001214 uFMOD_PlaySong
          8    7 0000109E uFMOD_Resume
          9    8 00001081 uFMOD_SetVolume

但是,还有一个关键的问题没有解决——函数的参数呢?

由于这个库是使用汇编编写的,生涩难懂,想通过阅读源码的方法来获得所需的信息对我来说不大可能了。我回到源码包,注意到其中有一份 ufmod.desc,里面有着各个函数的简要描述,包括函数的参数。

然而,这份描述有些令人难以理解:为什么 uFMOD_PlaySong 函数的前两个参数类型为 Any ???

所幸,作者使用了匈牙利表示法来命名参数,在 ChatGPT 的帮助下,我大致猜到了 lpXM 和 fdwSong 含义。但是第二个参数 para 我仍不明白有何含义。

随着bdfs再次失败,我终于看到了源码包中有一个 Examples 文件夹,看到这个文件夹的那一刻,我感觉自己唐完了。

链接出锅

Examples 文件夹里有着非常完善的解释,报错各个参数的含义,和之前提到的 XM_MEMORY 和 XM_FILE 的定义。最关键的是,这份实例里有一份 ufmod.h 头文件,这意味着我无需再操心函数定义的问题了。

胜利仿佛就在眼前,我添加好了链接器参数,开始编译。

不幸的,链接出锅了。uFMOD_PlaySong 没能成功链接到库文件。

随后我又尝试了多种方法,包括改换 ufmod.obj,都没能链接成功

灵异事件

由于链接器抽风,我不得不改换使用动态 DLL 的方式来加载这个库。

经过一番努力,代码再次出锅,GetLastError() 返回 139,为什么会变成这样呢?

一如往常,bdfs没能给出令人满意的回答。许多资料称这是因为加载的 dll 库依赖的其他库不存在。此时,我突发奇想,把编译器换成了32位,奇妙地解决了这个问题。

dll成功加载了,函数地址也成功获取。我的努力看起来没有白费,似乎马上就要成功,然而我的电脑却给我开了个玩笑:程序在播放了第一个音符后就立即停止了。

我尝试了库中所有的函数,都没法使音乐继续播放。使用 DLL 的道路也失败了。

最后的希望

尽管 dll 失败,我仍不死心,我注意源码包中还有一个 oalufmod.dll,OpenAL 成了我最后的希望。

参考了一番示例中的代码,我成功抄出来了一份使用 OpenAL 播放的代码(雾)。

在安装完 OpenAL 的驱动后。我期待着我的程序能够正常播放,结果却更加令人失望,这次连一点声音灰没有了。

于是,最后的希望也破灭了。

年代久远的运行库

我终于是气急败坏了。

由于示例中提供的 obj 文件虽能链接却会使程序还没进入主函数就立刻 RE。抱着破罐子破摔的心态,我下载了一个 fasm 汇编器,直接重新编译了一份 obj 目标文件。但是 waveOut 系列函数却总无法链接。bdfs得到的资料称应在链接器参数中加入 -lwinmm,然而这并没有任何效果。

忽然,我想起了我前段时间安装的 Windows SDK 中有一个 WinMM.Lib。我抱着试一试的想法,把源码包中提供的 vb_winmm.lib 替换为了 WinMM.Lib。

就在此时,奇迹发生了,程序成功链接并播放了音乐。我不禁感叹:远古库害死人呐。

警示后人

在链接器参数中,请将 WinMM.Lib 放在 ufmod.obj 后面,否则将导致链接失败。

2024/8/7 20:30
加载中...