在MFC中添加多个音乐

时间:2021-12-24 19:43:17

花了一天的时间,终于办了一件令人高兴的事——完成了“泡泡龙”的背景音乐和音效,值得记录一下。

    首先,若只有一个音乐WAV,则相当简单,两句话搞定(有两种方法):

【添加头文件:#include <mmsystem.h>】
【#pragma comment(lib, "winmm.lib")】

方法1、sndPlaySound("music.wav",SND_ASYNC|SND_LOOP);//播放
       sndPlaySound(NULL,SND_ASYNC);//停止播放

方法2、PlaySound("music.wav",NULL,SND_ASYNC|SND_LOOP);//播放
       PlaySound(NULL,NULL,SND_ASYNC);//停止播放
   

    但是,我要达到的效果是:有多个按键音效和背景音乐,多个背景音乐供用户选择,而且,播放音效时不影响背景音乐的播放,即多个音乐可以同时播放。然而用上述办法资源是独占的,只能播放一个音乐。因而我请张师兄帮忙,奋战又一下午,终于找到了办法:

     mciSendString真是个好东西,可以播放任何多个音乐,格式如下的一句话,哪要播放哪就ok了。

mciSendString("play click_sound.wav","",0,NULL);//播放“点击”声音

     可是,有遇到新问题了,我要实现背景音乐的循环播放,(PlaySound就能循环播放,可事实证明我这用不了)网上和MSDN上本来都说加了repeat参数就可以了的,但是本人尝试了,不得行滴!故而又麻烦张师兄,他就是厉害,不一会儿就又找到办法了:mciSendCommand加自定义消息,当播放发结束就发送自定义的消息促发它又重头播放,我还没理解透彻,不知是不是这样理解的,先将这段代码附上:

void CPaopaolongView::OnInitialUpdate()
{
 CView::OnInitialUpdate();

 MCI_OPEN_PARMS mciOpen;
 mciOpen.lpstrDeviceType = "avivideo";
 mciOpen.lpstrElementName = m_strMusicName;
 mciSendCommand(0, MCI_OPEN, MCI_OPEN_ELEMENT, (DWORD)&mciOpen);
 m_DeviceID = mciOpen.wDeviceID;
 MCI_PLAY_PARMS mciPlayParms;
 //循环播放
 mciPlayParms.dwCallback = (DWORD)this->m_hWnd;
 mciSendCommand(m_DeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)(LPVOID) &mciPlayParms);
}


void CPaopaolongView::OnMciNotify(WPARAM wParam,LPARAM lParam) //背景音乐循环播放
{
    switch (wParam)
    {
    case MCI_NOTIFY_SUCCESSFUL:
        {
   MCI_PLAY_PARMS PlayParms;
            PlayParms.dwFrom = 0;
            PlayParms.dwCallback = (DWORD)m_hWnd;
            mciSendCommand(m_DeviceID, MCI_SEEK, MCI_SEEK_TO_START, NULL);
            mciSendCommand(m_DeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)(LPVOID)&PlayParms);
        }
        break; 
        default:
        break;
    }
}



MFC中播放WAV文件的方法


一、使用PlaySound()函数
该函数的原型的是:
BOOL PlaySound(
     LPCSTR pszSound,  
     HMODULE hmod,     
     DWORD fdwSound    
)

其中,参数pszSound指定要播放文件的文件名,若该参数为NULL,则停止正在播放的声音;
        参数hmod说明资源的句柄;
        参数fdwSound则指定该命令的标志,也就是说明播放WAV文件的方式,可以取以下值:

标志

说明

SND_ASYNC

异步播放声音

SND_SYNC

同步播放声音(默认)

SND_NODEFAULT

pszSound指定的文件找不到,不使用默认声音

SND_MEMORY

pszSound指向的一个内存文件

SND_LOOP

循环播放声音,直到下一次调用PlaySound()pszSound参数为NULL,此标志要与SND_ASYNC一起使用

SND_NOSTOP

不停止当前正在播放的任何声音

SND_RESOURCE

表示pszSound是一个资源的标识符,而hmod则是包含这个资源的一个实例

SND_FILENAME

表示pszSound是一个文件名

SND_ALIAS_ID

表示pszSound预定义的声音标识符

SND_ALIAS

pszSound参数是在注册表或win.ini中定义的一个系统事件,不可与SND_FILENAME SND_RESOURCE同时使用。

SND_NOWAIT

如果设备正忙,则立即返回而不播放指定的声音

例:
//播放call2.wav
PlaySound("call2.wav",NULL,SND_FILENAME | SND_ASYNC | SND_LOOP);
//关闭声音
PlaySound(NULL,NULL,SND_FILENAME | SND_ASYNC | SND_LOOP);


二、使用sndPlaySound()函数
该函数的原型的是:
BOOL sndPlaySound(
     LPCSTR lpszSound,  
     UINT fuSound       
)

其中,参数lpszSound指定要播放的文件名,若为NULL,则停止正在播放的声音
         参数fuSound指定播放的方式,可取以下值:
SND_ASYNC,SND_SYNC,SND_LOOP,SND_MEMORY,SND_DODEFAULT,SND_NOSTOP
各个标志的含义同上。

例:
//播放call1.wav
sndPlaySound("call1.wav",SND_ASYNC | SND_LOOP); 
//关闭声音
sndPlaySound(NULL,SND_ASYNC | SND_LOOP);


       以上两个函数使用简洁、方便,但有两个缺陷,即:
(1) 整个声音必须放入可用的物理内存中,因此,当文件大小小于100K时使用这两个函数,而在大
        于100K时,一般使用MCI方式
(2) 该声音用的必须是已安装的音频驱动程序之一所支持的数据格式。
    
三、使用MCI命令
      使用MCI命令实际上也就是利用以下几个相关的API函数完成播放声音的功能:
      mciSendCommand()     //向MCI设备发送命令消息
      mciSendString()      //向MCI设备发送命令字符串
      mciGetErrorString() //获取MCI函数的返回值的文本描述信息

1.     mciSendCommand()函数
函数原型:
MCIERROR mciSendCommand(
     MCIDEVICEID IDDevice,   
     UINT           uMsg,       
     DWORD          fdwCommand, 
     DWORD_PTR      dwParam     
);


其中,参数IDDevice是接收命令消息的MCI设备的标识,该参数不与命令消息MCI_OPEN同时使用
         参数uMsg是命令消息。具体信息可参考MSDN。
         参数fdwCommand用来设置命令消息的标志
         参数dwParam指向一个包含命令消息参数的结构体
若函数执行成功返回值为0,否则为错误代码。错误信息可用mciGetErrorString()函数得到。
例:
/******************************
打开音频设备
*******************************/
MCI_OPEN_PARMS mciOpenParms;
DWORD dwResult;
mciOpenParms.lpstrDeviceType=(LPSTR)MCI_DEVTYPE_WAVEFORM_AUDIO;
dwResult=mciSendCommand(NULL,MCI_OPEN,MCI_OPEN_TYPE | MCI_OPEN_TYPE_ID | MCI_WAIT,(DWORD)(LPVOID)&mciOpenParms);

/******************************
关闭音频设备
*******************************/
mciSendCommand(m_nDeviceID,MCI_CLOSE,NULL,NULL);

/****************************
播放WAV文件
****************************/
MCI_OPEN_PARMS mciOpenParms;
memset(&mciOpenParms,0,sizeof(MCI_OPEN_PARMS));
//设置要播放的WAV文件名
mciOpenParms.lpstrElementName=pFileName;
//打开WAV文件
DWORD dwResult=mciSendCommand(m_nDeviceID,MCI_OPEN,MCI_OPEN_ELEMENT,(DWORD)(LPVOID)&mciOpenParms);


2. mciSendString()函数
函数原型:
MCIERROR mciSendString(
     LPCTSTR lpszCommand,  
     LPTSTR lpszReturnString,  
     UINT cchReturn,       
     HANDLE hwndCallback   
);

其中,参数lpszCommand是一个以'/0'结尾的命令字符串,相关字符串与mciSendCommand()函数中参数uMsg的消息相对应,详见

MSDN
         参数lpszReturnString指向一个用来存储返回信息的缓冲区。若不需要返回信息,可设为NULL
         参数cchReturn是用来存储返回信息的lpszReturnString的字节数
         若在命令字符串中指定了"notify"(通告),则参数hwndCallback指向一个回调窗口。
返回值与mciSendCommand()函数相似

 

3. mciGetErrorString()函数
函数原型:
BOOL mciGetErrorString(
     DWORD fdwError,        
     LPTSTR lpszErrorText,  
     UINT cchErrorText      
);

其中,参数fdwError是mciSendCommand()函数或mciSendString()函数返回的错误码
         参数lpszErrorText指向一个缓冲区,该缓冲区接收一个以'/0'结尾的错误描述字符串
         参数cchErrorText用来存储返回信息的lpszErrorText的字节数

例:
char szErrorMsg[MAXERRORLENGTH];
//获取错误描述信息
if(!mciGetErrorString(dwError,szErrorMsg,sizeof(szErrorMsg)))
strcpy(szErrorMsg,"Unknown Error!");

 

另外,需要注意的是:要包含一个头文件"Mmsystem.h",在链接的时候也要使用一个库文件"Winmm.lib"

MFC中的几种播放声音的方法  


一.播放声音文件的简单方法
  在VC++ 中的多媒体动态连接库中提供了一组与音频设备有关的函数。利用这些函数可以方便地播放声音。最简单的播放声音方法就是直接调用VC++中提供的声音播放函数BOOL sndPlaySound ( LPCSTR lpszSound,UINT fuSound ); 或BOOL PlaySound( LPCSTR lpszSound, HMODULE hmod, DWORD fuSound );其中参数lpszSound是需要播放声音的.WAV文件的路径和文件名, hmod在这里为NULL,fuSound是播放声音的标志,详细说明请参考VC++中的帮助。例如播放C:\sound\music.wav可以用sndPlaySound ("c:\\sound\\music.wav",SND_ASYNC);或PlaySound("c:\\sound\\music.wav",NULL, SND_ASYNC|SND_NODEFAULT );如果没有找到music.wav文件,第一种格式将播放系统默认的声音,第二种格式不会播放系统默认的声音。

二.将声音文件加入到程序中
  在VC++的程序设计中,可以利用各种标准的资源,如位图,菜单,对话框等。同时VC++也允许用户自定义资源,因此我们可以将声音文件作为用户自定义资源加入程序资源文件中,经过编译连接生成EXE文件,实现无.WAV文件的声音播放。
  要实现作为资源的声音文件的播放,首先要在资源管理器中加入待播放的声音文件(实现过程并不复杂,这里不在叙述)。假设生成的声音文件资源标识符为IDR_WAVE1。在播放时只需要调用下面的语句:
  PlaySound(MAKEINTRESOURCE(IDR_WAVE1),AfxGetResourceHandle(),   SND_ASYNC|SND_RESOURCE|SND_NODEFAULT|SND_LOOP);
  其中MAKEINTRESOURCE()宏将整数资源标识符转变为字符串,AfxGetResourceHandle()函数返回包含资源的模块句柄,
SND_RESOURCE是必须的标志。
  作为资源的声音文件的第二种播放方法是把资源读入内存后作为内存数据播放。具体步骤入下:
  1.获得包含资源的模块句柄:
  HMODULE hmod=AfxGetResourceHandle();
  2.检索资源块信息:
  HRSRC hSndResource=FindResource(hmod,MAKEINTRESOURCE(IDR_WAVE1),_T("WAVE"));
  3. 装载资源数据并加锁:
  HGLOBAL hGlobalMem=LoadResource(hmod,hSndResource);
LPCTSTR lpMemSound=(LPCSTR)LockResource(hGlobalMem);
  4.播放声音文件:
  sndPlaySound(lpMemSound,SND_MEMORY));
  5.释放资源句柄:
  FreeResource(hGlobalMem);

第三种方法是用PlaySound播放系统声音,Windows启动的声音是由SystemStart定义的系统声音,因此可以用下面的方法播放启动声音:
PlaySound("SystemStart",NULL,SND_ALIAS|SND_ASYNC);
函数sndPlaySound的功能与PlaySound类似,但少了一个参数。函数的声明为:
BOOL sndPlaySound(LPCSTR lpszSound, UINT fuSound);
除了不能指定资源名字外,参数lpszSound与PlaySound的是一样的。参数fuSound是如何播放声音的标志,可以是SND_ASYNC、SND_LOOP、SND_MEMORY、SND_NODEFAULT、SND_NOSTOP和SND_SYNC的组合,这些标志的含义与PlaySound的一样。
可以看出,sndPlaySound不能直接播放声音资源。要用该函数播放WAVE文件,可按下面的方式调用

 

=================================================================================

一、PlaySound函数的声明为:

BOOL PlaySound(LPCSTR pszSound,HMODULE hmod,DWORD fdwSound);

参数说明:

pszSound:是指定了要播放声音的字符串,该参数可以是WAVE文件的名字,或是WAVE资源的名字,或是内存中声音数据的指针,或是在系统注册表WIN.INI中定义的系统事件声音.如果该参数为NULL则停止正在播放的声音.

hmod:是应用程序的实例句柄,当播放WAV资源时要用到该参数,否则它必须为NULL.

fdwSound: 是标志的组合,如下表所示。若成功则函数返回TRUE,否则返回FALSE。
二、播放标志以及含义:

SND_APPLICATION
用应用程序指定的关联来播放声音。

SND_ALIAS
pszSound参数指定了注册表或WIN.INI中的系统事件的别名。

SND_ALIAS_ID
pszSound参数指定了预定义的声音标识符。

SND_ASYNC
用异步方式播放声音,PlaySound函数在开始播放后立即返回。

SND_FILENAME
pszSound参数指定了WAVE文件名。

SND_LOOP
重复播放声音,必须与SND_ASYNC标志一块使用。

SND_MEMORY
播放载入到内存中的声音,此时pszSound是指向声音数据的指针。

SND_NODEFAULT
不播放缺省声音,若无此标志,则PlaySound在没找到声音时会播放缺省声音。

SND_NOSTOP
PlaySound不打断原来的声音播出并立即返回FALSE。

SND_NOWAIT
如果驱动程序正忙则函数就不播放声音并立即返回。
SND_PURGE
停止所有与调用任务有关的声音。若参数pszSound为NULL,就停止所有的声音,否则,停止pszSound指定的声音。
SND_RESOURCE
pszSound参数是WAVE资源的标识符,这时要用到hmod参数。
SND_SYNC
同步播放声音,在播放完后PlaySound函数才返回。

三、函数使用方法及代码:

注意:在使用函数前要加入:

#include "mmsystem.h"//导入声音头文件

#pragma comment(lib,"winmm.lib")//导入声音头文件库

1、直接播出声音文件:

PlaySound("c:\\win95\\media\\The Microsoft Sound.wav", NULL, SND_FILENAME | SND_ASYNC);
注意:参数中的路径使用两个连续的反斜杠转义代表一个反斜杠。

2、把声音文件加入到资源中,然后从资源中播放声音:

Visual C++支持WAVE型资源,用户在资源视图中单击鼠标右键并选择Import命令,然后在文件选择对话框中选择The Microsoft Sound.wav文件,则该文件就会被加入到WAVE资源中。假定声音资源的ID为IDR_STARTWIN,则下面的调用同样会输出启动声音:
PlaySound((LPCTSTR)IDR_STARTWIN, AfxGetInstanceHandle(), SND_RESOURCE | SND_ASYNC);

或:

PlaySound(MAKEINTRESOURCE(IDR_WAVE2),AfxGetResourceHandle(),SND_ASYNC|SND_RESOURCE|SND_NODEFAULT|SND_LOOP);//将声音文件写入到程序中
3、用PlaySound播放系统声音:

Windows启动的声音是由SystemStart定义的系统声音,因此可以用下面的方法播放启动声音:
PlaySound("SystemStart",NULL,SND_ALIAS|SND_ASYNC);
            

sndPlaySound函数的声明为:
BOOL sndPlaySound(LPCSTR lpszSound, UINT fuSound);

用法:

除了不能指定资源名字外,参数lpszSound与PlaySound的是一样的。参数fuSound是如何播放声音的标志,可以是SND_ASYNC、SND_LOOP、SND_MEMORY、SND_NODEFAULT、SND_NOSTOP和SND_SYNC的组合,这些标志的含义与PlaySound的一样。
可以看出,sndPlaySound不能直接播放声音资源。要用该函数播放WAVE文件,可按下面的方式调用:
sndPlaySound("MYSOUND.WAV",SND_ASYNC);