聲音是多媒體的一個重要組成部分,在應用程序中加入聲音可以使界面更友好。在VC++中可以根據不同的應用要求,用不同的方法實現聲音的播放。
一.播放聲音文件的簡單方法
在VC++ 中的多媒體動態連接庫中提供了一組與音頻設備有關的函數。利用這些函數可以方便地播放聲音。最簡單的播放聲音方法就是直接調用VC++中提供的聲音播放函數BOOL sndPlaySound ( LPCSTR lpszSound,UINT fuSound ); 或BOOL PlaySound( LPCSTR lpszSound, HMODULE hmod, DWord fuSound );其中參數lpszSound是需要播放聲音的.WAV文件的路徑和文件名, hmod在這裡為NULL,fuSound是播放聲音的標志,詳細說明請參考VC++中的幫助。 例如播放C:soundmusic.wav可以用sndPlaySound ("c:soundmusic.wav",SND_ASYNC);或PlaySound("c:soundmusic.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++中可以根據應用需要采用不同的方法播放聲音文件。簡單應用可以直接調用聲音播放函數。第二種方法可以把聲音作為資源加入可執行文件中。如果在播放之前要對聲音數據進行處理,可用第三種方法。