C++ 在多线程中使用mciSendString播放音乐 上篇

来源:互联网 发布:豆瓣fm for windows 编辑:程序博客网 时间:2024/04/29 07:20

假设你正在学习游戏编程,随着学习的深入,终于要开始学习音乐播放了,能让自己编写的游戏播放音乐,那是件多么美好的事情啊!

于是乎,你根据MCI的相关资料,简单编写了一个音乐播放程序,代码如下:

Eg1:

#include <Windows.h>#include <string>#include <iostream>#pragma comment(lib,"winmm.lib")//发送信息给MCIbool SendToMCI(std::string command){if(!mciSendString(command.c_str(),NULL,0,0))return true;return false;}//从MCI获取信息std::string GetFromMCI(std::string command){char message[20];mciSendString(command.c_str(),message,20,0);std::string str(message);return str;}//入口函数int main(int argc, char *argv[]){std::string filepath="歌唱祖国.mid";std::string Open="OPEN "+filepath+" ALIAS MUSIC";std::string Status="status MUSIC mode";std::string Play="PLAY MUSIC FROM 0";std::string Close="CLOSE MUSIC";SendToMCI(Open);//打开音乐文件long Tick=0;std::string result="";while(true){Sleep(1);Tick=timeGetTime();result=GetFromMCI(Status);//获取音乐状态if(result=="stopped")SendToMCI(Play);//如果音乐停止,重新播放if(timeGetTime()-Tick>100)std::cout<<"卡了一下!\n";}SendToMCI(Close);//关闭音乐文件return 0;}

运行程序,它确实播放音乐了(^o^),你感到很欣慰,只是......每次音乐结束后,重新播放音乐时,都会卡一下(midi文件的该现象尤为明显)。

你心想,游戏运行时,肯定不能让主线程卡,除非想让玩家扔板砖(回想当年在PC上玩卡卡布三部曲,每换一个场景播放BGM时都会卡,无奈之下只有把BGM关了,哎......)。

左想右想,最终你决定用多线程解决之,要卡就让子线程去卡,不要影响咱主线程。

于是乎,你各方面查找多线程编程知识,最终改编代码如下:

Eg2:

#include <Windows.h>#include <process.h>#include <string>#include <iostream>#pragma comment(lib,"winmm.lib")volatile bool g_bEndMusicThread;//让线程结束的标志//发送信息给MCIbool SendToMCI(std::string command){if(!mciSendString(command.c_str(),NULL,0,0))return true;return false;}//从MCI获取信息std::string GetFromMCI(std::string command){char message[20];mciSendString(command.c_str(),message,20,0);std::string str(message);return str;}//音乐线程的方法unsigned __stdcall ThreadPlayMusic(LPVOID lpParameter){std::string Status="status MUSIC mode";std::string Play="PLAY MUSIC FROM 0";while(true){Sleep(1);if(g_bEndMusicThread==true)//接收主线程发送的退出信号break;std::string result=GetFromMCI(Status);//获取音乐状态if(result=="stopped")SendToMCI(Play);//如果音乐停止,重新播放}return 0;}//入口函数int main(int argc, char *argv[]){std::string filepath="歌唱祖国.mid";std::string Open="OPEN "+filepath+" ALIAS MUSIC";std::string Close="CLOSE MUSIC";SendToMCI(Open);//打开音乐文件g_bEndMusicThread=false;//初始化子线程标志位HANDLE hThread = (HANDLE)_beginthreadex(NULL,0,ThreadPlayMusic,NULL,0,NULL);//创建线程std::string GetCommand="";//从控制台获取用户输入信息while(true){Sleep(1);std::cin>>GetCommand;if(GetCommand=="exit")//用户输入“exit”就退出break;}g_bEndMusicThread=true;//通知子线程退出WaitForSingleObject(hThread,INFINITE);//线程结束之后再释放资源SendToMCI(Close);//关闭音乐文件return 0;}

你再次检查了一下程序逻辑......嗯,没问题,运行程序............(鸦雀无声)............

这到底是怎么回事?于是你Debug、百度、谷歌、雅虎............结果发现很多人都遇到这个问题,但是却没有一个明确可用的解决方法,眼看着自己的游戏每次重放音乐时都要卡,实在是难受,但又没法子,你顿时觉得天突然就阴沉了下来,太阳也不见了,世界一点也不美好。

情急之下,你产生了一个奇怪的想法,于是更改代码如下:

Eg3:

#include <Windows.h>#include <process.h>#include <string>#include <iostream>#pragma comment(lib,"winmm.lib")volatile bool g_bEndMusicThread;//让线程结束的标志//发送信息给MCIbool SendToMCI(std::string command){if(!mciSendString(command.c_str(),NULL,0,0))return true;return false;}//从MCI获取信息std::string GetFromMCI(std::string command){char message[20];mciSendString(command.c_str(),message,20,0);std::string str(message);return str;}//音乐线程的方法unsigned __stdcall ThreadPlayMusic(LPVOID lpParameter){std::string filepath="歌唱祖国.mid";std::string Open="OPEN "+filepath+" ALIAS MUSIC";std::string Close="CLOSE MUSIC";std::string Status="status MUSIC mode";std::string Play="PLAY MUSIC FROM 0";SendToMCI(Open);//打开音乐文件while(true){Sleep(1);std::string result=GetFromMCI(Status);//获取音乐状态if(result=="stopped")SendToMCI(Play);//如果音乐停止,重新播放if(g_bEndMusicThread==true)//接收主线程发送的退出信号break;}SendToMCI(Close);//关闭音乐文件return 0;}//入口函数int main(int argc, char *argv[]){g_bEndMusicThread=false;//初始化子线程标志位HANDLE hThread = (HANDLE)_beginthreadex(NULL,0,ThreadPlayMusic,NULL,0,NULL);//创建线程std::string GetCommand="";//从控制台获取用户输入信息while(true){Sleep(1);std::cin>>GetCommand;if(GetCommand=="exit")//用户输入“exit”就退出break;}g_bEndMusicThread=true;//通知子线程退出WaitForSingleObject(hThread,INFINITE);//线程结束之后再释放资源return 0;}

干脆把所有事情都塞给子线程干,主线程完全不理睬这让人失望的MCI了。

抱着试一试的心情,你运行了程序............音乐竟然播放了?!你真是又高兴又觉得奇怪,怎么回事儿?

就在此时,你想起了多线程的基础知识:

在默认情况下,一个进程中的多个线程之间,堆公有而栈私有。

于是你开始思考,难不成mciSendString进行的是栈操作?所以在主线程中打开的音乐文件,在子线程中是看不见的,因为子线程的栈里面压根没有该音乐文件信息。

总之,音乐确实播放了,主线程也不会在音乐重放时卡了,你顿时觉得舒畅了许多,看看窗外,天还是那么的蓝,太阳还是那么的灿烂,世界依然是那么美好。

------------------------------

总结:

如果想在多线程中使用mciSendString播放音乐,你必须保证所有的mciSendString方法调用都在一个线程中进行。

示例代码下载:

http://download.csdn.net/detail/daeba/8403545


后记:

虽然知道了多线程中使用mciSendString播放音乐的要点,但是问题依然存在,毕竟子线程不能直接与用户交互,用户的操作信息由主线程接收,还要实现主线程与子线程之间的信息传递才行,此外,用mciSendString播放音乐也存在一些特殊问题,这些问题将在下一篇中讨论。


0 0