MFC 声音的播放和录音的实现(一)

编程中经常会又到播放声音的操作,现总结一下在MFC中关于声音有关的问题。这一篇介绍一下播放声音的操作。下一篇介绍一下录音操作的实现。

(一)调用Windows API函数实现声音的播放

VC++ 中的多媒体动态连接库中提供了一组与音频设备有关的函数。利用这些函数可以方便地播放声音。最简单的播放声音方法就是在需要播放声音的地方直接调用WindowsAPI提供的两个声音播放函数:PlaySound和sndPlaySound,现在对这个两个函数的用法分别做一个小结:

1.PlaySound函数用法

PlaySound函数的声明为:
BOOL PlaySound(LPCSTR pszSound, HMODULE hmod,DWORD fdwSound);

参数说明:

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

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

3) 参数fdwSound是标志的组合,如表1-1所示。若成功则函数返回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

停止所有与调用任务有关的声音。若参数pszSoundNULL,就停止所有的声音,否则,停止pszSound指定的声音

SND_RESOURCE

pszSound参数是WAVE资源的标识符,这时要用到hmod参数

SND_SYNC

同步播放声音,在播放完后PlaySound函数才返回

1-1播放标志的含义

4) 举例:

1.4.1.直接播放本地存放的.wav格式的文件为例。播放系统开机的声音,示例代码:

PlaySound(_T("C:\\WINDOWS\\Media\\Windows XP 启动.wav"), NULLSND_FILENAME | SND_ASYNC);

注意:这里一定要注意需要添加头文件的支持,在对应的源文件中添加头文件的引用,函数sndPlaySound也必须添加,如下

//声音相关

#include <windows.h>

#include <mmsystem.h>

#pragma comment(lib"WINMM.LIB")

 

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

VC++的程序设计中,可以利用各种标准的资源,如位图,菜单,对话框等。同时VC++也允许用户自定义资源,因此我们可以将声音文件作为用户自定义资源加入程序资源文件中,经过编译连接生成EXE文件,实现无.WAV文件的声音播放。
  要实现作为资源的声音文件的播放,首先要在资源管理器中加入待播放的声音文件。前面介绍的扫雷程序中的爆炸声和点击声音都是通过这种方式实现的。具体步骤:打开资源视图界面,右键添加资源,初始界面中没有wav格式的选项(VS2008),选择 自定义 的选项,资源类型输入WAV,然后资源视图中添加了WAV的选项,右键点击它,添加资源,选择导入,在资源类型中选中Wave 文件(*.wav,选中本地存放的wave文件,打开,然后再属性中修改它的ID。最后在需要播放这个声音的地方调用这个函数(以扫雷爆炸声为例)

在鼠标左键点击到了有雷的方格后调用

PlaySound(MAKEINTRESOURCE(IDR_BOMB),AfxGetResourceHandle(), SND_ASYNC|SND_RESOURCE|SND_NODEFAULT);

IDR_BOMB为添加的爆炸声的ID 

关于这个函数在这种情况下的用法再补充一点:

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.播放声音文件:
  PlaySound(lpMemSound,AfxGetResourceHandle(),SND_MEMORY))
  5.释放资源句柄:
  FreeResource(hGlobalMem);

 

2.sndPlaySound函数用法

函数sndPlaySound的功能与PlaySound类似,但少了一个参数。

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

 

1) 除了不能指定资源名字外,参数lpszSoundPlaySound的是一样的。

2) 参数fuSound是如何播放声音的标志,可以是SND_ASYNCSND_LOOPSND_MEMORYSND_NODEFAULTSND_NOSTOPSND_SYNC的组合,这些标志的含义与PlaySound的一样,详见表格1-1

可以看出,sndPlaySound不能直接播放声音资源(上面介绍的在MFC添加自定义资源的那种方式)。要用该函数播放WAVE文件,按照1.4.1的方式即可

3) 举例说明

sndPlaySound(_T("C:\\WINDOWS\\Media\\Windows XP 启动.wav"),SND_ASYNC);

控制第一个参数为路径名即可。例如:

TCHAR szPath[MAX_PATH]; 

GetModuleFileName(NULLszPathMAX_PATH);

CString PathName(szPath);

//获取exe目录

CString PROGRAM_PATH = PathName.Left(PathName.ReverseFind(_T('\\')) + 1);

PROGRAM_PATH+=_T("\Sound\\BOMB.WAV");

sndPlaySound(PROGRAM_PATH,SND_ASYNC);


(二)选择播放本地的声音文件

 

上面的两种方法对于播放声音有很大的局限性:只能播放wav格式的音频文件,其他格式的音频文件都不支持。下面介绍的这种方法可以实现播放本地的任何格式的音频文件。注:这种方法是在网上搜集整理的,在扫雷程序中试过,有效。将扫雷程序中的代码作为示例,代码中已添加详细的注释,以供参考。

DWORD CMineSweepingView::GetInfo(DWORD item)  
{  
	//MCI接口对象的状态   
	MCI_STATUS_PARMS mcistatusparms;  
	//歌曲格式   
	mcistatusparms.dwCallback=(DWORD)GetSafeHwnd();  
	//待获取的项目   
	mcistatusparms.dwItem=item;  
	mcistatusparms.dwReturn=0;  
	//想多媒体设备发送指令,获取当前的状态参数   
	mciSendCommand(m_ulCount,MCI_STATUS,MCI_STATUS_ITEM,(DWORD)&mcistatusparms);  
	return mcistatusparms.dwReturn;   
}  

在播放声音的消息响应函数代码:

void CMineSweepingView::OnGamePlaymusic()
{

	CFileDialog filedlg(true);// 文件对话框处于打开状态   
	// TODO: Add your command handler code here   
	filedlg.m_ofn.Flags |=512;//可以选取多个曲目   
	filedlg.m_ofn.lpstrFilter=_T("All Support type\0*.*\0\0");//文件格式任意   
	POSITION pos=filedlg.GetStartPosition();//选取目标音频文件 
	CString aa=_T("");         
	if(filedlg.DoModal()==IDOK)//选定List控件中的添加位置,将文件添加在选定位置后边   
	{     
		while (pos != NULL)  
		{  
			aa=filedlg.GetNextPathName(pos);//将添加的音频文件路径存放在数组file中   

			UpdateData(false);            
		}  
	}  
	mciSendString(_T("close wave"), 0, 0, NULL);  
	mciSendCommand(m_ulCount,MCI_CLOSE,0,NULL);//向多媒体设备发送消息,关闭多媒体设//备   
	MCI_OPEN_PARMS mciopenparms;//打开   
	MCI_PLAY_PARMS mciplayparms;//播放   
	mciopenparms.lpstrElementName=aa;//播放路径   

	mciopenparms.lpstrDeviceType=NULL;//文件类型   
	mciSendCommand(0,MCI_OPEN,MCI_DEVTYPE_WAVEFORM_AUDIO,(DWORD)(LPVOID)&mciopenparms);//向MCI设备发送命令消息,包含歌曲文件的类型和路径   
	m_ulCount=mciopenparms.wDeviceID;//多媒体设备类型编号   
	mciplayparms.dwCallback=(DWORD)GetSafeHwnd();//歌曲播放支持类型   
	DWORD cdlen=0;//某个音频文件的总时间长度   
	cdlen=GetInfo(MCI_STATUS_LENGTH);    //得到曲目长度
	DWORD cdfrom=0;
	DWORD cdto=0;//歌曲的起点和终点 
	cdto=MCI_MAKE_HMS(MCI_HMS_HOUR(cdlen),MCI_HMS_MINUTE(cdlen),MCI_HMS_SECOND(cdlen));//设置播放完毕某音频文件需要的时间n时n分n秒     
	mciplayparms.dwFrom=cdfrom;  
	mciplayparms.dwTo=cdto;  
	mciSendCommand(m_ulCount,MCI_PLAY,MCI_TO|MCI_FROM,(DWORD)(LPVOID)& mciplayparms);//向多媒体设备发送播放文件命令   
	UpdateData(false);  
}
第二种方法用法比第一种更广泛。用这种方式如果需要 停止播放声音,调用函数:

mciSendCommand (m_ulCount, MCI_CLOSE, NULL, NULL); 

即可。

你可能感兴趣的:(mfc,多媒体,录音,播放,声音)