系统消息 发表于 2019-8-28 01:13:09

【DirectX】用 DirectMusic 实现可更换音色库和多轨的 midiOut

本帖最后由 系统消息 于 2019-8-28 20:52 编辑

原帖来源:【DirectX】用 DirectMusic 实现可更换音色库和多轨的 midiOuthttps://www.0xaa55.com/forum.php?mod=viewthread&tid=25859&fromuid=4293(出处: 技术宅的结界,转载请保留出处。)
参考来源:【C语言】Midi文件播放器(可跨平台)https://www.0xaa55.com/forum.php?mod=viewthread&tid=1489&fromuid=4293(出处: 技术宅的结界)

之前看过A5大神写的midi文件解析,并用winmm.dll的midiOut函数输出实现播放midi音乐,我在研究的过程发现了一些不太理想的问题:
1.midiOut 设备不能打开多个实例(也就基本上告别了同时播放多个文件),软件设备一个进程只能打开1个实例,硬件设备整个系统只能打开1个实例。
2.midiOut 设备没有多轨支持,A5大神的代码是忽略了音轨序号,这样做对于一些比较大的midi文件会出现冲突。
3.midiOut 设备没有精确时间控制,A5大神的代码是每Sleep1毫秒,更新一次midi进度,这样只能做到毫秒级精度控制,但是Sleep本身又不稳定。
4.midiOut 设备无法更换音色库,只能局限于midiOut设备驱动提供的音色。
5.midiOut API只能选择用哪个midi设备来合成声音,不能选择最终合成的声音输出到哪张声卡。

我在之前接触过DirectMusic,知道DirectMusic支持在应用层自行midi合成,再通过DirectSound做最终输出,因此以上问题在它这里都不是问题。
不过网上的DirectMusic资料全部是老掉牙的,只有上层接口的相关API调用代码(加载、解析midi文件都封装好了,完全不用接触底层),但是我想要的不是这些,而是像winmm.dll的midiOut,自己输出原始midi命令的功能,而且微软在很多年已经放弃了对DirectMusic的上层接口支持(x86下还在兼容,但新版DXSDK里面不再提供头文件了,x64下直接连dll都没有了),只有DirectMusic的底层接口微软一直提供支持(SDK一直在提供头文件,x64也有对于的dll),可是微软从来没有给过这方面的例子,只有接口描述文档,网上也找不到有人给过这方面的例子。于是我就自己慢慢研究DirectMusic的底层接口,最后终于成功自己实现midiOut的功能,再与A5大神的midi文件解析代码结合,不仅完美解决所有问题,还可以结合DirectSound实现音量、音高、平衡、3D空间音效以及DX8的声音特效。现在是时候来分享技术了:

从上图可以看到,基于DirectMusic实现的MIDI播放器,我们除了可以更换midi文件外,还可以自由更换音色库文件,可以随时调节音量、音高、平衡,中间的输出内容就是A5大神的midi文件解析库的输出内容(A5大神原版是输出到控制台)。
提示:DirectMusic每个合成器实例最大支持1000个音轨,每个音轨又各自有16个独立通道(每个通道9都是架子鼓),但是这16000个通道都是在同一个DirectSoundBuffer上输出的,因此单独调整DirectSoundBuffer属性,请分别创建多个DirectMusicPort实例(并为它们各自己绑定一个DirectSoundBuffer)。
推荐用法:在一个游戏进程内同时播放多个midi文件时,只需要创建一个DirectSound+DirectMusic,给每个midi文件创建一个DirectSoundBuffer+DirectMusicPort,而同一个midi文件的多个音轨分配在同一个DirectMusicPort的不同通道组即可。

在附件中提供了,DXMIDIOut(用DX模拟的midiOut)、midiPlayer(MIDI播放器,不带GUI)、midiPiano(MIDI电钢琴)等源码,以及MIDI播放器和MIDI电钢琴的二进制文件(需要VC2013运行库)。
MIDI 电钢琴是模仿当年4399上的flash键盘钢琴做的,只不过我没有做界面(只有一个空白对话框,方便让你拖拽dls文件去更换音色库,不然我就直接做成控制台了)。
附件中的压缩包密码是没有密码的拼音,其中 ReadMe.txt 提供压缩包内文件描述,MusicScore.txt 是MIDI电钢琴的乐谱(从flash键盘钢琴上继承,少部分是我自己添加)。
提示:由于附件资源的大小有限,无法上传Demo中测试用的音色库文件(一百多兆),如有学习需要请QQ联系我,或者自己去网上找找。(如需商用请自己注意版权问题)。
免责声明:Demo中的资源文件全部来源网络,仅供学习参考,未避免版权纠纷问题,请勿用于商业。

秋枫萧萧 发表于 2019-9-30 23:47:53

YY菌的改良效果不错!

0xAA55 发表于 2019-8-28 10:17:44

我记得我似乎没有忽略多音轨的问题

watermelon 发表于 2019-8-28 12:01:31

支持系统消息大佬。

系统消息 发表于 2019-8-28 19:32:05

本帖最后由 系统消息 于 2019-8-28 19:42 编辑

0xAA55 发表于 2019-8-28 10:17
我记得我似乎没有忽略多音轨的问题

你的是解析多轨的确是对的,我是说最后用midiOutShortMsg输出的时候,你忽略了音轨参数,直接把所有音轨往同一个音轨上输出的。
举个例子,假如说音轨1和音轨2在同时在使用通道1,它们就会发生冲突,比如音轨1把通道1设成钢琴,音轨2把通道1设成小提琴,谁执行的晚一步就会覆盖上一个音轨内容,还有就是两个音轨互相开关冲突问题。
DirectMusic的多轨机制是每个音轨各自都有独立的16个通道,音轨1的通道1和音轨2的通道1是两个单独的通道实例。
我帖子截图里面那个梁祝.mid就存在这个现象(好像有三十多个音轨),从截图上的输出内容可以看出20、21、22三个音轨在同时使用D(13)通道,改用DirectMusic的多轨机制后就正常了。

0xAA55 发表于 2019-8-28 22:05:00

系统消息 发表于 2019-8-28 19:32
你的是解析多轨的确是对的,我是说最后用midiOutShortMsg输出的时候,你忽略了音轨参数,直接把所有音轨 ...

与其说我忽略了多音轨的问题,不如说,我把音轨和通道概念搞混了。

图片是多年前写的VB6播放midi时解析midi字节的逻辑

失去系统消息 发表于 2019-9-30 23:27:12

我就坐等你出vb6版的再下载

BUG菌 发表于 2020-2-12 16:59:50

膜拜大佬

失去系统消息 发表于 2020-4-21 19:27:40

好了,不过你啥时解决一下大文件卡住的问题 ?

clhenyan 发表于 2021-6-21 06:38:50

正需要这个代码,音色库在什么地方下载?

clhenyan 发表于 2021-6-21 06:40:27

手上有sf2音色库,可以转成dls音色库吗

系统消息 发表于 2021-6-21 09:58:06

clhenyan 发表于 2021-6-21 06:40
手上有sf2音色库,可以转成dls音色库吗

可以,有专门的音色库格式转换工具(比如Awave Studio)。

小冰 发表于 2022-1-18 02:28:46

系统消息YYDS

工具人 发表于 2022-3-25 19:00:40

牛蛙 牛蛙

COCO在丫 发表于 2022-3-29 10:55:26

大神,可以提供一下源代码研究研究嘛,最近在弄这个

系统消息 发表于 2022-3-29 22:26:20

COCO在丫 发表于 2022-3-29 10:55
大神,可以提供一下源代码研究研究嘛,最近在弄这个

源码不是在附件里面吗?
页: [1]
查看完整版本: 【DirectX】用 DirectMusic 实现可更换音色库和多轨的 midiOut