DirectSound的應用

weixin_34377065發表於2015-03-04

    假設僅僅使用PlaySound()這個API函式來表現聲音效果的話,那麼就無法表現出聲音的混音效果,由於PlaySound在播放還有一個聲音時,必定會導致現有聲音的停止。因此,使用

PlaySound()要想構建出一個包括豐富音樂與音效的遊戲世界是不現實的。
    而DirectSound就能夠完美的解決混音問題,並且它直接針對硬體程式設計,最大程度上減小了遊戲程式邏輯對於聲音播放效果的影響。
    在這篇文章裡,先談談DirectSound的使用。

    初始化工作第一步,也是全部DirectX組建初始化的必做工作,總的來說分為三個步驟:1、設定好庫連線的支援。2、加入須要的標頭檔案。3、也是大家最easy遺忘的一步,將

設定資料夾中與DirectX SDK相關的庫與標頭檔案的連線保持在最頂端。
    以下針對DirectSound來具體講講上面三個步驟,1.加入庫連線,能夠有兩種方法,你能夠在project選單條中選擇設定選項,並在連線這一項的物件與庫模組這一欄目中寫上

Dsound.lib Dxguid.lib兩個字串;你也能夠僅僅在工作區用加入檔案的方法把這兩項放在裡面。2.加入<dsound.h>標頭檔案放在程式碼段的頂端,這個無須贅言。3.假設此時發現有些

Directsound的類名無法識別,那麼請檢查工具選單項的設定選項中資料夾的設定,看是否在lib與include中都將DirectX SDK相關內容都放在第一位,由於在編譯連線中第一位的庫

是作為連線的首選的。
    另外還應加入Winmm.lib以及mmsystem.h,mmreg.h標頭檔案,由於載入WAVE檔案時會用到。

    初始化工作第二步,DirectSound物件的建立
    (1)建立DirectSound物件
    (2)設定共享層級
    (3)設定主緩衝區的格式
    首先要建立一個代表音效卡的DirectSound物件,我們先定義LPDIRECTSOUND pDS,然後用DirectSoundCreate(NULL, &pDS, NULL);方法來建立它,假設你想察看這個物件是否成功

的建立,能夠定義一個HRESULT result變數,由它接受DirectSoundCreate方法的返回值得並推斷是否等於DS_OK,假設不等於它,則能夠用MessageBox()方式彈出對話方塊來告訴程

序員建立失敗。注意DirectSoundCreate()中的第一個引數,是NULL,表示使用眼下預設的音效卡。也能夠呼叫DirectSoundEnumerate取得可用的音效卡。
    然後要設定程式協調層級,使用pDS呼叫SetCooperativeLevel方法來實現,注意這種方法有兩個引數,第一個引數代表應用程式的主窗體,而第二個引數則設定使用資源的優

先權。
    最後要看看緩衝區的概念,主緩衝區能夠看作一個DirectSound是用來播放聲音,產生混音效果的區域,它能夠自己主動生成,也能夠自己建立,但假設自己建立並設定其播放模式

,在設定協調層級時,標誌位必須設定為DSSCL_PRIORITY.次緩衝區則儲存播放聲音的檔案。在載入聲音檔案後,僅僅要呼叫Play()方法,聲音就會自己主動的送入主緩衝區中並進行播放

。在初始化過程中,應重點注意DSBUFFERDESC結構,它擔負著區分主次緩衝區以及緩衝區明細初始化的重任,在使用它時,首先要清空,能夠使用memset()方法來將其全部記憶體中

的位設為0,同一時候要設定結構的大小,並確定它的標誌位,以及設定緩衝區大小與格式,其詳細的初始化過程能夠看文章結尾的樣例。

    在完畢了初始化工作後,應該先把須要播放的聲音檔案載入到已經完畢初始化的次緩衝區中。這裡重點講下怎樣讀入一個聲音檔案以及取得當中的資訊與播放的資料。
    首先我們要知道,WAVE是利用區塊(chunk)方式儲存檔案的,包括紀錄檔案格式的fmt區塊與檔案實際內容的data區塊。因此讀取檔案必需要完畢下面步驟:
    (1)開啟檔案
    (2)確認是否為RIFF檔案,型別為WAVE
    (3)尋找fmt區塊,取得檔案格式
    (4)尋找data區塊,取得檔案內容
    (5)關閉檔案
    (6)載入聲音入次緩衝區
詳細過程見樣例。

    最後當然是播放與停止的使用了,詳細能夠自己去用次緩衝區指標試一下。
/*--------------------------------------------------------------------------------*/

//    以下是我寫的一個使用DirectSound的樣例:
////////////////////////////下面為標頭檔案部分
#ifndef GAMESOUND
#define GAMESOUND

#include "dsound.h"
#include "windows.h"
#include "mmsystem.h"
#include "mmreg.h"


class GameSound
{
private:
    HWND soundhwnd;

    HRESULT result;      //用來接受建立後的返回值
    LPDIRECTSOUND pDS;   //代表音效卡的DirectSound物件
    LPDIRECTSOUNDBUFFER pMainBuf;  //宣告主緩衝區指標
    DSBUFFERDESC desc;             //宣告描寫敘述結構,用來初始化緩衝區域
    WAVEFORMATEX pwfmt;            //宣告聲音結構,用來設定播放格式
 
    WAVEFORMATEX swfmt;    //宣告聲音結構
    MMCKINFO     ckRiff;   //RIFF區塊的資訊
    MMCKINFO     ckInfo;   //子區塊的資訊
    MMRESULT     mmresult; //返回的結果
    DWORD        size;     //實際資料的大小
    HMMIO        hbackground;    //開啟的多媒體檔案
public:
    GameSound();
    void GameSoundInit(HWND);             //GameSound物件的建立
    void GameSoundbufferConstruct();      //緩衝區的建立
    void GameSoundfmtSet(int ,int ,int);  //通過主緩衝區指標來設定播放格式
    void GameSoundReadWAVfile(char*, HMMIO&);//將聲音檔案讀入並將明細存在HMMIO結構中
    void GameSoundReadinbuffer(LPDIRECTSOUNDBUFFER&, char*);//將聲音檔案讀入次緩衝區中

 
    LPDIRECTSOUNDBUFFER pStartmusic;  //宣告子緩衝區指標(開始音樂指標)
    LPDIRECTSOUNDBUFFER pTalkmusic;  //宣告子緩衝區指標(談天音樂指標)
    LPDIRECTSOUNDBUFFER pWalkmusic;  //宣告子緩衝區指標(行走音樂指標)
    LPDIRECTSOUNDBUFFER pWarmusic;  //宣告子緩衝區指標(戰鬥音樂指標)
    LPDIRECTSOUNDBUFFER pyudimusic;  //宣告子緩衝區指標(攻擊聲音指標)
    LPDIRECTSOUNDBUFFER pwinmusic;  //宣告子緩衝區指標(勝利音樂指標)
    LPDIRECTSOUNDBUFFER plosemusic;  //宣告子緩衝區指標(失敗聲音指標)

    LPDIRECTSOUNDBUFFER pAttacksound;  //宣告子緩衝區指標(攻擊聲音指標)
    LPDIRECTSOUNDBUFFER pAIAttacksound;  //宣告子緩衝區指標(攻擊聲音指標)

    void GameSoundAllstop();  //for背景音樂,讓背景音樂更換時,先前的全部音樂都停止,從而播放新的音樂
    void GameMusicplay(LPDIRECTSOUNDBUFFER&);     //用來播放迴圈音樂
    void GameSoundplay(LPDIRECTSOUNDBUFFER&);     //用來播放一次性音效
};

#endif

 

////////////////////////////下面為原始檔部分
#include "GameSound.h"

GameSound::GameSound()
{
}

void GameSound::GameSoundInit(HWND hwnd)
{
 this->pDS;
 this->soundhwnd = hwnd;
 this->result = DirectSoundCreate(NULL, &pDS, NULL);
 if(this->result != DS_OK)
  MessageBox(hwnd, "建立 DirectSound 物件失敗!", NULL,MB_OK);

 this->result = this->pDS->SetCooperativeLevel(hwnd, DSSCL_PRIORITY);
 if(this->result != DS_OK)
  MessageBox(hwnd, "設定程式協調層級失敗!", NULL,MB_OK);

    this->GameSoundbufferConstruct(); 
}

void GameSound::GameSoundbufferConstruct()
{

 memset(&this->desc, 0, sizeof(desc));   //清空結構內容
 desc.dwSize = sizeof(desc);             //配製描寫敘述結構大小
 desc.dwFlags = DSBCAPS_PRIMARYBUFFER;   //???
 desc.dwBufferBytes = 0;
 desc.lpwfxFormat = NULL;
 result = pDS->CreateSoundBuffer(&desc, &this->pMainBuf, NULL);
 if(this->result != DS_OK)
  MessageBox(this->soundhwnd, "建立主緩衝區域失敗!", NULL,MB_OK);
 

 this->GameSoundReadinbuffer(this->pTalkmusic, "sound//talk2.wav");
 this->GameSoundReadinbuffer(this->pStartmusic, "sound//startwav.wav");
 this->GameSoundReadinbuffer(this->pWarmusic, "sound//zhandou.wav");
 this->GameSoundReadinbuffer(this->pWalkmusic, "sound//mainwav.wav");
 this->GameSoundReadinbuffer(this->pAttacksound, "sound//fire.wav");
 this->GameSoundReadinbuffer(this->pAIAttacksound, "sound//fire2.wav");
 this->GameSoundReadinbuffer(this->pyudimusic, "sound//yudi.wav");
 this->GameSoundReadinbuffer(this->pwinmusic, "sound//win.wav");
 this->GameSoundReadinbuffer(this->plosemusic, "sound//lose.wav");
 
}

void GameSound::GameSoundfmtSet(int channels, int SamplesPerSec, int wBitPerSample)
{
    memset(&this->pwfmt, 0, sizeof(pwfmt));
    this->pwfmt.wFormatTag = WAVE_FORMAT_PCM;
    this->pwfmt.nChannels = channels;
    this->pwfmt.nSamplesPerSec = SamplesPerSec;
    this->pwfmt.wBitsPerSample = wBitPerSample;
    this->pwfmt.nBlockAlign = this->pwfmt.wBitsPerSample / 8 * this->pwfmt.nChannels;
    this->pwfmt.nAvgBytesPerSec = this->pwfmt.nSamplesPerSec * this->pwfmt.nBlockAlign;
    this->result = this->pMainBuf->SetFormat(&this->pwfmt);
    if(this->result != DS_OK)
 MessageBox(this->soundhwnd, "設定播放格式失敗!", NULL,MB_OK); 
}

void GameSound::GameSoundReadWAVfile(char* filename, HMMIO &hmmbackground)
{
 hmmbackground = mmioOpen(filename, NULL, MMIO_ALLOCBUF | MMIO_READ);  //開啟檔案
 if(hmmbackground == NULL)
  MessageBox(this->soundhwnd, "檔案不存在!", NULL,MB_OK);

 //搜尋型別
 ckRiff.fccType = mmioFOURCC('W', 'A', 'V', 'E');//設定檔案型別
 mmresult = mmioDescend(hmmbackground, &ckRiff, NULL, MMIO_FINDRIFF);
 if(mmresult != MMSYSERR_NOERROR)
        MessageBox(this->soundhwnd, "檔案格式錯誤!", NULL,MB_OK);

        //搜尋區塊
 ckInfo.ckid = mmioFOURCC('f', 'm', 't', ' ');//設定區塊型別
 mmresult = mmioDescend(hmmbackground, &ckInfo, &ckRiff, MMIO_FINDCHUNK);
        if(mmresult != MMSYSERR_NOERROR)
  MessageBox(this->soundhwnd, "檔案格式錯誤!", NULL,MB_OK);
 if(mmioRead(hmmbackground, (HPSTR)&swfmt, sizeof(swfmt)) == -1)
  MessageBox(this->soundhwnd, "讀取格式失敗!", NULL,MB_OK);

 mmresult = mmioAscend(hmmbackground, &ckInfo, 0);   //跳出子區塊

        //搜尋區塊
 ckInfo.ckid = mmioFOURCC('d', 'a', 't', 'a');
 mmresult = mmioDescend(hmmbackground, &ckInfo, &ckRiff, MMIO_FINDCHUNK);
 if(mmresult != MMSYSERR_NOERROR)
  MessageBox(this->soundhwnd, "檔案格式錯誤!", NULL,MB_OK);

 size = ckInfo.cksize;

}

void GameSound::GameSoundReadinbuffer(LPDIRECTSOUNDBUFFER& buffer, char* filename)
{
 LPVOID pAudio;
 DWORD bytesAudio;

 
 this->GameSoundReadWAVfile(filename, this->hbackground);

        memset(&this->desc, 0, sizeof(desc));   //清空結構內容
 desc.dwSize = sizeof(desc);             //配製描寫敘述結構大小
 desc.dwFlags = DSBCAPS_STATIC | DSBCAPS_CTRLPAN |
             DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS;   //???
 desc.dwBufferBytes = this->size;
 desc.lpwfxFormat = &this->swfmt;
 result = pDS->CreateSoundBuffer(&desc, &buffer, NULL);
 if(this->result != DS_OK)
  MessageBox(this->soundhwnd, "建立次緩衝區域失敗!", NULL,MB_OK);

 

        result = buffer->Lock(0, this->size, &pAudio, &bytesAudio, NULL, NULL, NULL);
 if(this->result != DS_OK)
  MessageBox(this->soundhwnd, "鎖定緩衝區失敗!", NULL,MB_OK);

 this->mmresult = mmioRead(this->hbackground, (HPSTR)pAudio, bytesAudio);

 if(mmresult == -1)
  MessageBox(this->soundhwnd, "讀取聲音檔案資料失敗", NULL,MB_OK);

 this->result = buffer->Unlock(pAudio, bytesAudio, NULL, NULL);

 if(this->result != DS_OK)
  MessageBox(this->soundhwnd, "解除鎖定緩衝區失敗!", NULL,MB_OK);

 mmioClose(this->hbackground, 0);
}

void GameSound::GameSoundAllstop()
{
 this->pAttacksound->Stop();
 this->pStartmusic->Stop();
 this->pTalkmusic->Stop();
 this->pWalkmusic->Stop();
 this->pWarmusic->Stop();
 this->pyudimusic->Stop();
 this->pwinmusic->Stop();
 this->plosemusic->Stop();
 this->pAIAttacksound->Stop();
 this->pAttacksound->SetCurrentPosition(0);
 this->pStartmusic->SetCurrentPosition(0);
 this->pTalkmusic->SetCurrentPosition(0);
 this->pWalkmusic->SetCurrentPosition(0);
 this->pWarmusic->SetCurrentPosition(0);
 this->pyudimusic->SetCurrentPosition(0);
 this->pwinmusic->SetCurrentPosition(0);
 this->plosemusic->SetCurrentPosition(0);
 this->pAIAttacksound->SetCurrentPosition(0);
}

void GameSound::GameMusicplay(LPDIRECTSOUNDBUFFER& buffer)
{
 this->GameSoundAllstop();

 buffer->Play(0, 0, 1);
}

void GameSound::GameSoundplay(LPDIRECTSOUNDBUFFER& buffer)
{
 buffer->Play(0, 0, 0);
}