音频处理技术
11.2.1 播放应用程序中的WAVE资源
在播放WAVE资源时,首先调用FindResource函数搜索指定的音频资源,然后调用LoadResource函数装载资源到存储器,再调用LockResource函数锁定资源句柄,使用sndPlaySound函数播放WAVE资源,最后调用FreeResource函数释放资源句柄。
例如:
播放WAVE资源的程序设计步骤如下。
ch1104实例位置:mr\11\sl\04
(1)创建一个基于对话框的应用程序。
(2)在工作区窗口选择ResourceView视图,右键单击任意节点,在快捷菜单中选择Insert菜单项,向对话框中导入一个WAVE资源。
(3)向对话框中添加一个按钮和一个群组框控件。
(4)在对话框的源文件导入多媒体库的头文件引用及多媒体静态库的链接。代码如下:
#include <MMSystem.h>
#pragma comment(lib, "winmm.lib")
(5)处理“播放”按钮的单击事件,播放WAVE资源。代码如下:
void CWAVEDlg::OnPlaybut()
{
HINSTANCE hInstance = AfxGetInstanceHandle();
CString str;
str.Format("#%d",IDR_WAVE1);
HRSRC hRsrc = FindResource(hInstance,str,"WAVE");
HGLOBAL hGlobal = LoadResource(hInstance,hRsrc);
LPCTSTR lPctstr = (LPCTSTR)LockResource(hGlobal);
sndPlaySound(lPctstr,SND_MEMORY|SND_SYNC);
FreeResource(hGlobal);
}
(6)程序运行结果如图11.4所示。
11.2.2 播放WAVE文件
WAVE音频文件是比较常用的音频文件,对于音频的播放可以有多种方法,下面使用mciSendCommand函数播放WAVE文件。
例如:
播放WAVE文件的应用程序设计步骤如下。
ch1105实例位置:mr\11\sl\05
(1)创建一个基于对话框的应用程序。
(2)向对话框中添加一个群组框控件、一个编辑框控件和两个按钮控件
(3)在对话框的源文件WavePlayDlg.cpp中加入多媒体库的头文件引用及多媒体静态库的链接。
#include <MMSystem.h>
#pragma comment(lib, "winmm.lib")
(4)在函数OnInitDialog中将编辑框控件设置为不可用。代码如下:
BOOL CWavePlayDlg::OnInitDialog()
{
……//此处代码省略
GetDlgItem(IDC_PATH)->EnableWindow(false);
return TRUE;
}
(5)处理“选择文件” 按钮的单击事件,实现添加将要播放文件的路径。代码如下:
void CWavePlayDlg::OnAddpath()
{
CFileDialog file(TRUE,NULL,NULL,NULL,"文件(*.wav)|*.wav||");
if(file.DoModal()==IDOK)
{
CString strname=file.GetPathName();
GetDlgItem(IDC_PATH)->SetWindowText(strname);
}
}
(6)处理“播放” 按钮的单击事件,使用mciSendCommand函数向音频设备发送打开设备、设置播放参数、播放文件等指令。代码如下:
void CWavePlayDlg::OnPlay()
{
CString tmp;
GetDlgItem(IDC_PATH)->GetWindowText(tmp);
if(tmp.IsEmpty())
{
MessageBox("清选择播放文件");
return;
}
MCIDEVICEID m_nDeviceID;
MCIDEVICEID m_nElementID;
MCI_OPEN_PARMS mciOpenParms;
mciOpenParms.lpstrDeviceType=(LPSTR)MCI_DEVTYPE_WAVEFORM_AUDIO;
mciSendCommand(NULL,MCI_OPEN,MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID|MCI_WAIT,
(DWORD)(LPVOID)&mciOpenParms);
m_nDeviceID=mciOpenParms.wDeviceID;
MCI_OPEN_PARMS mciOpen;
memset(&mciOpen,0,sizeof(MCI_OPEN_PARMS));
mciOpen.lpstrElementName=tmp;
mciSendCommand(m_nDeviceID,MCI_OPEN,MCI_OPEN_ELEMENT,(DWORD)(LPVOID)&mciOpen);
m_nElementID=mciOpen.wDeviceID;
MCI_PLAY_PARMS mciPlay;
mciPlay.dwCallback=(DWORD)this->GetSafeHwnd();
if(mciSendCommand(m_nElementID,MCI_PLAY,MCI_NOTIFY,
(DWORD)(LPVOID)&mciPlay)!=MMSYSERR_ERROR)
{
mciSendCommand(m_nDeviceID, MCI_CLOSE, 0, NULL);
}
}
(7)通过类向导添加关闭窗体消息的实现函数DestroyWindow,在该函数中实现关闭音频设备。
BOOL CWavePlayDlg::DestroyWindow()
{
mciSendCommand(m_nDeviceID, MCI_CLOSE, 0, NULL);
return CDialog::DestroyWindow();
}
(8)程序运行效果如图11.5所示。
图11.5 WAVE文件播放
11.2.3 音量大小控制
音量控制是多媒体应用的主要部分,在音频采集方面可以起到辅助控制录制效果的作用。使用MCI(The Media Control Interface)接口可以进行音量控制。系统提供了一个音量控制的程序,通过该程序可以控制不同类型音量,音量控制程序运行如图11.6所示。
其中,Volume表示系统总音量,Wave表示波形音量,SW Synth表示SW设备音量,CD Player表示CD控制线,Line In表示磁带设备音量,Microphone表示麦克风音量。
图11.6 音量控制程序
为了实现音量的控制,微软提供了一些与Mixer(混音器)相关的API函数。在控制系统总音量时,首先通过mixerOpen函数打开混音设备,然后通过mixerGetLineInfo函数获取控制线信息取得设备ID号并通过mixerGetLineControls函数得到控制线的控制,最后通过mixerGetControlDetails得到具体的音量信息,通过mixerSetControlDetails函数来设置音量。
(1)mixerOpen函数用于打开指定的混音器设备,并确保在应用程序关闭之前设备不被移除。
语法:
MMRESULT mixerOpen( LPHMIXER phmx, UINT uMxld, DWORD dwCallback, DWORD dwlnstance, DWORD fdwOpen );
mixerOpen函数语法中的参数说明如表11.5所示。
表11.5 mixerOpen函数语法中的参数说明
参 数 名 称 |
描 述 |
phmx |
返回已打开的混音设备的标识句柄 |
uMxld |
指定要打开的混音器设备标识 |
dwCallback |
指定调用窗口句柄 |
dwlnstance |
指定调用实例句柄 |
fdwOpen |
设备打开标志 |
(2)mixerGetLineInfo函数用于获取混音器设备指定的线路信息。
语法:
MMRESULT mixerGetLinelnfo( HMIXEROBJ hmxobj, LPMIXERLINE pmxl, DWORD fdwlnfo );
hmxobj:控制指定音频线路的混音器设备对象句柄。
pmxl:MIXERLINE结构对象。
fdwlnfo:指定要获取的信息标志。
(3)mixerGetLineControls函数用于获取关联音频线路的一个或多个控制器。
语法:
MMRESULT mixerGetLineControls ( HMIXEROBJ hmxobj, LPMIXERLINE CONTROLS pmxlc, DWORD fdwControls );
hmxobj:指定要查询的混音器设备对象句柄。
pmxlc:LPMIXERLINE CONTROLS结构对象。
fdwControl:指定要获取的信息标志。
(4)mixerGetControlDetails函数用于获取指定控制器的详细信息。
语法:
MMRESULT mixerGetControlDetails ( HMIXEROBJ hmxobj, LPMIXER CONTROLDETAILS pmxcd, DWORD fdwDetails );
hmxobj:指定要查询的混音器设备对象句柄。
pmxcd:LPMIXER CONTROLDETAILS结构对象。
fdwDetails:指定要获取的信息标志。
(5)mixerSetControlDetails函数用于设置指定控制器的详细信息。
语法:
MMRESULT mixerSetControlDetails ( HMIXEROBJ hmxobj, LPMIXER CONTROLDETAILS pmxcd, DWORD fdwDetails );
hmxobj:指定要查询的混音器设备对象句柄。
pmxcd:LPMIXER CONTROLDETAILS结构对象。
fdwDetails:指定要获取的信息标志。
下面通过实例来介绍如何控制音量大小。
例如:
控制音量大小的应用程序设计步骤如下。
ch1106实例位置:mr\11\sl\06
(1)新建MFC对话框工程,工程名设置为“ControlSound”。
(2)在对话框上添加一个滑标控件,图片控件和两个静态文本控件。
(3)通过类向导为滑标控件添加成员变量m_control。
(4)在实现文件ControlSoundDlg.cpp中加入多媒体库的头文件引用及多媒体静态库的链接。
#include<mmsystem.h>
#pragma comment(lib,"winmm.lib")
(5)在头文件中加入类成员变量的定义。
HMIXER m_hMixer;
MIXERCAPS m_mxcaps;
DWORD m_curvalue;
DWORD m_controlid;
(6)在对话框初始化工程中可以先获取音量的大小。
BOOL CControlSoundDlg::OnInitDialog()
{
……//此处代码省略
MIXERLINE mxl;
MIXERCONTROL mxc;
MIXERLINECONTROLS mxlc;
mixerOpen(&m_hMixer,0,(DWORD)this->GetSafeHwnd(),
NULL,MIXER_OBJECTF_MIXER | CALLBACK_WINDOW);
mixerGetDevCaps((UINT)m_hMixer, &m_mxcaps, sizeof(MIXERCAPS));
////////////////////////////////////////////////////////////////////
mxl.cbStruct = sizeof(MIXERLINE);
mxl.dwComponentType =MIXERLINE_COMPONENTTYPE_DST_SPEAKERS;
mixerGetLineInfo((HMIXEROBJ)m_hMixer,&mxl,
MIXER_OBJECTF_HMIXER|MIXER_GETLINEINFOF_COMPONENTTYPE);
/////////////////////////////////////////////////////////////////////////
mxlc.cbStruct=sizeof(MIXERLINECONTROLS);
mxlc.dwLineID=mxl.dwLineID;
mxlc.dwControlType=MIXERCONTROL_CONTROLTYPE_VOLUME;
mxlc.cControls=1;//一般为1
mxlc.cbmxctrl=sizeof(MIXERCONTROL);
mxlc.pamxctrl=&mxc;
mixerGetLineControls((HMIXEROBJ)m_hMixer,&mxlc,
MIXER_OBJECTF_HMIXER|MIXER_GETLINECONTROLSF_ONEBYTYPE);
m_controlid=mxc.dwControlID;
m_control.SetRange(mxc.Bounds.lMinimum,mxc.Bounds.lMaximum);
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
MIXERCONTROLDETAILS_SIGNED mxcdVolume;
MIXERCONTROLDETAILS mxcd;
mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
mxcd.dwControlID = mxc.dwControlID;
mxcd.cChannels = 1;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_SIGNED);
mxcd.paDetails = &mxcdVolume;
mixerGetControlDetails((HMIXEROBJ)m_hMixer,&mxcd,
MIXER_OBJECTF_HMIXER|MIXER_GETCONTROLDETAILSF_VALUE);
m_control.SetPos(mxcdVolume.lValue);
return TRUE;
}
(7)添加滑标控件的WM_HSCROLL消息的实现函数,该函数不能通过类向导添加,需要手动添加,首先在头文件ControlSoundDlg.h进行函数声明。
afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar);
然后在实现文件ControlSoundDlg.cpp中建立消息与函数的映射。
ON_WM_HSCROLL()
在OnHScroll函数中通过获得滑标的位置变化来对音量进行调节。
void CControlSoundDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
DWORD val;
val=((CSliderCtrl*)pScrollBar)->GetPos();
MIXERCONTROLDETAILS_UNSIGNED mxcdVolume = {val};
MIXERCONTROLDETAILS mxcd;
mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
mxcd.dwControlID = m_controlid;
mxcd.cChannels = 1;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
mxcd.paDetails = &mxcdVolume;
mixerSetControlDetails((HMIXEROBJ)m_hMixer,&mxcd,
MIXER_OBJECTF_HMIXER|MIXER_SETCONTROLDETAILSF_VALUE);
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
(8)当音量发生改变时,系统会向程序发送MM_MIXM_CONTROL_CHANGE消息,可以通过对该消息进行映射,来达到当系统音量改变时应用程序滑标也发生变化。对MM_MIXM_CONTROL_CHANGE消息进行映射首先在头文件中加入函数声明。
afx_msg LONG OnMixerCtrlChange(UINT wParam, LONG lParam);
然后在实现文件中建立消息与函数的映射。
ON_MESSAGE(MM_MIXM_CONTROL_CHANGE,OnMixerCtrlChange)
在OnMixerCtrlChange函数中参数wParam获取的是音量设备句柄,参数lParam获取的是音量控制行ID值,通过这两个参数就可以获得改变后的音量。
LONG CControlSoundDlg::OnMixerCtrlChange(UINT wParam, LONG lParam)
{
if ((HMIXER)wParam == m_hMixer && (DWORD)lParam ==m_controlid)
{
MIXERCONTROLDETAILS_UNSIGNED mxcdVolume;
MIXERCONTROLDETAILS mxcd;
mxcd.cbStruct = sizeof(MIXERCONTROLDETAILS);
mxcd.dwControlID = m_controlid;
mxcd.cChannels = 1;
mxcd.cMultipleItems = 0;
mxcd.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
mxcd.paDetails = &mxcdVolume;
mixerGetControlDetails((HMIXEROBJ)m_hMixer,&mxcd,
MIXER_OBJECTF_HMIXER|MIXER_GETCONTROLDETAILSF_VALUE);
m_control.SetPos(mxcdVolume.dwValue);
}
return 0L;
}
(9)程序运行效果如图11.7所示。