如何播放 WAV 檔案?

n3verl4nd發表於2013-09-19

fromhttp://www.vckbase.com/index.php/wv/434

平時,你在多媒體軟體的設計中是怎樣處理聲音檔案的呢?使用Windows 提供的API函式 sndPlaySound來實現WAV檔案的播放?但是,你有沒有遇到過這種情況呢:當WAV檔案大於可用記憶體時,sndPlaySound 函式就不能進行播放!!!那麼,如何利用MCI播放大型音訊檔案呢?

本文將介紹一種方法。

Windows支援兩種RIFF(resource interchange file format,“資源互動檔案格式”)音 頻檔案:MIDI的RMID檔案和波形音訊檔案格式WAV檔案,本文將介紹如何用MCI命令播放大型W AV檔案。用sndPlaySound播放音訊檔案只需要一行程式碼。比如實現非同步播放的方法為 sndPlaySound("c:\windows\ding.wav",SND_ASYNC);

由此可以看到,sndPlaySound 的使用是很簡單的。但是用sndPlaySound播放音訊檔案有 一個限制,即整個音訊檔案必須全部調入可用的實體記憶體。因此應用sndPlaySound播放的音訊 檔案相對較小,最大約100K。要播放大一些的音訊檔案(在多媒體設計中是經常要遇到的情況 )需要使用MCI的功能。這裡建立了一個Cwave類,可以處理播放音訊的MCI命令,因為該類能夠 執行很多的MCI命令和建立了資料結構,所以只需要簡單的成員函式(如OpenDevice, CloseDe vice, Play和Stop)。在CWave類中抽象了特定的MCI命令和資料結構,只含幾個簡單的成員函 數OpenDevice, CloseDevice, Play和Stop。波形音訊裝置是一個複合裝置,如果開啟波形設 備,然後開啟並關閉每個波形元素,最後關閉波形裝置,這樣可以使得播放效能更好。呼叫C wave::OpenDevice就可以開啟波形裝置,OpenDevice將MCI_OPEN命令傳遞給mciSendCommand函 數,如果呼叫成功,就用資料結構MCI_OPEN_PARMS的wDeviceID成員返回波形裝置的識別符號, 該識別符號儲存在一個供以後使用的私有資料成員中。一旦開啟了Cwace物件,通過Cwace::Pla y播放WAV檔案就就緒了,WAV檔名和一個視窗指標被傳遞給Play方法以便將MCI通知訊息傳送 到制定的視窗。

WAV檔案的播放首先要通過分配一個MCI_OPEN_PARMS結構並給所要播放的WAV檔案設定
lpstrElementName成員開啟WAV檔案。將該結構和MCI_OPEN傳遞給mciSendCommand,開啟WAV檔案 並用MCI_OPEN_PARMS結構的wDeviceID成員返回元素識別符號。第二步是命令波形音訊裝置播放 WAV檔案。分配了MCI_PLAY_PARMS結構並將dwCallback成員設定為視窗控制程式碼。如果要同步播放 音訊波形檔案,就增加MCI_WAIT標誌並跳過視窗控制程式碼。這樣做會使應用程式在mciSendComman d函式返回之前等待WAV檔案播放完畢。最可能的情況是非同步播放大型WAV檔案,可以象下面那 樣指定MCI_NOTIFY標誌並設定dwCallback成員做到這一點。

MCI_PLAY_PARMS mciPlayParms;
MciPlayParms.dwCallback=(DWORD)pWnd->m_hWnd;
DwResult=mciSendCommand(m_nDevice,
                        MCI_PLAY,
                        MCI_NOTIFY,
                        (DWORD)(LPVOID)&mciPlayParms);

這樣就開始了WAV檔案的播放,並且在播放完畢後,MM_MCINOTIFY訊息會傳送到指定的視窗。一個WAV檔案播放所發生的事件序列為:(1)命令播放WAV檔案並立即返回;(2)播放WAV文 件;(3)完成後傳送通知訊息。

完成播放後關閉WAV檔案元素是程式設計師的責任,簡單的呼叫Cwave類的Stop成員函式就可以了。Stop成員函式將WAV檔案識別符號和MCI_CLOSE命令傳遞給mciSendCommand函式,不必為該命令分配一個MCI結構,下述程式碼關閉了WAV檔案:

mciSendCommand(m_nElement,MCI_CLOSE,NULL,NULL);
      

播放完所有的WAV檔案後必須關閉波形音訊裝置,Cwave類的解構函式呼叫Cwave::Close Device自動完成。 將本文中介紹的CWave類加入到自己的程式中,就可以方便的應用它播放音訊檔案了。

//建立Cwave類,放在Wave.h檔案中:

class CWave:public CObject
{
    //Construction
    public:
        CWave();
        virtual ~CWave();

    //Operations
public:
        DWORD OpenDevice();
        DWORD CloseDevice();
        DWORD Play(CWnd *pParentWnd,LPCSTR pFileName);
        DWORD Stop();

    //Implementation
    protected:
        void DisplayErrorMsg(DWORD dwError);

    //Members
    protected:
        MCIDEVICEID m_nDeviceID;
        MCIDEVICEID m_nElementID;
};

//Cwave類的實現程式碼,Cwave.cpp
#include <stdafx.h>  
#include "cwave.h"

CWave::CWave()
{
    m_nDeviceID=0;
    m_nElementID=0;
}

CWave::~CWave()
{
    if(m_nElementID)
        Stop();
    if(m_nDeviceID)
        CloseDevice();
}

DWORD CWave::OpenDevice()
{
    DWORD dwResult=0;

    if (m_nDeviceID)
    {
        MCI_OPEN_PARMS mciOpenParms;

	    mciOpenParms.lpstrDeviceType=(LPSTR)MCI_DEVTYPE_WAVEFORM_AUDIO;
		
	    //open the wave device
	    dwResult = mciSendCommand(NULL,
 				MCI_OPEN,
				MCI_OPEN_TYPE|MCI_OPEN_TYPE_ID|MCI_WAIT,
				(DWORD)(LPVOID)&mciOpenParms);

	    //save device identifier,will use eith other MCI commands
	    m_nDeviceID = mciOpenParms.wDeviceID;

	    //display error message if failed 
	    if(dwResult)
	         DisplayErrorMsg(dwResult);
      }
      //return result of MCI operation
      return dwResult;
}

DWORD CWave::CloseDevice()
{
    DWORD dwResult=0;

    //close if currently open
    if(m_nDeviceID)
    {
        //close the MCI device
        dwResult=mciSendCommand(m_nDeviceID,MCI_CLOSE,NULL,NULL);

        //display error message if failed
        if(dwResult)
            DisplayErrorMsg(dwResult);

        //set identifier to close state
        else 
            m_nDeviceID=0;
    }

    //return result of MCI operation
    return dwResult;
}

DWORD CWave::Play(CWnd* pWnd,LPCSTR pFileName)
{
    MCI_OPEN_PARMS mciOpenParms;
    //initialize structure
    memset(&mciOpenParms,0,sizeof(MCI_OPEN_PARMS));

    //set the WAV file name to be played
    mciOpenParms.lpstrElementName=pFileName;

    //first open the device
    DWORD dwResult=mciSendCommand(m_nDeviceID,MCI_OPEN,
        MCI_OPEN_ELEMENT,(DWORD)(LPVOID)&mciOpenParms);

    //display error message if failed
    if(dwResult)
        DisplayErrorMsg(dwResult);

    //if successful,instruct the device to play the WAV file
    else
    {
        //save element indentifier
        m_nElementID=mciOpenParms.wDeviceID;

        MCI_PLAY_PARMS mciPlayParms;

        //set the window that will receive notification message
        mciPlayParms.dwCallback=(DWORD)pWnd->m_hWnd;

        //instruct device to play file
        dwResult=mciSendCommand(m_nElementID,MCI_PLAY,
            MCI_NOTIFY,(DWORD)(LPVOID)&mciPlayParms);

        //display error and close element if failed
        if(dwResult)
        {
            DisplayErrorMsg(dwResult);
            Stop();
        }
    }

    //return result of MCI operation
    return dwResult;
}

DWORD CWave::Stop()
{
    DWORD dwResult=0;

    //close if element is currently open
    if(m_nElementID)
    {
        dwResult=mciSendCommand(m_nElementID,MCI_CLOSE,NULL,NULL);

	    //display error message if failed
        if(dwResult)
            DisplayErrorMsg(dwResult);
        //set identifier to closed state
        else
            m_nElementID=0;
    }
    return dwResult;
}

void CWave::DisplayErrorMsg(DWORD dwError)
{
    //check if there was an error
    if(dwError)
    {
        //character string that contains error message
        char szErrorMsg[MAXERRORLENGTH];

        //retrieve string associated error message
        if(!mciGetErrorString(dwError,szErrorMsg,sizeof(szErrorMsg)))
            strcpy(szErrorMsg,"Unknown Error");
        //display error string in message box
        AfxMessageBox(szErrorMsg);
    }
}      




相關文章