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

书上的第十一章是声音系统,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) 
  • 标准级(DSSCL_NORMAL):该级别只能使用22KHZ、立体声、8位的音乐,并且不能直接的写主缓冲,也不能使用压缩过的声音
  • 优先级(DSSCL_PAIORITY):可以实现硬件混合,可以设置主缓冲的声音格式和压缩过的音乐
  • 独占级(DSSCL_EXCLUSIVE):当应用程序在前台工作时,其它程序是不可使用声音的
  • 写主缓冲级(DSSCL_WRITEPRIMARY):最高的合作级,程序可以直接的操纵主缓冲,而且程序必须直接的写主缓冲区(最基层的操作).在这种级别,第二缓冲将不可用
  1. 播放声音播放声音要通过以下步骤:
  2. 锁定辅助缓冲的一部分以获得你所需要的那部分缓冲的基址
  3. 向缓冲写数据
  4. 解锁
  5. 使用IDirectSoundBuffer::Play方法来播放声音
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容