본문 바로가기

Programming/DirectX

[DirectSound] 5. DirectSound 버퍼의 생성

반응형

DirectSound의 장치 생성이 완료된 이후에 버퍼를 생성합니다.

버퍼는 DirectSound의 실제 소리를 담당합니다.

각 소리(배경음, 효과음 등)는 하나의 Secondary Buffer를 가집니다.

Secondary Buffer는 IDirectSound8이 제공하는 메소드인 CreateSoundBuffer()를 통해서 생성할 수 있습니다.

여기에서 다음과 같이 정의된 DSBUFFERDSEC라는 구조체가 사용됩니다.

typedef struct _DSBUFFERDESC
{
    DWORD           dwSize;
    DWORD           dwFlags;
    DWORD           dwBufferBytes;
    DWORD           dwReserved;
    LPWAVEFORMATEX  lpwfxFormat;
#if DIRECTSOUND_VERSION >= 0x0700
    GUID            guid3DAlgorithm;
#endif
} DSBUFFERDESC, *LPDSBUFFERDESC;

구조체의 내용을 적절한 값으로 채워야 합니다.

먼저 dwSize에는 sizeof(DSBUFFERDESC)을 넣으면 됩니다.

dwFlags를 통해서 다양한 플래그를 설정하는 것이 가능합니다.

먼저 DSBCAPS_STATIC | DSBCAPS_GLOBALFOCUS를 지정하고 필요한 값을 추가로 지정하면 됩니다.

DSBCAPS_STATIC 플래그의 경우에 온보드 하드웨어 메모리를 사용하게 됩니다.

DSBCAPS_GLOBALFOCUS는 윈도우가 포커스된 상태가 아닐 때도 재생이 가능하게 해줍니다.

재생 중에 볼륨이나 재생 속도 등을 조절하기 위한 플래그도 존재합니다.

DSBCAPS_CTRLVOLUME이나 DSBCAPS_CTRLFREQUENCY 등의 플래그를 사용하면 됩니다.

버퍼가 특정 위치까지 재생했을 때 통지를 받기 위해서 DSBCAPS_CTRLPOSITIONNOTIFY를 추가합니다.

버퍼의 통지는 긴 사운드나 무한히 반복되는 사운드를 구현할 때 사용됩니다.

다음은 버퍼의 크기를 지정하는 dwBufferBytes입니다.

스트리밍 방식이 아닌 전체 파일을 한 번에 불러올 때는 해당 파일의 오디오 데이터만큼 크기를 지정하면 됩니다.

일반적으로 파일의 크기가 클 경우에는 한 번에 불러오지 않고 일정 크기 단위로 계속 읽는 방법을 사용합니다.

lpwfxFormat은 Wave 파일의 포맷을 지정하는 구조체로 아래 링크를 확인하면 됩니다.

2015/09/02 - [Programming/DirectX] - [DirectSound] 2. Wave 파일을 읽기

Wave 파일의 경우 파일 내부에 포맷이 포함되어 있지만 순수한 PCM 파일의 경우에는 직접 입력해야 합니다.

guid3DAlgorithm는 DSBCAPS_CTRL3D 플래그를 지정했을 때 사용됩니다.

사용하지 않을 경우에는 GUID_NULL을 넣어주면 됩니다.

구조체의 준비가 완료되면 생성된 장치 객체의 메소드인 CreateSoundBuffer()를 호출합니다.

첫 번째 인자가 위에서 설명한 DSBUFFERDESC 구조체의 포인터입니다.

두 번째는 리턴받을 IDirectSoundBuffer**타입입니다.

장치를 생성할 때와 동일하게 더블 포인터 타입을 받아서 포인터에 객체를 동적 생성하고 리턴합니다.

세 번째는 무조건 NULL을 전달하면 됩니다.

호출이 성공하면 IDirectSoundBuffer를 전달받게 되고 이것을 변환해야 합니다.

QueryInterface() 메소드를 호출해서 IDirectSoundBuffer8 인터페이스를 얻어야 합니다.

COM 인터페이스는 모두 IUnknown에서 상속을 받도록 되어 있습니다.

그리고 IUnknown의 기본 메소드 중 하나가 QueryInterface()입니다.

다른 나머지는 해제를 위한 Release()와 참조 카운트를 올리기 위한 AddRef()입니다.

다음과 같은 형태로 호출하면 됩니다.

pDsb->QueryInterface(IID_IDirectSoundBuffer8,  (LPVOID*)ppDsb8);

전체적인 소스 코드는 다음과 같이 구현하면 됩니다.

HRESULT CreateSoundBuffer(LPDIRECTSOUND8 lpDirectSound, LPDIRECTSOUNDBUFFER8* ppDsb8)
{
	WAVEFORMATEX wfx;
	DSBUFFERDESC dsbdesc;
	LPDIRECTSOUNDBUFFER pDsb = NULL;
	HRESULT hr;

	memset(&wfx, 0, sizeof(WAVEFORMATEX));
	wfx.wFormatTag = WAVE_FORMAT_PCM;
	wfx.nChannels = 1;
	wfx.wBitsPerSample = 16;
	wfx.cbSize = 0;
	wfx.nSamplesPerSec = 16000;
	wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * (wfx.wBitsPerSample / 8);
	wfx.nBlockAlign = (wfx.wBitsPerSample / 8) * wfx.nChannels;

	memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
	dsbdesc.dwSize = sizeof(DSBUFFERDESC);
	dsbdesc.dwFlags = DSBCAPS_STATIC | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY;
	dsbdesc.dwBufferBytes = 3 * wfx.nAvgBytesPerSec;
	dsbdesc.lpwfxFormat = &wfx;

	hr = lpDirectSound->CreateSoundBuffer(&dsbdesc, &pDsb, NULL);
	if (SUCCEEDED(hr))
	{
		hr = pDsb->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*)ppDsb8);
		pDsb->Release();
	}
	return hr;
}

Wave의 포맷을 지정하는 부분을 제외하고는 그렇게 긴 코드는 아닙니다.

예제 코드에서는 초당 바이트의 수에 3을 곱해서 3초 길이를 재생할 수 있도록 버퍼의 크기를 지정했습니다.

이전에 장치의 생성으로 생성된 포인터를 함수의 첫 번째에 전달하면 됩니다.

그리고 두 번째에는 리턴을 받기 위해서 더블 포인터로 전달을 받습니다.

이것을 버퍼의 생성도 완료가 됩니다.

반응형