상세 컨텐츠

본문 제목

다이렉트 사운드 더블 버퍼링 또는 다중버퍼관리출력

프로그래밍 관련/사운드

by AlrepondTech 2011. 1. 5. 16:42

본문

반응형

 

 

 

=================================

=================================

=================================

 

 

 

 

 

 

제목:[강좌] DirectX Programming (6)

 

다이렉트 사운드 실전 프로그래밍

 

게임에 있어 중요한 것 중의 하나가 바로 음향 효과이다. 요즘은 게임마

다 음향 효과의 중요도 한층 더 배가되는 분위기이다. 사실적인 음향으로

인해 게임에 한층 더 몰입할 수 있는 것은 물론이고 돈 좀 들었을 것 같

은 배경 음악이 계속적으로 들려나오기도 한다. 다이렉트X의 로우레벨 서

비스인 다이렉트 파운데이션의 일부로 존재하는 다이렉트 사운드에서 바

로 이런 기능을 여러분의 프로그램에 쉽게 추가할 수 있도록 해준다.

 

다이렉트 사운드 실전 프로그래밍

 

다이렉트 사운드란 ?

 

다이렉트 사운드는 디지털 사운드를 플레이하고 캡처하고 이들을 믹싱할

수 있는 기능을 제공해준다. 다른 다이렉트X 컴포넌트들과 마찬가지로 이

역시 하드웨어를 가능한한 최대로 이용하고 할 수 없는 경우에만 소프트웨

어를 이용한다. 다이렉트 사운드 객체는 시스템 상의 오디오 하드웨어를

나타낸다. 이 객체를 이용해 사운드 카드, 스피커와 관련 메모리 환경을 제

어할 수 있다. 현재까지 다이렉트 사운드는 압축된 형식의 사운드 데이

터는 지원하지 못한다.

즉, 예를 들어 .WAV 형식의 파일을 다이렉트 사운드를 통해 연주하려면

그 파일의 데이터가 압축되어 있을 경우 압축을 풀어서 연주해야 한다. 다

이렉트 사운드의 구조는 그림 10과 같다.

그림 10 : 다이렉트 사운드 시스템의 구조

 

다이렉트 사운드 관련 객체

다이렉트 사운드 객체는 플레이와 캡처별로 별도의 자식 객체를 운영한

다. 다이렉트 사운드 관련 객체로는 다음과 같은 것이 있다.

- 다이렉트 사운드 객체

- 다이렉트 사운드 버퍼 객체

- 다이렉트 3D 사운드 버퍼 객체

- 다이렉트 캡처 객체

다이렉트 드로우에서처럼 중심이 되는 객체는 다이렉트 사운드 객체이

다. 이 객체를 통해 다른 객체들을 생성하게 된다. 어떤 음향을 출력하려

면 다이렉트 사운드 버퍼 객체나 3D 사운드 버퍼 객체를 생성해야 한다. 만

일 어떤 음향을 녹음하고자 한다면 다이렉트 캡처 객체를 이용한다.

 

다이렉트 사운드 프로그램 작성

만들고자 하는 다이렉트 사운드 예제 프로그램의 기능은 간단하다. 파일

메뉴의 열기 명령을 통해 .WAV 파일을 로드한 다음 연주 명령을 택하

면 .WAV 파일의 연주를 시작하고 중지 명령을 택하면 연주를 중단한다.

 

예제 프로그램의 시작

다이렉트 드로우를 사용하려면 제일 먼저 해야할 일은 dsound.h라는 헤더

파일을 포함하고 dsound.lib와 winmm.lib라는 라이브러리 파일을 링크시

라이브러리 항목으로 지정하는 일이다. 앞서 Project의 Settings 명령을

이용해 라이브러리 파일을 지정하는 예는 설명하였다. 이 예제 프로그램 역

시 API를 이용해 작성할 것이다. 이 프로그램은 보통의 윈도 프로그램

과 동일한 형태의 WinMain을 갖는다. WinMain에 대한 설명은 생략하기로

하겠다.

 

다이렉트 사운드 객체의 초기화

일단 다이렉트 사운드 객체를 대표하는 전역 변수를 하나 선언한다.

// DirectSound 관련 변수
LPDIRECTSOUND pDirectSoundObj; // DirectSound 객체

다이렉트 사운드 객체의 초기화는 WM_CREATE 메시지부에서 수행한다.

switch (message)
{
// 변수를 초기화한다.
case WM_CREATE:
{
     pDirectSoundObj = NULL;
     // 다이렉트 사운드 객체를 일단 NULL로
     pDirectSoundBuffer = NULL;
     // 다이렉트 사운드 버퍼 객체를 일단 NULL로

     // ----------------------------------
     // DirectSound 관련 초기화를 수행한다.
     // ----------------------------------
     if (DirectSoundCreate(NULL, &pDirectSoundObj, NULL) != DS_OK)
          return -1;

     // 우선순위 레벨을 정한다.
     if (pDirectSoundObj->SetCooperativeLevel(hWnd, DSSCL_NORMAL) != DS_OK)
          return -1;
          return 0;
     }

다이렉트 사운드에는 다이렉트 사운드 객체의 생성을 위해서

DirectSoundCreate라는 별도의 도우미 함수를 제공해준다. 이를 이용해 다

이렉트 사운드 객체를 생성한다. 이 함수 및 이후에 나오는 다이렉트 사운

드 관련 함수의 자세한 기능에 대해서는 뒤에 있는 다이렉트 사운드 레퍼런

스를 참고하기 바란다.

만일 DirectSoundCreate와 같은 도우미 함수를 이용하지 않는다면

CreateInstance와 같은 COM API를 이용하여 약간은 복잡하게 객체를 생성

해야 한다. 다이렉트 사운드 객체의 생성이 끝나면 이것의 협동 레벨을

설정해야 한다. 사운드 장치라는 것이 혼자서만 독점할 수 있는 것이 아니

다. 왜냐면 윈도 운영체계는 멀티태스크 환경이기 때문에 여러 프로그램에

서 사운드 디바이스를 사용하려 할 수 있다. 그렇기 때문에 협동 레벨을 지

정하여 남들과의 공존을 꾀하거나 독점을 시도할 수도 있다. 여기서

는 두 번째 인자로 DSSCL_NORMAL을 주어 공존을 꾀하고 있다. 이렇

게 DSSCL_NORMAL을 주면 사운드 카드를 통한 출력이 22KHz의 8비트 샘플

데이터와 스테레오 사운드로 고정된다는 점도 같이 알아두기 바란다.

이렇게 하나로 고정시켜야 여러 프로그램에서 공용하기가 쉽기 때문이다.

협동 레벨에는‘정상(normal)’,‘우선(priority)’,‘독점(exclusive)’,

‘쓰기우선(write primary)’이 있다. 대부분의 경우 특별한 이유가 없다

면 ‘정상’을 사용한다.

 

다이렉트 사운드 버퍼 객체의 생성

다이렉트 사운드를 이용해 사운드 출력을 행하려면 버퍼 객체를 별도로 생

성해 주어야 한다. 이러한 개념은 다이렉트 드로우에서와 비슷한 면이 있

다. 다만 다이렉트 드로우에서는 전위 버퍼와 후위 버퍼를 명시적으로 생성

해주어야 했지만 다이렉트 사운드에서는 주 사운드 버퍼(Primary sound

buffer)는 명시적으로 생성할 필요가 없고 다만 이차 사운드 버퍼만 명시

적으로 생성하면 된다.

그래서 이차 사운드 버퍼에 사운드 데이터를 써넣고 Play 명령을 주면 중

지 명령이 들어갈 때까지 다이렉트 사운드는 그 데이터를 계속적으로 연주

한다.

물론 한 번만 연주하고 끝내도록 할 수도 있고 직접 주 사운드 버퍼를 제어

할 수도 있는데 후자의 방법은 그다지 좋은 방법은 아닌 듯 하다.

다이렉트 사운드를 초기화하면 다이렉트 사운드는 내부적으로 주 사운드

버퍼를 생성하여 소리를 합성하고 출력장치로 내보내는데 사용한다. 각

사운드를 출력하려면 각기 이차 버퍼를 만들어 사용해야 한다. 사운드 믹싱

은 이차 버퍼에 있는 사운드를 동시에 출력하여 이뤄진다. 다이렉트 사운

드 믹서의 지연 시간은 20ms 정도이지만 이를 소프트웨어로 애뮬레이트

할 경우는 100~150ms 정도의 지연 시간이 발생한다.

이차 사운드 버퍼를 생성할 때는 이것이 스태틱 사운드 버퍼인지 아니면

스트리밍 사운드 버퍼인지를 명시해야 한다. 스태틱 사운드 버퍼는 메모리

에 사운드를 모두 보관한다. 스트리밍 사운드 버퍼는 사운드의 일부만 유지

한다. 스트리밍 버퍼를 사용할 때는 응용프로그램에서 주기적으로 새로운

데이터를 버퍼에 써넣어야 한다.

만일 사운드 데이터를 모두 메모리에 올려놓고 연주하려 한다면 버퍼 관리

에는 거의 신경쓸 일이 없다. 하지만 이는 작은 크기의 사운드 파일에만

가능한 일이다. 큰 파일의 경우에는 보다 더 메모리를 효율적으로 써야

한다. 큰 파일이 아니더라도 여러 개의 사운드 파일을 동시에 연주할 경우

에도 마찬가지이다. 스트리밍(streaming)이 바로 작은 버퍼를 통해 커다란

파일을 연주하는 방법이다. 즉 메모리는 조금만 할당해놓고 계속적으로

사운드 파일의 다음 데이터를 읽어다가 채우는 방식을 택하는 것이다.

이 방법은 효율적이긴 하지만 연주되는 속도가 채워지는 속도보다 빠를 경

우 문제가 된다. 즉, 지연이 발생하여 듣는 사람 입장에서는 끊기는 듯한

느낌이나 잡음이 들릴 수가 있다.

이 방법의 관건은 바로 버퍼가 비워지기 전에 채워넣는 적합한 시점을 어

떻게 찾아내느냐에 달려있다. 여기에는 다음과 같은 방법이 있다.

- 타이머 메시지 등을 이용해 주기적으로 검사한다.

- 버퍼의 특정 위치가 연주되면 이벤트를 발생시키도록 한다.

예제 프로그램에서는 간단하게 스태틱 사운드 버퍼를 사용해 사운드 파일

데이터를 모두 메모리에 올려놓고 한 번에 연주해보도록 하겠다. 다음과 같

은 변수를 전역 변수로 정의한다.

 

LPDIRECTSOUNDBUFFER pDirectSoundBuffer;
// 사운드 버퍼 객체
// WAVE 파일 관련 변수
DWORD waveSize;
BOOL waveOK;
char *pWave;
WAVEFORMATEX waveFormatEx;

먼저 사용자가 파일 메뉴의 열기 명령을 택하면 리스트 11의 코드가 실행된

다.

===============================================================
리스트 11 : 파일 메뉴의 열기 명령을 택하면 실행되는 코드
---------------------------------------------------------------

case WM_COMMAND :
{
     switch(LOWORD(wParam))
     {

case ID_OPEN :

{

OPENFILENAME ofn;

char buffer[MAXCHARS];

buffer[0] = 0;

memset(&ofn, 0x00, sizeof

(OPENFILENAME));

ofn.lStructSize = sizeof(OPENFILENAME);

ofn.hwndOwner = hWnd;

ofn.lpstrFilter = "Wave Files\

000*.wav\000\000";

ofn.nFilterIndex = 1;

ofn.lpstrFile = buffer;

ofn.nMaxFile = MAXCHARS;

// 파일 오픈 다이알로그를 띄운다.

if (GetOpenFileName(&ofn))

{

if (!Load(buffer, TRUE))

// 지정된 Wave 파일을 로드한다.

{

MessageBox(hWnd, "Wave 파일이

아닙니다.", "에러", MB_OK);

}

}

break;

}

}

}

===============================================================

리스트 11은 파일 오픈 다이얼로그 박스를 실행시켜 사용자로부터 .WAV

파일을 입력받는것이다. 사용자가 파일의 입력을 끝내고 OK 버튼을 누르면

Load 함수를 실행시켜 이차 사운드 버퍼를 생성하고 거기로 WAV 파일의 모

든 내용을 올려버린다. Load 파일은 다음과 같다.

 

// ----------------------------------------------------

// Load : 지정한 파일을 로드하여 이차 버퍼를 만들고 거기에

// 사운드 데이터를 로드한다.

// ----------------------------------------------------

BOOL Load(LPCSTR pWavFileName, BOOL bAll)

{

// 이차 버퍼를 생성한다.

if (!CreateBuffer(pWavFileName, bAll))

return FALSE;

// 사운드 데이터를 로드한다.

return LoadSoundData(bAll, (bAll) ? 0 : cbBufSize);

}

 

위의 함수에서는 이차 사운드 버퍼를 생성하기 위해 CreateBuffer라는 함

수를 이용하고 있다. 그리고 사운드 데이터를 로드하기 위해

LoadSoundData라는 함수를 호출하고 있다. 이 두 함수의 내용은 리스트

12, 13, 14와 같다.

CreateBuffer 함수를 보면 WAV 파일 데이터를 로드하기 위해 LoadWave

FileToPCM이란 함수를 호출한다. 이 함수는 WAV 파일의 압축 데이터를 다

이렉트 사운드에서 사용할 수 있도록 풀어주는 역할을 담당한다. 그러

기 위해서 윈도의 mmIO 라이브러리를 사용하는데 그 내용이 꽤 까다롭

다. 필자도 그 내용을 이해한 것은 아니고 웹사이트에서 남들이 만들어 놓

은 것을 가져다 썼다. LoadWaveFileToPCM 함수에서는 일단 WAV 파일의 데

이터를 pWave가 가리키는 메모리로 올려놓고 그 크기를 waveSize 변수에 저

장해둔다.

 

===============================================================

리스트 12 : CreateBuffer 함수

// ----------------------------------------------

// CreateBuffer : 지정된 파일을 읽어들여 DirectSound

// 이차 버퍼를 만든다.

// ----------------------------------------------

BOOL CreateBuffer(LPCSTR sndFileName, BOOL bAll)

{

DSBUFFERDESC dsBufferDesc;

// WAV 파일을 로드한다.

if (LoadWaveFileToPCM((LPSTR)sndFileName)

== FALSE)

return FALSE;

// DSBUFFERDESC 구조체를 초기화한다.

memset(&dsBufferDesc, 0, sizeof

(DSBUFFERDESC));

dsBufferDesc.dwSize = sizeof(DSBUFFERDESC);

dsBufferDesc.dwFlags = DSBCAPS_CTRLDEFAULT;

if (bAll)

dsBufferDesc.dwBufferBytes = waveSize;

else

{

cbBufSize = 2000;

dsBufferDesc.dwBufferBytes = waveSize;

}

dsBufferDesc.lpwfxFormat

= (LPWAVEFORMATEX)&waveFormatEx;

// 앞서 생성된 이차 버퍼가 있으면 이를 먼저 해제한다.

if (pDirectSoundBuffer)

pDirectSoundBuffer->Release();

// 이차 버퍼를 생성한다.

if (pDirectSoundObj->CreateSoundBuffer

(&dsBufferDesc, &pDirectSoundBuffer, NULL)

!= DS_OK)

return FALSE;

return TRUE;

}

===============================================================

 

다음으로 DSBUFFERDESC 구조체의 dsBufferDesc 변수를 초기화하고 이차

사운드 버퍼를 생성하기위해 다이렉트 사운드 객체의 CreateSoundBuffer 함

를 호출하여 그 값을 pDirectSoundBuffer 변수에 저장해둔다.

LoadWaveFile

ToPCM 함수의 원형은 리스트 13과 같다.

===============================================================

리스트 13 : LoadWaveFileToPCM 함수

// ----------------------------------------------

// LoadWaveFileToPCM : 지정된 파일을 읽어들여 이를

// 버퍼로 읽어들인다.

// ----------------------------------------------

BOOL LoadWaveFileToPCM(char* fileName)

{

MMCKINFO mmCkInfoRIFF;

MMCKINFO mmCkInfoChunk;

HMMIO hMMIO;

// Open the wave file.

hMMIO = mmioOpen(fileName, NULL, MMIO_READ

| MMIO_ALLOCBUF);

if (hMMIO == NULL)

return FALSE;

// Descend into the RIFF chunk.

mmCkInfoRIFF.fccType = mmioFOURCC

('W', 'A', 'V', 'E');

if (mmioDescend(hMMIO, &mmCkInfoRIFF,

NULL, MMIO_FINDRIFF) !

= MMSYSERR_NOERROR)

return FALSE;

// Descend into the format chunk.

mmCkInfoChunk.ckid = mmioFOURCC

('f', 'm', 't', ' ');

if (mmioDescend(hMMIO, &mmCkInfoChunk,

&mmCkInfoRIFF, MMIO_FINDCHUNK)

!= MMSYSERR_NOERROR)

return FALSE;

// Read the format information into the

WAVEFORMATEX structure.

if (mmioRead(hMMIO, (char*)&waveFormatEx,

sizeof(WAVEFORMATEX)) == -1)

return FALSE;

// Ascend out of the format chunk.

if (mmioAscend(hMMIO, &mmCkInfoChunk, 0)

!= MMSYSERR_NOERROR)

return FALSE;

// Descend into the data chunk.

mmCkInfoChunk.ckid = mmioFOURCC

('d', 'a', 't', 'a');

if (mmioDescend(hMMIO, &mmCkInfoChunk,

&mmCkInfoRIFF, MMIO_FINDCHUNK)

!= MMSYSERR_NOERROR)

return FALSE;

// WAV 데이터의 크기를 저장해둔다.

waveSize = mmCkInfoChunk.cksize;

// WAV 데이터를 저장해둘 메모리 영역을 확보해둔다.

pWave = (char*)GlobalAllocPtr

(GMEM_MOVEABLE, waveSize);

if (pWave == NULL)

return FALSE;

// WAV 데이터를 로드한다.

if (mmioRead(hMMIO, (char*)pWave, waveSize)

== -1)

return FALSE;

mmioClose(hMMIO, 0);

return TRUE;

}

===============================================================

 

 

 

 

 

반응형

 

728x90

 

 

 

이렇게 이차 사운드 버퍼가 생성되고 연주하고자 하는 WAV 파일의 내용

을 메모리로 읽어들여 놓았으면 WAV 파일의 내용을 이차 사운드 버퍼로 복

사해두어야 한다. 그 역할을 하는 것이 바로 LoadSoundData 함수이다.

WAV 데이터를 이차 사운드 버퍼로 써넣으려면 이차 사운드 버퍼의 포인터

를 얻어야 한다. 그 용도로 사용되는 것이 바로 다이렉트 사운드 버퍼

객체의 Lock 함수이고 사용한 다음에는 반드시 Unlock 함수를 호출하여 사

용이 끝났음을 알려야 한다. Lock 함수를 보면 이상하게도 포인터를 하나

가 아닌 두 개를 받도록 되어 있다. 이는 이차사운드 버퍼를 스트리밍

방식으로 사용할 때만 필요한 것이다. 예제 프로그램에서는 스태틱 방식으

로 모두 메모리에 올려놓고 사용하기 때문에 두 개의 포인터 중에서 앞의

것 하나만 필요하다.

사운드의 연주와 중지 이렇게 사운드 데이터의 이차 사운드 버퍼의 생성

이 종료되었으면 이제 연주하고 중지할 준비가 끝난 것이다.

연주 메뉴의 전체 연주 명령을 택하면 다음의 명령이 실행된다.

// --------------------------------------------------

// Play : 이차 버퍼에 쓰여진 데이터를 연주한다.

// --------------------------------------------------

BOOL Play(BOOL bInfiniteLoop)

{

if (pDirectSoundBuffer)

{

pDirectSoundBuffer->SetCurrentPosition(0);

pDirectSoundBuffer->Play(0, 0, DSBPLAY_LOOPING);

return TRUE;

}

return FALSE;

}

일단 이차 사운드 버퍼의 시작 연주 위치를 처음으로 되돌

리고(SetCurrentPosition(0)) 연주를 시작(Play)한다. 세 번째 인자로

DSBPLAY_LOOPING을 주었기 때문에 연주가 반복된다. 이 연주를 중지하고 싶

다면 중지 명령을 택한다. 이차 사운드 버퍼 객체의 Stop 함수를 호출하

면 현재 진행 중이던 연주가 중단된다. 중지 명령을 택하면 다음 함수가 실

행된다.

 

// ---------------------------------------------------

// Stop : 사운드 데이터의 연주를 중단한다.

// ---------------------------------------------------

BOOL Stop()

{ if (pDirectSoundBuffer)

return (pDirectSoundBuffer->Stop()==DS_OK)

? TRUE : FALSE;

else

return FALSE;

}

 

이것으로 다이렉트 사운드 프로그래밍에 대해 간단히 살펴보았다. 사실

스트리밍을 이용한 예제까지도 살펴봐야 제대로 된 다이렉트 사운드 프로

그래밍을 했다고 할 수 있을 텐데 다음 기회에 꼭 보여드릴 것을 약속한다.

 

다이렉트 사운드 레퍼런스

다이렉트 사운드 객체

DirectSoundCreate 함수

이 함수는 다이렉트 사운드 객체를 생성하고 초기화하는 역할을 담당한

다.

원형은 다음과 같다.

HRESULT WINAPI DirectSoundCreate(LPGUID lpGuid, LPDIRECT

SOUND * ppDS, IUnknown FAR * pUnkOuter);

첫 번째 인자인 lpGuid로는 생성하고자 하는 사운드 장치를 가리키는 GUID

를 지정해야 한다. 만일 디폴트 사운드 디바이스를 사용하려면 NULL을 지

정하면 된다. 두 번째 인자인 ppDS로는 결과적으로 생성되는 DirectSound

객체의 포인터를 받아오는 곳이다. 세 번째 인자는 현재로는 NULL을 주어

야 한다. 이 함수가 성공하면 DS_OK가 리턴된다. DirectSoundCreate로

DirectSound 객체의 생성이 성공하면 바로 SetCooperativeLevel 메소드를

호출해야 한다.

 

CreateSoundBuffer 함수

CreateSoundBuffer 메소드는 오디오 샘플 데이터를 저장하는데 사용되는

다이렉트 사운드 버퍼 객체를 생성한다. 이 함수의 원형은 다음과 같다.

HRESULT CreateSoundBuffer(LPCDSBUFFERDESC

lpcDSBufferDesc, LPLPDIRECTSOUNDBUFFER

lplpDirectSoundBuffer, IUnknown FAR * pUnkOuter);

 

첫 번째 인자인 lpcDSBufferDesc은 생성하고자 하는 사운드 버퍼의 형태를

기술한 DSBUFFERDESC 구조체를 인자로 받는다. 출력될 오디오 데이터의 형

식과 크기와 기능 등을 기술한다. 예를 들어 다이렉트 사운드 버퍼 객

체의 SetFrequency 함수를 사용하고 싶다면 이 구조체의 dwFlags

필드에 DSBCAPS_CTRLFREQUENCY를 지정해주어야 한다.

두 번째 인자인 lplpDirectSoundBuffer는 생성된 Direc tSoundBuffer 객체

의 주소를 받는다.

마지막 인자는 항상 NULL이어야 한다. 이 함수가 성공적으로 끝나면 리턴

값은 DS_OK가 된다.

DSBUFFERDESC 구조체

이 구조체는 생성하고자 하는 DirectSoundBuffer 객체의 특성을 기술하는

데 사용된다. 이 구조체는 앞에서 본 CreateSoundBuffer 메소드에 의해 사

용된다.

이 구조체의 각 필드의 구성은 다음과 같다.

LPWAVEFORMATEX lpwfxFormat;

DWORD dwSize : 이 구조체의 크기를 바이트 단위로 나타낸다.

DWORD dwFlags : 이 필드는 생성되는 DirectSoundBuffer의 기능을 지정하

는데 사용된다. 표 7과 같은 값을 하나 이상 지정할 수 있다.

DWORD dwBufferBytes : 버퍼의 크기를 바이트 단위로 지정한다.

주 버퍼를 생성할 때는 0을 지정해야 한다. 이 차 버퍼의 경우에는

DSBSIZE_

MIN와 DSBSIZE_MAX사이의 크기를 지정해야한다.

DWORD dwReserved : 사용되지 않는다.

LPWAVEFORMATEX lpwfxFormat

버퍼에서 사용할 사운드 데이터의 파형을 지정한다. 주버퍼의 경우에는

NULL을 지정해야 한다. 주 버퍼의 사운드 형식은 SetFormat 메소드를 이

용해 지정할 수 있다.

SetCooperativeLevel 함수

SetCooperativeLevel 메소드는 응용프로그램에서 사운드 디바이스를 어

떻게 사용할 것인지 협동 레벨을 지정한다. 이 함수의 원형은 다음과 같다.

HRESULT SetCooperativeLevel(HWND hwnd, DWORD dwLevel);

 

첫 번째 인자로는 이 응용프로그램의 윈도 핸들을 지정한다. 두 번째 인자

로는 원하는 우선 순위 레벨을 지정한다. 다음 값 중의 하나를 지정해야 한

다.

■DSSCL_EXCLUSIVE : 배타적 독점 레벨을 지정한다. 이 프로그램이 포커스

를 갖고 있을 때 이 프로그램이 단 하나의 사운드 소스가 된다. 이 레

벨은 DSSCL_PRIORITY 레벨의 모든 특권을 다 포함한다.

■DSSCL_NORMAL : 응용프로그램을 공동작업 상태로 만든다. 대부분의 프

로그램은 별다른 이유가 없는 한 이 모드를 사용해야 한다.

■DSSCL_PRIORITY : 이 모드의 프로그램은 버퍼의 형식을 변경(SetFormat

함수 호출 가능)할 수 있고 메모리를 온보드 메모리의 사용되지 않는 영

역을 이동(Compact 메소드 호출 가능)시킬 수 있다.

■DSSCL_WRITEPRIMARY : 가장 우선순위가 높은 레벨이다. 주 버퍼에 바

로 쓰는 것이 가능하며 이차 버퍼는 필요없다.

 

다이렉트 사운드 버퍼 객체

Play 함수

Play 메소드는 사운드 버퍼의 현재 위치부터 연주하도록 한다. 이 함수의

원형은 다음과 같다.

HRESULT Play(DWORD dwReserved1, DWORD dwReserved2,

DWORD dwFlags);

첫 번째 인자와 두 번째 인자는 0이어야 한다. 세 번째 인자인 dwFlags

는 버퍼를 어떻게 연주할 것인지를 나타내는 플래그이다. DSBPLAY_LOOPING

을 주면 연주가 명시적으로 중지될 때까지 연주가 계속적으로 반복된다.

Stop 함수

Stop 함수는 사운드 버퍼의 연주를 중지한다. 마지막으로 연주했던 곳

에서 멈추기 때문에 다시 Play 함수를 호출하면 멈췄던 부분에서부터 연주

가 다시 시작된다. 원형은 다음과 같다.

HRESULT Stop();

SetCurrentPosition 함수

SetCurrentPosition 함수는 버퍼(이차 버퍼만 가능)의 현재 연주 위치를

이동시킨다. 이 함수의 원형은 다음과 같다.

HRESULT SetCurrentPosition(DWORD dwNewPosition);

인자인 dwNewPosition으로는 버퍼의 새로운 위치를 나타내는 숫자를 지정

한다.

Lock 함수

Lock 함수는 버퍼의 유효한 쓰기 포인터를 얻어준다. 이 함수의 원형은 다

음과 같다.

HRESULT Lock(DWORD dwWriteCursor, DWORD dwWriteBytes,

LPVOID lplpvAudioPtr1, LPDWORD lpdwAudioBytes1,

LPVOID lplpvAudioPtr2, LPDWORD lpdwAudioBytes2,

DWORD dwFlags);

dwWriteCursor 인자는 버퍼의 어디서부터 락을 시작할 것인지를 지정한

다. 만일 dwFlags 인자에 DSBLOC K_FRO MWRITECURSOR가 지정되어

있다면 이 인자는 무시된다. dwWriteBytes 인자는 버퍼에서 어느 정도의 크

기를 락하려고 하는지 지정하는데 사용된다. lplpvAudioPtr1 인자는 버퍼

의 뒤 영역 중 락 된 영역에 대한 포인터를 얻어온다.

lpdwAudioBytes1는 lplpvAud ioPtr1가 가리키는 영역에서 몇 바이트가 락

되었는지를 나타내는 인자이다. 만일 이 인자의 값이 dwWriteBytes 인자

의 값보다 적다면 lplpvAudioPtr2 인자가 사운드 데이터의 두 번째 블록

을 가리킬 것이다. lplpvAudioPtr2는 두 번째 블록의 락된 주소를 받아온

다. 만일 첫 번째 블록으로 필요한 영역을 모두 락한 경우에는 이 인자의

값이 NULL이 된다. lpdwAudioBytes2는 lplpvAudioPtr2가 가리키는 주소에

서 몇 바이트가 락되었는지를 가리킨다. lplpvAudioPtr2가 NULL이면 이 인

자의 값은 0이 된다.

dwFlags 인자는 다음과 같은 값이 가능하다.

■DSBLOCK_FROMWRITECURSOR : 현재의 쓰기 위치부터 락을 건다. 이

인자를 주면 GetCurrentPosition 함수를 호출할 필요가 없으며

dwWriteCursor

인자가 무시된다.

■DSBLOCK_ENTIREBUFFER : 전체 버퍼를 락한다. dwWriteBytes 인자도

무시된다. 이렇게 락한 버퍼에 대한 포인터는 반드시 다이렉트 사운드 버

퍼의 Unlock 함수를 호출하여 반환해야 한다. 락되어있는 시간은 되도록 최

소화해야 한다.

Unlock 함수

이 함수는 락된 사운드 버퍼를 풀어주는 역할을 한다. 이 함수의 원형은

다음과 같다.

HRESULT Unlock(LPVOID lpvAudioPtr1, DWORD dwAudioBytes1,

LPVOID lpvAudioPtr2, DWORD dwAudioBytes2);

이 함수의 인자들의 값은 앞서 Lock함수의 세 번째 인자부터 여섯 번째

인자까지의 값과 동일하다. 간단하게나마 다이렉트X의 극히 일부에 해당하

는 다이렉트 사운드와 드로우에 대해 알아보았다. 예제 프로그램 중심으

로 살펴보았기 때문에 이해에 큰 무리는 없었으리라 생각된다. 물론 API

에 대한 프로그래밍 경험이 없거나 익숙하지 못한 독자들은 어려운 점이

많았으리라 생각된다. 한 입에 배부를 수는 없는 일이다. API를 이용한

프로그래밍은 윈도 프로그래밍의 기본이다. API 프로그래밍부터 공부한 사

람은 어떤 타입의 윈도 프로그래밍이든지 간에 뼈속 깊이까지 이해할 수

있다. 이제 천고마비의 계절 가을이다. 선선한 날씨에 공부 열심히 하기

바라며 필자의 글이 여러분의 다이렉트X에 대한 궁금증 중 일부를 풀어주

었기를 기대한다. 다이렉트 드로우와 사운드를 이용한 게임 제작은 다음

기회에 설명하도록 하겠다. 그 때까지 독자 여러분들도 열심히 공부하기

바란다.

 

다이렉트 뮤직의 등장

필자가 원고를 거의 다 마무리해갈 즈음해서 마이크로소프트의 다이렉트X

사이트에 들어갔다가 다이렉트X에 새로운 컴포넌트인 다이렉

트 뮤직(DirectMusic)이 추가되었다는 것을 알게 되었다.

현재까지 다이렉트X는 주로 그래픽이나 애니메이션과 사운드 기능의 향상

에 중점을 두어온 것이 사실이다. 사운드의 경우 3D 사운드까지 지원되기

는 하지만 기능 자체가 멀티미디어 프로그램에 충분하다고 볼 수는 없었

다.

이 컴포넌트는 MIDI에 기반하여 동작하기 때문에 기존의 다이렉트 사운드와

는 다른 차원의 서비스를 제공해준다. MIDI 프로토콜이 처음 등장한 것

은 바로 1982년이었다. 이것의 등장으로 인해 전자 악기와 컴퓨터간의 연결

이 가능해졌고 이것으로 인해 집에서도 음악을 만드는 것이 가능해졌다.

이러한 MIDI의 기반 위에 만들어진 다이렉트 뮤직은 산업계의 표준이

라 할 수 있는 DLS(Downloadable sound)를 완벽하게 지원한다는 특징

도 갖고 있다. 따라서 사운드카드 등의 실제 연주 장치에 따라 음질이 달라

지는 문제를 해결할 수 있는 실마리를 제공한다. 다이렉트 뮤직에 관한

자세한 사항은 http://www.microsoft.com/directx에서 찾아볼 수 있다.

델파이에서 다이렉트X 사용하기 필자가 예로 든 예제 프로그램들은 모두

API를 이용해 작성되어있다. 사실 델파이에서는 윈도 API를 아무런 무리 없

이 다 호출할 수 있기 때문에(VB는 포인터라는 개념이 없기 때문에 모든

API를 다 부를 수는 없다) 예제 프로그램의 코드를 델파이에서 사용할

수 있다. 다만 모양이 별로 델파이답지는 못할 것이다. 다이렉트X를 하나

의 컨트롤처럼 쉽게 사용할 수 있도록 해주려는 시도가 여러 곳에서 진행되

고 있다. 델파이에서 다이렉트X를 쉽게 사용할 수 있도록 해주는 모듈을

제공해주는 웹사이트도 있는데 관심 있는 독자라면 이 사이트

(http://www.delphi-jedi.org/DelphiGraphi cs/)를 방문해보기 바란다.

 

참고문헌

1) http://www.microsoft.com/directx

2) 윈도 95용 Game SDK 전략 가이드, 정보 문화사, 차주현, 박상욱 역

3) http://www.aros.net/~npawn/tutorial.htm

4) http://www.ziron.com/links/directx/

5) http://www.aha.ru/~pervago/

6) DirectX Foundation 도움말

 

- 끝 -

 

 

=================================

=================================

=================================

 

 

반응형


관련글 더보기

댓글 영역