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));
}
- 创造DirectSound对象最简单的方法是调用DirectSoundCreate函数
LPDIRECTSOUND lpds;
HRESULT hr = DirectSoundCreate(NULL, &lpds, NULL));
该函数的第一个参数是硬件设备,NULL表示使用默认的设备,第二个参数是远程指针LPDIRECTSOUND的地址,也就是创造的DirectSound对象放置的地址,第三个参数必须为NULL,暂时没有用
- 因为WINDOWS是一多任务环境,可以允许多个应用程序同时工作,当然也会产生多个程序在同时里使用同一设备工作的情况,通过合作级别,DirectX可以保证所有的程序在使用同一设备时不会发生冲突
所以每个使用DirectSound的程序都应该有一合作级别用来决定允许访问的设备。DirectSound有四种合作级别:标准级、优先级、独占级和写主缓冲级(write-primary,写是主要的动作),其中游戏普遍使用优先级这种级别可以使程序在同一采样条件下作出最柔韧的输出
lpds->SetCooperativeLevel(main_window_handle, DSSCL_NORMAL)
- 标准级(DSSCL_NORMAL):该级别只能使用22KHZ、立体声、8位的音乐,并且不能直接的写主缓冲,也不能使用压缩过的声音
- 优先级(DSSCL_PAIORITY):可以实现硬件混合,可以设置主缓冲的声音格式和压缩过的音乐
- 独占级(DSSCL_EXCLUSIVE):当应用程序在前台工作时,其它程序是不可使用声音的
- 写主缓冲级(DSSCL_WRITEPRIMARY):最高的合作级,程序可以直接的操纵主缓冲,而且程序必须直接的写主缓冲区(最基层的操作).在这种级别,第二缓冲将不可用
- 播放声音播放声音要通过以下步骤:
- 锁定辅助缓冲的一部分以获得你所需要的那部分缓冲的基址
- 向缓冲写数据
- 解锁
- 使用IDirectSoundBuffer::Play方法来播放声音