本教程仅针对C语言的单线程播放bgm功能,均为本人查阅使用后总结,如有问题和指正欢迎在评论区留言
在播放bgm的时候,我们常常要考虑如何才能让bgm和其他音效同时播放的问题,而网络上很多答复是使用多线程
众所周知多线程的上手难度较高,因此我在这里提供一个思路来规避多线程操作,用简单的单线程就可以实现兼顾bgm和音效的播放
- 前言:
- 一、运行前提
- 1.函数:实现bgm的播放共需要2个函数
- (1)PlaySound函数:
- (2)mciSendString函数
- 2.头文件需求:
- 二、使用思路
- 1.基本思路
- 2.静音bgm的实现
- (1)使用mciSendString做bgm
- (2)使用PlaySound做bgm
- 三、注意事项&&常见问题
- 1.PlaySound无法播放的问题
- (1)格式问题
- (2)检测播放问题
- 2.mciSendString无法播放的问题
- (1)格式问题
- (2)解决方案
- (3)检测播放问题
适用于播放非BGM的声音,原型:
BOOL PlaySound (LPCSTR pszSound, HMODULE hmod,DWORD fdwSound)
其中三个变量分别是:pszSound要播放的音频的相对位置(字符串表示)、hmod资源指示符(一般为NULL即可)、fdwSound播放模式
常用的播放模式有:
SND_FILENAME pszSound参数指定WAV文件名(使第一个参数生效的,必须有)
SND_ASYNC 用异步方式播放声音,PlaySound函数在开始播放后立即返回(在程序进行时播放,不会影响其他代码正常运行,即不会让游戏停下来等音频播放)
SND_SYNC 同步播放声音,在播放完后PlaySound函数才返回(会让游戏停下来等音乐播放,只是为了和上面的对比,实际上不会用)
SND_LOOP 重复播放声音,必须与SND_ASYNC标志一块使用(可选,让音频重复播放的)
多个播放模式中间用或 “ | ” 连接
具体使用例子:
PlaySound("叮.wav", NULL, SND_FILENAME | SND_ASYNC);
(2)mciSendString函数
适用于bgm的播放,原型:
MCIERROR mciSendString( LPCTSTR lpszCommand, LPTSTR lpszReturnString, UINT cchReturn, HANDLE hwndCallback)
其中的四个变量只需要了解第一个即可,如果只是播放bgm的话,剩下三个变量分别为NULL,0,NULL
PS:因为mciSendString是MCI(Media Control Interface,媒体控制接口)的一个函数,顾名思义是播放媒体的,不仅能播放音频还能播放视频,因此剩下三个变量是为播放视频等准备的,如需了解请参考这位dalao的MCI详解,这里不做过多赘述
而第一个参数是一个String型的变量,共包含3个部分:命令,文件名称,其他操作
命令:open(打开音频),play(播放已打开的音频),close(关闭) ——类似C语言中对文件的操作
文件名称:
2.头文件需求:#include二、使用思路 1.基本思路#include #pragma comment(lib,"Winmm.lib")
推荐使用mciSendString播放bgm,使用PlaySound播放游戏内各种音效
原因:由于mciSendString播放的时候,如果一个bgm已经在播放了,那么新播放的bgm不会生效,例如:
mciSendString(TEXT("open bgm1.mp3 alias BGM1"), NULL, 0, NULL);
mciSendString(TEXT("open bgm2.mp3 alias BGM2"), NULL, 0, NULL);
mciSendString(TEXT("play BGM1 repeat"), NULL, 0, NULL);
mciSendString(TEXT("play BGM2 repeat"), NULL, 0, NULL);
则播放的只有bgm1.mp3
因此如果用mciSendString播放一个bgm则没有任何问题,如果用它播放音效则会出现前一个还没有播放完,后一个音效就不出声的问题。
而PlaySound由于拥有参数fdwSound来控制播放模式,正常的 SND_ASYNC (异步播放模式)就可以达到让后一个音效播放时覆盖前一个音效的效果了。
如果有个别音效需要不覆盖前面的音效(即模仿mciSendString播放逻辑的话),可以使用播放模式SND_NOWAIT 来实现,它的作用是"如果驱动程序正忙则函数就不播放声音并立即返回"。
例如:
PlaySound("音效1.wav", NULL, SND_FILENAME | SND_ASYNC);
PlaySound("音效2.wav", NULL, SND_FILENAME | SND_ASYNC);
PlaySound("音效3.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NOWAIT);
Sleep(1000);
PlaySound("音效4.wav", NULL, SND_FILENAME | SND_ASYNC | SND_NOWAIT);
则最后结果为:
音效1一定不会播放(被音效2覆盖了)
音效2一定会播放
音效3一定不会播放
音效4不一定播放(如果音效2比1秒长不播放,否则播放)
写到这里我有一个奇思妙想,有没有同学帮我实现一下:如果mciSendCommand和mciSendString交替使用可不可以实现音频覆盖,从而达到PlaySound的结果?很明显这个东西并不实用
bool music=1;
if("开/关bgm的操作"){
music = !music;
if (music) {
mciSendString("open bgm.mp3 alias BGM", NULL, 0, NULL);
mciSendString("play BGM repeat", NULL, 0, NULL);
}
else {
mciSendString("close BGM", NULL, 0, NULL);
}
}
(2)使用PlaySound做bgm
有两种思路:
一是使用SND_PURGE 播放模式,作用是让正在播放的音频停止播放
二是播放一个无声的音频来覆盖之前的bgm,以达到"伪静音"的效果
例如:
bool music=1;
if("开/关bgm的操作"){
music = !music;
if (music) {
PlaySound("bgm.wav",NULL, SND_FILENAME | SND_ASYNC | SND_LOOP);
}
else {
PlaySound("bgm.wav",NULL, SND_PURGE);//方法1
PlaySound("空音轨.wav",NULL, SND_FILENAME | SND_ASYNC);//方法2
//方法1和方法2选择一种即可
}
}
三、注意事项&&常见问题
1.PlaySound无法播放的问题
(1)格式问题
PlaySound只支持播放wav类型的音频
如果是其他格式的音频,如mp3格式,可以通过转码操作得到wav格式的音频
这里推荐的转码软件:格式工厂,一个免费纯净的转格式软件
(2)检测播放问题PlaySound如果没能成功播放音频,则会出现系统提示音
这样很容易判断到底是没有执行到PlaySound函数,还是音频本身的问题
2.mciSendString无法播放的问题相比于PlaySound而言,mciSendString并没有那么强的检测功能和兼容性,因此选做bgm还有一个好处就是使用的地方少。
(1)格式问题mciSendString虽然支持格式更多,包括MPEG,AVI,WAV,MP3等等,其中常用音频有MP3和WAV,但是兼容性很差
首先,如果播放的是WAV格式,则要保证这个wav音频是无损的,我个人测试通过格式工厂转码过后的WAV格式是播放不了的
其次,如果是mp3格式,则要保证这个mp3是无封面且无损的,因为mp3格式的音频支持携带封面,而且常常是从音乐软件那里下载的,经常会带着封面一起下载下来,这时的mciSendString就无法播放了
(2)解决方案如果是wav格式,请确保下载的是无损版的,推荐可以从耳聆网下载
如果是mp3格式,推荐从QQ音乐下载,不带封面而且直接就可以播放;不要用网易云下载,网易云下载的音频不能直接播放,而且貌似即使去掉封面依旧不能用
(3)检测播放问题mciSendString并没有PlaySound那样的提示音,如果没有播放成功不会有任何响应
可以使用printf大法来检测究竟是谁出了问题
到这里就结束了
这是新人第一篇CSDN,如有谬误欢迎指正批评
特别鸣谢:提供帮助的舍友和CSDN的大佬们参考资料:
https://blog.csdn.net/qq_42591783/article/details/121686347https://blog.csdn.net/seektostart/article/details/9429711



