1实验7波形音频播放器实验目的1.了解Wave文件格式,并学习Windows下用MCI函数播放Wave文件。2.熟悉多媒体开发工具——VisualC++。3.制作波形音频播放器。其运行界面如下图所示:要求此播放器具有打开、播放、录制及保存波形音频文件的功能。预备知识1.VC6编程方法,MCI编程方法。2.Wave文件的结构用.wav为扩展名的文件格式称为波形文件格式(WAVEFileFormat),它是一种资源交换文件格式(ResourceInterchangeFileFormat,RIFF),RIFF格式是面向部分(chunk)的,一个RIFF文件是由一个或多个部分组成的,其中每一个部分都指向下一个部分。下图是一个RIFF文件结构的示意:波形文件格式支持存储各种采样频率和样本精度的声音数据,并支持声音数据的压缩。一个WAVE文件至少包含三个块RIFF块是其中最大的,整个WAVE文件就是一个RIFF块。Cksize紧跟在“RIFF”CKID之后出现,它包含一个值,等于文件的大小减去8个字节,这8个字节用来存储RIFF的CKID和CKSIZE。第二和第三块称为子块,包含在RIFF块之中。这些块的第一个块是“fmt”2块,包含PCMWAVEFORMAT结构所需要的信息;第二个块“data”块紧跟在“fmt”之后,包含所有的数据波形。RIFF的CKSIZE等于“fmt”块和“data”块所占用的字节之和。为了读写RIFF文件,用户使用为多媒体块信息准备的叫MMCKINFO的标准的数据结构。在VC中这个结构定义为:typedefsruct{FOURCCckid;DWORDcksize;FOURCCfccType;DWORDdwDataOffset;DWORDdwFlags;}MMCKINFO;设计多媒体程序,关键是对多种多媒体设备的控制和使用,在Windows操作系统中,对多媒体设备进行控制主要有三种方法:第一种方法是使用微软公司窗口系统中对多媒体支持的MCI,即媒体控制接口,MCI是多媒体设备和多媒体应用软件之间进行设备无关的沟通的桥梁。在VB和VC中MCI都得到了很好的支持;第二种方法,通过调用Windows的API(应用程序接口)多媒体相关函数实现媒体控制;第三种方法是使用OLE(ObjectLinking&Embedding),即对象嵌入和链接技术,它为不同数据之间共享数据和资源提供了有利的手段。实验步骤:1.用VC建立应用程序框架:a)打开文件菜单,点击新建,在弹出的对话框中选中MFCAppWizard(exe)后,选好你文件所需存储的目录,然后在工程中填入工程文件名,如下图所示:点击确定按钮在弹出的对话框中,按下图进行设置后,点击“完成”。3b)编制应用程序界面:各控件的属性如下表所示:控件ID标题控件ID标题GroupBoxIDC_STATIC波形音频信息StaticIDC_RESOLUTIONStaticIDC_STATIC波形文件:StaticIDC_WAVE_LENGTHStaticIDC_STATIC声道:ButtonIDC_OPEN打开StaticIDC_STATIC采样率:ButtonIDC_PLAY播放StaticIDC_STATIC音频长度:ButtonIDC_RECORD录制StaticIDC_WAVE_NAMEButtonIDC_SAVE保存StaticIDC_CHANNELButtonIDC_STOP停止StaticIDC_SAMPLE_RATEPictureIDC_WAVE_GRAPHc)点击查看建立类向导,在MessageMaps中建立相应的消息函数OnOpen(),OnPlay(),OnRecord(),OnSave(),OnStop(),点击确定。-2.建立处理波形音频的类CWaveAudio:工程添加工程ComponentsandControls,选中Wave4Audio.ogx文件插入,可重用类CWaveAudio便添加到工程中。在CWavePlayerDlg类的头文件中将CWaveAudio的头文件包含进来,即#include“media.h”。3.加入所需的功能:a)在CWavePlayerDlg类中加入以下成员变量CStringpath;//文件目录CWaveAudiom_Wave;//可重用类的对象intINFO_WAVEWIDE;//绘图框的宽度intINFO_WAVEDEEP;//绘图框的高度boolm_bOpen;//判断是否有波形音频文件打开右键点击Class中的CWavePlayerDlg,选中AddNumberFunction,如下所示,将绘图函数voidDrawWave(HDChdc,unsignedintx,unsignedinty,char*path)加到CWavePlayerDlg中。b)在BOOLCWavePlayerDlg::OnInitDialog()中进行初始化:CWnd*m_pWnd;m_pWnd=GetDlgItem(IDC_PLAY);m_pWnd-EnableWindow(false);m_pWnd=GetDlgItem(IDC_SAVE);m_pWnd-EnableWindow(false);c)在OnOpen(),OnPlay(),OnRecord(),OnSave(),OnStop()中添加代码如下:voidCWavePlayerDlg::OnOpen(){//TODO:AddyourcontrolnotificationhandlercodehereCStringFilter,str;Filter=波形音频文件(*.WAV)|*.WAV||;CFileDialogFileDlg(true,NULL,NULL,OFN_HIDEREADONLY,Filter);if(FileDlg.DoModal()==IDOK){if(!m_Wave.Load(FileDlg.GetFileName())){MessageBox(不能打开文件!,错误,MB_OK|MB_ICONSTOP);}CWnd*m_pWnd;m_pWnd=GetDlgItem(IDC_PLAY);m_pWnd-EnableWindow(true);}5path=FileDlg.GetPathName();SetDlgItemText(IDC_WAVE_NAME,FileDlg.GetFileName());str.Format(%5.3f,m_Wave.GetSampleRate()/1000);str+=_T(kHZ);SetDlgItemText(IDC_SAMPLE_RATE,str);str.Empty();switch(m_Wave.GetChannel()){case1:str=单声道;break;case2:str=立体声;break;}SetDlgItemText(IDC_CHANNEL,str);str.Empty();str.Format(%d,m_Wave.GetResolution());str+=位;SetDlgItemText(IDC_RESOLUTION,str);str.Empty();str.Format(%2.2u:%02.2f:%02.2u,m_Wave.GetWaveLength()/1000/60,m_Wave.GetWaveLength()/1000,m_Wave.GetWaveLength()/1000/3600);SetDlgItemText(IDC_WAVE_LENGTH,str);m_bOpen=true;}voidCWavePlayerDlg::OnPlay(){//TODO:Addyourcontrolnotificationhandlercodeherem_Wave.Play();}voidCWavePlayerDlg::OnRecord(){//TODO:AddyourcontrolnotificationhandlercodehereCWnd*m_pWnd;m_pWnd=GetDlgItem(IDC_PLAY);m_pWnd-EnableWindow(false);m_pWnd=GetDlgItem(IDC_OPEN);m_pWnd-EnableWindow(false);6m_pWnd=GetDlgItem(IDC_SAVE);m_pWnd-EnableWindow(false);m_Wave.Record();}voidCWavePlayerDlg::OnSave(){//TODO:AddyourcontrolnotificationhandlercodehereCStringFilter;Filter=WaveFile(*.WAV)|*.WAV||;CFileDialogFileDlg(false,NULL,NULL,OFN_OVERWRITEPROMPT,Filter);FileDlg.m_ofn.lpstrDefExt=wav;if(FileDlg.DoModal()==IDOK)m_Wave.Save(FileDlg.GetPathName());}voidCWavePlayerDlg::OnStop(){//TODO:AddyourcontrolnotificationhandlercodehereCWnd*m_pWnd;m_pWnd=GetDlgItem(IDC_PLAY);m_pWnd-EnableWindow(true);m_pWnd=GetDlgItem(IDC_OPEN);m_pWnd-EnableWindow(true);m_pWnd=GetDlgItem(IDC_SAVE);m_pWnd-EnableWindow(true);m_Wave.Stop();}现在可以编译运行一下,可以发现你已经实现了绝大多数的功能。d)现在,我们将绘图功能添加进去,由于对话框的变动都会触发调用OnPaint()函数重绘客户区,我们在OnPaint()中加入以下代码:voidCWavePlayerDlg::OnPaint(){if(IsIconic()){……}else{CDialog::OnPaint();if(m_bOpen){7CWnd*m_pWnd;CRectrect;m_pWnd=GetDlgItem(IDC_WAVE_GRAPH);m_pWnd-GetClientRect(rect);INFO_WAVEWIDE=rect.Width();INFO_WAVEDEEP=rect.Height();CClientDCdc(m_pWnd);DrawWave(dc.m_hDC,rect.left,rect.top,(char*)path.operatorLPCTSTR());}}}我们就在OnPaint()中调用了DrawWave函数绘制波形,DrawWave函数的代码如下:voidCWavePlayerDlg::DrawWave(HDChdc,unsignedintx,unsignedinty,char*path){HMMIOh;MMCKINFOmmParent,mmSub;//MMCKINFO结构中包含了有关部分的信息GLOBALHANDLEgh;PCMWAVEFORMATwaveformat;//fmt部分结构char*p;unsignedlongnextsample;longafactor;unsignedinti,n,amp;int*ip;HPENOldPen=(HPEN)SelectObject(hdc,GetStockObject(BLACK_PEN));HBRUSHOldBrush=(HBRUSH)SelectObject(hdc,GetStockObject(WHITE_BRUSH));Rectangle(hdc,x,y,x+INFO_WAVEWIDE,y+INFO_WAVEDEEP);if((h=mmioOpen(path,NULL,MMIO_RE