VC++中播放聲音的方法
聲音是多媒體的一個重要組成部分,在應用程式中加入聲音可以使介面更友好。在VC++中可以根據不同的應用要求,用不同的方法實現聲音的播放。 一.播放音效檔的簡單方法 在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); 三.播放音效檔的進階方法 在VC++中提供了一組對音訊裝置及多媒體檔案直接進行操作的函數。利用這些函數可以靈活地對音效檔進行各種處理。 首先介紹幾個要用到的資料結構。WAVEFORMATEX結構定義了WAVE音頻資料檔案的格式。WAVEHDR結構定義了波形音頻緩衝區。讀出的資料首先要填充此緩衝區才能送音訊裝置播放。WAVEOUTCAPS結構描述了音訊裝置的效能。MMCKINFO結構包含了RIFF檔案中一個塊的資訊。詳細的說明請參考VC++中的協助。 下面給出程式流程簡圖及程式原始碼清單,在VC++環境下可直接使用:
來源程式清單如下: LPSTR szFileName;//音效檔名 MMCKINFO mmckinfoParent; MMCKINFO mmckinfoSubChunk; DWORD dwFmtSize; HMMIO m_hmmio;//音頻檔案控制代碼 DWORD m_WaveLong; HPSTR lpData;//音頻資料 HANDLE m_hData; HANDLE m_hFormat; WAVEFORMATEX * lpFormat; DWORD m_dwDataOffset; DWORD m_dwDataSize; WAVEHDR pWaveOutHdr; WAVEOUTCAPS pwoc; HWAVEOUT hWaveOut; //開啟波形檔案 if(!(m_hmmio=mmioOpen(szFileName,NULL,MMIO_READ|MMIO_ALLOCBUF))) { //File open Error Error("Failed to open the file.");//錯誤處理函數 return false; } //檢查開啟檔案是否是音效檔 mmckinfoParent.fccType =mmioFOURCC('W','A','V','E'); if(mmioDescend(m_hmmio,(LPMMCKINFO)&mmckinfoParent,NULL,MMIO_FINDRIFF)) { //NOT WAVE FILE AND QUIT } //尋找 'fmt' 塊 mmckinfoSubChunk.ckid =mmioFOURCC('f','m','t',' '); if(mmioDescend(m_hmmio,&mmckinfoSubChunk,&mmckinfoParent,MMIO_FINDCHUNK)) { //Can't find 'fmt' chunk } //獲得 'fmt '塊的大小,申請記憶體 dwFmtSize=mmckinfoSubChunk.cksize ; m_hFormat=LocalAlloc(LMEM_MOVEABLE,LOWORD(dwFmtSize)); if(!m_hFormat) { //failed alloc memory } lpFormat=(WAVEFORMATEX*)LocalLock(m_hFormat); if(!lpFormat) { //failed to lock the memory } if((unsigned long)mmioRead(m_hmmio,(HPSTR)lpFormat,dwFmtSize)!=dwFmtSize) { //failed to read format chunk } //離開 fmt 塊 mmioAscend(m_hmmio,&mmckinfoSubChunk,0); //尋找 'data' 塊 mmckinfoSubChunk.ckid=mmioFOURCC('d','a','t','a'); if(mmioDescend(m_hmmio,&mmckinfoSubChunk,&mmckinfoParent,MMIO_FINDCHUNK)) { //Can't find 'data' chunk } //獲得 'data'塊的大小 m_dwDataSize=mmckinfoSubChunk.cksize ; m_dwDataOffset =mmckinfoSubChunk.dwDataOffset ; if(m_dwDataSize==0L) { //no data in the 'data' chunk } //為音頻資料分配記憶體 lpData=new char[m_dwDataSize]; if(!lpData) { //faile } if(mmioSeek(m_hmmio,SoundOffset,SEEK_SET)<0) { //Failed to read the data chunk } m_WaveLong=mmioRead(m_hmmio,lpData,SoundLong); if(m_WaveLong<0) { //Failed to read the data chunk } //檢查音訊裝置,返迴音頻輸出裝置的效能 if(waveOutGetDeVCaps(WAVE_MAPPER,&pwoc,sizeof(WAVEOUTCAPS))!=0) { //Unable to allocate or lock memory } //檢查音訊輸出裝置是否能播放指定的音頻檔案 if(waveOutOpen(&hWaveOut,DevsNum,lpFormat,NULL,NULL,CALLBACK_NULL)!=0) { //Failed to OPEN the wave out devices } //準備待播放的資料 pWaveOutHdr.lpData =(HPSTR)lpData; pWaveOutHdr.dwBufferLength =m_WaveLong; pWaveOutHdr.dwFlags =0; if(waveOutPrepareHeader(hWaveOut,&pWaveOutHdr,sizeof(WAVEHDR))!=0) { //Failed to prepare the wave data buffer } //播放音頻資料檔案 if(waveOutWrite(hWaveOut,&pWaveOutHdr,sizeof(WAVEHDR))!=0) { //Failed to write the wave data buffer } //關閉音訊輸出裝置,釋放記憶體 waveOutReset(hWaveOut); waveOutClose(hWaveOut); LocalUnlock(m_hFormat); LocalFree(m_hFormat); delete [] lpData; 說明:1)以上使用的音訊裝置和音效檔操作函數的聲明包含在mmsystem.h標頭檔中,因此在程式中必須用#include "mmsystem.h"語句加入標頭檔。同時在編譯時間要加入動態串連匯入庫winmm.lib,具體實現方法是從Developer Studio的Project菜單中選擇Settings,然後在Link選項卡上的Object/Library Modules控制中加入winmm.lib。2)在pWaveOutHdr.lpData中指定不同的資料,可以播放音頻資料檔案中任意指定位置的聲音。3) 以上程式均在VC++6.0中調試通過,在文中省略了對錯誤及異常情況的處理,在實際應用中必須加入。 四.結論 在VC++中可以根據應用需要採用不同的方法播放音效檔。簡單應用可以直接調用聲音播放函數。第二種方法可以把聲音作為資源加入可執行檔中。如果在播放之前要對聲音資料進行處理,可用第三種方法。 參考書目: 1. 美 Paul Perry 陳向群 等譯《多媒體開發指南》 清華大學出版社 2. 美 Peter Norton, Rob McGregor 孫鳳英 等譯《MFC開發Windows95/NT4應用程式》 清華大學出版社 1998 3. 周敬利 《多媒體音效卡技術及應用》 電子工業出版社 1998 |