dx

DirectX游戏开发终极指南【声音系统】

2016-09-30  本文已影响216人  语文小子

书上的第十一章是声音系统,Dx9已经不支持DirectMusic,参考以下代码可以修改为基于DirectSound的声音系统

#define WIN32_LEAN_AND_MEAN

#include <iostream>
#include <windows.h>
#include <mmsystem.h>
#include <dsound.h>
using namespace std;


#pragma comment(lib,"dxguid.lib")
#pragma comment(lib,"dsound.lib")
#pragma comment(lib,"winmm.lib")
#define WINCLASSNAME "winclass1"

#ifndef DSBCAPS_CTRLDEFAULT
#define DSBCAPS_CTRLDEFAULT (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN | DSBCAPS_CTRLVOLUME)
#endif

////////////////////////////////
LPDIRECTSOUND    lpds = NULL;
LPDIRECTSOUNDBUFFER    lpdbsBuffer = NULL;
DSBUFFERDESC    dsbd;
WAVEFORMATEX    wfmx;
UCHAR*            sndBuffer = NULL;
HWND            main_window_handle = NULL;
//////////////////////////////////
LRESULT    CALLBACK    WinProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
int GameInit(void* params = NULL, int num = 0);
int GameMain(void* params = NULL, int num = 0);
int GameShutdown(void* params = NULL, int num = 0);
int DSound_Load_Wav(char* filename, int control_flags = DSBCAPS_CTRLDEFAULT);
int GameInit(void* params, int num)
{
    if (DirectSoundCreate(NULL, &lpds, NULL) != DS_OK)
        return 0;

    if (lpds->SetCooperativeLevel(main_window_handle, DSSCL_NORMAL) != DS_OK)
        return 0;
    DSound_Load_Wav("Windows XP Startup.wav");

    lpdbsBuffer->Play(0, 0, 1);
    return 1;
}
//-----------------------------------------------------------
//名称:DSound_Load_Wav
//功能:利用微软提供的mmio库,解析wav文件,将数据读入内存,然后拷贝到
//DirectSound建立的 辅助缓冲区 从而实现文件的播放(GameInit())
//----------------------------------------------------------


int DSound_Load_Wav(char* filename, int control_flags)
{
    HMMIO handle;
    MMCKINFO mmckriff, mmckIn;
    PCMWAVEFORMAT    pwfm;
    memset(&mmckriff, 0, sizeof(MMCKINFO));
    if ((handle = mmioOpen(filename, NULL, MMIO_READ | MMIO_ALLOCBUF)) == NULL)
        return 0;

    //--------------------------------------------
    //进入块(chunk),查找 riff和wave的标记
    //----------------------------------------------
    if (0 != mmioDescend(handle, &mmckriff, NULL, 0))
    {
        mmioClose(handle, 0);
        return -1;
    }

    //-----------------------------------------
    //#define FOURCC_RIFF  mmioFOURCC('R','I','F','F')
    //----------------------------------------------
    if (mmckriff.ckid != FOURCC_RIFF || mmckriff.fccType != mmioFOURCC('W', 'A', 'V', 'E'))
    {
        mmioClose(handle, 0);
        return -1;
    }

    /////////////////////////////////////////////
    //查找 fmt 块
    ////////////////////////////////////////////
    mmckIn.ckid = mmioFOURCC('f', 'm', 't', ' ');
    if (0 != mmioDescend(handle, &mmckIn, &mmckriff, MMIO_FINDCHUNK))
    {
        mmioClose(handle, 0);
        return -1;
    }

    ////////////////////////////////////////////////////
    //fmt块的格式与PCMWAVEFORMAT 的格式定义相同
    //所以读入临时变量,最后写入 wmtf中
    ////////////////////////////////////////////////////
    if (mmioRead(handle, (HPSTR)&pwfm, sizeof(PCMWAVEFORMAT)) != sizeof(PCMWAVEFORMAT))
    {
        mmioClose(handle, 0);
        return -1;
    }
    ////////////////////////////////////
    //检测是不是 pc的wave标准格式
    //
    //////////////////////////////
    if (pwfm.wf.wFormatTag != WAVE_FORMAT_PCM)
    {
        mmioClose(handle, 0);
        return -1;
    }

    /////////////////////////////
    //给wmfx赋值
    /////////////////////////
    memcpy(&wfmx, &pwfm, sizeof(pwfm));
    wfmx.cbSize = 0;

    if (0 != mmioAscend(handle, &mmckIn, 0))
    {
        mmioClose(handle, 0);
        return -1;
    }

    /////////////////////////////
    //查找 data chunk
    /////////////////////////
    mmckIn.ckid = mmioFOURCC('d', 'a', 't', 'a');

    if (0 != mmioDescend(handle, &mmckIn, &mmckriff, MMIO_FINDCHUNK))
    {
        mmioClose(handle, 0);
        return -1;
    }

    sndBuffer = new UCHAR[mmckIn.cksize];

    /////////////////////////////
    //数据写入sndBuffer
    /////////////////////////
    mmioRead(handle, (HPSTR)sndBuffer, mmckIn.cksize);

    mmioClose(handle, 0);

    /////////////////////////////
    //建立directsound的辅助缓存
    //
    /////////////////////////
    dsbd.dwSize = sizeof(DSBUFFERDESC);
    dsbd.dwBufferBytes = mmckIn.cksize;
    dsbd.dwFlags = (control_flags | DSBCAPS_STATIC | DSBCAPS_LOCSOFTWARE);
    dsbd.lpwfxFormat = &wfmx;

    if (FAILED(lpds->CreateSoundBuffer(&dsbd, &lpdbsBuffer, NULL)))
    {
        delete[] sndBuffer;
        return -1;
    }
    VOID* pDSLockedBuffer = NULL;
    DWORD    dwDSLockedBufferSize = 0;

    ////////////////////
    //lock 住内存,现在他是可操作的,拷贝进去即可
    ////////////////
    if (lpdbsBuffer->Lock(0, mmckIn.cksize, &pDSLockedBuffer, &dwDSLockedBufferSize, NULL, NULL, 0L))
        return 0;
    memcpy(pDSLockedBuffer, sndBuffer, mmckIn.cksize);

    ///////////////
    //Unlock 缓冲区内存
    //
    ////////////////
    if (FAILED(lpdbsBuffer->Unlock(pDSLockedBuffer, dwDSLockedBufferSize, NULL, 0)))
    {
        delete[] sndBuffer;
        return 0;
    }

    return 1;
}
int GameMain(void* params, int num)
{
    return 1;
}
INT WINAPI WinMain(HINSTANCE hinst, HINSTANCE hprev, LPSTR lCmdline, int nCmdshow)
{
    MSG    msg;
    HWND hwnd;
    WNDCLASSEX wndclass;

    wndclass.cbClsExtra = 0;
    wndclass.cbSize = sizeof(WNDCLASSEX);
    wndclass.cbWndExtra = 0;
    wndclass.hbrBackground = (HBRUSH)CreateSolidBrush(RGB(0, 255, 255));
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hInstance = hinst;
    wndclass.lpfnWndProc = WinProc;
    wndclass.lpszClassName = WINCLASSNAME;
    wndclass.lpszMenuName = NULL;
    wndclass.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;

    RegisterClassEx(&wndclass);

    if (!(hwnd = CreateWindowEx(NULL, WINCLASSNAME, "DSOUND DEMO",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        0, 0,
        640, 480,
        NULL,
        NULL,
        hinst,
        NULL)))
        return 0;

    main_window_handle = hwnd;
    GameInit();
    while (TRUE)
    {

        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            if (msg.message == WM_QUIT)
                break;
            TranslateMessage(&msg);
            DispatchMessage(&msg);
            GameMain();
        }
    }
    return (int)(msg.wParam);
}

LRESULT    CALLBACK    WinProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    PAINTSTRUCT        ps;
    HDC                hdc;

    switch (msg)
    {
    case WM_CREATE:
        break;
    case WM_PAINT:
        hdc = BeginPaint(hwnd, &ps);
        EndPaint(hwnd, &ps);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        break;
    }

    return (DefWindowProc(hwnd, msg, wparam, lparam));
}
  1. 创造DirectSound对象最简单的方法是调用DirectSoundCreate函数
LPDIRECTSOUND lpds;
HRESULT hr = DirectSoundCreate(NULL, &lpds, NULL));

该函数的第一个参数是硬件设备,NULL表示使用默认的设备,第二个参数是远程指针LPDIRECTSOUND的地址,也就是创造的DirectSound对象放置的地址,第三个参数必须为NULL,暂时没有用

  1. 因为WINDOWS是一多任务环境,可以允许多个应用程序同时工作,当然也会产生多个程序在同时里使用同一设备工作的情况,通过合作级别,DirectX可以保证所有的程序在使用同一设备时不会发生冲突

所以每个使用DirectSound的程序都应该有一合作级别用来决定允许访问的设备。DirectSound有四种合作级别:标准级、优先级、独占级和写主缓冲级(write-primary,写是主要的动作),其中游戏普遍使用优先级这种级别可以使程序在同一采样条件下作出最柔韧的输出

lpds->SetCooperativeLevel(main_window_handle, DSSCL_NORMAL) 
  1. 播放声音播放声音要通过以下步骤:
  2. 锁定辅助缓冲的一部分以获得你所需要的那部分缓冲的基址
  3. 向缓冲写数据
  4. 解锁
  5. 使用IDirectSoundBuffer::Play方法来播放声音
上一篇下一篇

猜你喜欢

热点阅读