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引數指定了登錄檔或 |
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函式才返回 |
表1-1播放標誌的含義
4) 舉例:
1.4.1.直接播放本地存放的.wav格式的檔案為例。播放系統開機的聲音,示例程式碼:
PlaySound(_T("C:\\WINDOWS\\Media\\Windows XP 啟動.wav"), NULL, SND_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) 除了不能指定資源名字外,引數lpszSound與PlaySound的是一樣的。
2) 引數fuSound是如何播放聲音的標誌,可以是SND_ASYNC、SND_LOOP、SND_MEMORY、SND_NODEFAULT、SND_NOSTOP和SND_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(NULL, szPath, MAX_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);
即可。