DirectSound Direct Sound DirectSound DirectX에서는사운드를사용하기위해서다이렉트사운드 (DirectSound) API를제공한다. DirectSound 를사용하기위해 d3d9.lib, d3dx9.lib, winmm.lib, dsound.lib, dxerr9.lib, dxguid.lib 을프로젝트에링크 305890 2009 년봄학기 5/6/2009 박경신 kpark@dankook.ac.kr DirectSound API Direct Sound DirectSound API 게임에서사운드효과를부여함 윈도우상에서작동되는사운드카드를지원함 PCM 형태의디지털사운드출력 (WAV 파일 ) 2 차원사운드와 3 차원사운드로분류 3 차원사운드 emulating 2- 스피커, 4- 스피커등의옵션지정가능 파일입출력을지원하지않음 DirectSound 가지원하는일 WAV 형식의파일재생 여러개의사운드를동시에재생 하드웨어버퍼에고음질의사운드저장 3 차원환경에서사운드재생 에코, 코러스등의효과 마이크나다른입력장치로부터사운드녹음 DirectSound 사운드버퍼 사운드데이타를사운드버퍼 (sound buffer) 를사용하여관리한다. 다양한사운드를가진여러개의버퍼를가질수있다. 버퍼가제어되고재생될수있으며, 또는새로운버퍼자료를생성하기위해믹싱 (mixing) 도가능하다. 사운드버퍼는사운드데이터를갖는영역이다. 예를들어, WAV 파일을사운드버퍼에읽어들여서, 파일에있는사운드자료를버퍼에넣어재생또는제어를할수있다.
Direct Sound 사운드버퍼의종류 주버퍼 (primary buffer) 모든재생되고자하는사운드가주버퍼에서믹싱 (mix) 되어사운드카드에서출력된다. 보조버퍼 (secondary buffer) 보조버퍼는우리의응용프로그램에서필요한모든사운드데이터를가지는버퍼이다. 다이렉트사운드는하나이상의보조버퍼를사용하여여러개의사운드를동시에재생할수있다. 정적버퍼 (static buffer) 사운드데이터가일정크기를가지고있을때정적버퍼를사용한다. 정적버퍼는특정사운드전체를메모리로읽어들일수있다. 스트리밍버퍼 (streaming buffer) 사운드데이터가너무커서메모리에한꺼번에들어가지않을때스트리밍버퍼를사용한다. 사운드가재생되면서스트리밍버퍼에새로운사운드데이터를읽어들인다. Direct Sound Interface IDirectSound8: 사운드카드의기능을결정하고재생을위한버퍼생성에사용 IDirectSoundBuffer8: 재생가능한사운드데이터를담기위한버퍼 IDirectSound3DBuffer8: 3 차원사운드를담기위한버퍼 IDirectSound3DListener8: 3 차원 listener 를표현에사용 IDirectSoundCapture8: Capture buffer 생성에사용 IDirectSoundCaptureBuffer8: 마이크와같은장치에서녹음한데이타를저장하기위한버퍼 IDirectSoundNotify8: 사운드재생종료를알림 IKsPropertySet8: 사운드카드제조업체에서새로운기능을넣기위해사용하는인터페이스 DirectSound 설정 Using DirectSound DirectSound 설정 1. DirectSound 의객체를생성한다. DirectSoundCreate8() 함수사용. 2. 협력레벨을설정한다. IDirectSound8::SetCooperativeLevel() 함수사용. 3. 보조사운드버퍼를생성한다. IDirectSound8::CreateSoundBuffer() 함수사용. 4. 생성된보조사운드버퍼에사운드정보를복사한다. 5. 마지막으로, 프로그램종료시에는반드시사용중인객체들을해제시킨다. 다이렉트사운드 (DirectSound) 다이렉트사운드를사용하려면반드시초기화가필요하다. 첫번째로, IDirectSound8 인터페이스로표현되는다이렉트사운드장치를사용해야한다. 다이렉트사운드장치 (DirectSound Device) 컴퓨터안에서사운드하드웨어특정부분의인터페이스를말한다. 다이렉트사운드가작동되도록하려면사운드카드를선택하고다이렉트사운드장치를생성해야한다. 다이렉트사운드장치생성은 DirectSoundCreate8 를사용한다.
Using DirectSound HRESULT DirectSoundCreate8(LPCGUID lpcguiddevice, LPDIRECTSOUND8 *ppds8, // LPDIRECTSOUND8 의포인터변수 LPUNKNOWN punkouter); // 항상 NULL 이다 lpcguiddevice GUID는사용하고자하는사운드카드장치를말한다 DSDEVID_DefaultPlayback 나 NULL을사용한다. NULL은디폴트사운드장치 (default sound device) 를사용한다는의미 ppds8 생성된다이렉트사운드장치를지칭하는변수의포인터 punkouter 제어하고자하는객체의 IUnknown interface이다. 항상 NULL이다. Using DirectSound // variable that will hold the return code HRESULT hr; // variable that will hold the created DirectSound device LPDIRECTSOUND8 m_pds = NULL; // Attempt to create the DirectSound device hr = DirectSoundCreate8(NULL, &m_pds, NULL); // Check the return value to confirm that a valid device was created if (FAILED(hr)) return false; Setting the Cooperative Level 하드웨어장치에접근할수있게협력레벨을지정해야한다. 다이렉트사운드가제공하는 4 가지의협력레벨 DSSCL_NORMAL 일반적인협력레벨. 이레벨은다른이벤트발생을가능하게해서다른응용프로그램과같이쓰기에가장좋다. 응용프로그램이활성화중일때만사운드를출력한다. 주버퍼를제어하기위한코드를작성할필요가없으며, 대부분이이설정을사용한다. 하드웨어장치가다른응용프로그램과공유해서쓰기때문에주버퍼의포멧을바꿀수없다. DSSCL_PRIORITY 우선순위협력레벨. 사운드카드에접근할수있는권한을가지게되며, 주사운드버퍼의일반적인제어권이프로그래머에게있게된다. 이레벨은응용프로그램에서사운드압축과같은주버퍼의데이터형식변경작업을필요로할경우사용된다. 대부분의게임에서는이레벨을사용하고있다. Setting the Cooperative Level 다이렉트사운드가제공하는 4 가지의협력레벨 DSSCL_EXCLUSIVE 독점적협력레벨. 우선순위협력레벨과비슷하지만응용프로그램이활성화되어있을때만사운드를출력할수있다. DSSCL_WRITEPRIMARY 가장높은협력레벨로서, 이레벨은응용프로그램에게주버퍼의모든제어권한을준다. 즉, 재생, 정지등의동작들을프로그래머가직접제어해야한다. 자신만의사운드믹서, 사운드엔진을만들때에만이레벨을사용한다.
Setting the Cooperative Level 협력레벨을설정하기위해서 SetCooperativeLevel 함수를사용한다. HRESULT SetCooperativeLevel(HWND hwnd, DWORD dwlevel); hwnd 협력레벨바꾸기를요청하려는응용프로그램의윈도우핸들 dwlevel 협력레벨 Setting the Cooperative Level HRESULT hr; // Create the DirectSound device LPDIRECTSOUND8 g_pds = NULL; hr = DirectSoundCreate8(NULL, &g_pds, NULL); // Set the DirectSound cooperative level hr = g_pds->setcooperativelevel(hwnd, DSSCL_PRIORITY); if (FAILED(hr)) return false; Sound Files 사운드데이터는다이렉트사운드의보조버퍼로읽혀서사용된다. 사운드데이터는정적버퍼또는스트리밍버퍼로읽혀들인다. 정적버퍼 (Static buffer) 사운드전체를읽어들일수있는고정된길이의버퍼이다. 스트리밍버퍼 (Streaming buffer) 버퍼가수용할수있는양보다더큰대용량사운드를사용할때스트리밍버퍼를사용된다. 작은버퍼가사용된다. 사운드데이터의일부가계속읽어들이면서재생된다. The Secondary Buffer 사운드데이터가재생되는단계 다이렉트사운드는오디오데이터를저장하는데버퍼를사용한다. 재생하려는오디오데이터를저장하기위해보조버퍼를생성해야한다. 버퍼가생성된후에사운드전체를버퍼에올려놓는다. ( 또는스트리밍사운드의경우사운드의일부를올려놓는다.) 그리고사운드를재생한다. 다이렉트사운드는여러개의보조버퍼를지원하며동시에재생가능하다. 이때여러개의재생되는사운드는주버퍼에서믹싱된다. 보조버퍼를생성하기전에사운드의포멧을알아야한다. 다이렉트사운드는같은포멧으로버퍼를사용해야한다. 예를들어, 16-bit 2-채널 WAV파일경우보조버퍼도이파일포멧으로생성되어야한다.
WAVEFORMATEX structure WAVEFORMATEX structure 다이렉트사운에서버퍼의포멧지정 : WAVEFORMATEX 만약 wav 파일포멧이알려진것이라면, 일반적인 WAVEFORMATEX 구조체를생성한다. 만약파일포멧이모르는것이라면, 오디오파일을열은후에이구조체의값을채워야한다. typedef struct { WORD wformattag; // 오디오종류 (1-채널, 2-채널 ) WORD nchannels; // 채널수 DWORD nsamplespersec; // 샘플링주기 DWORD navgbytespersec; // 평균데이터전송비율 WORD nblockalign; // 블럭당전체데이터 WORD wbitspersample;// 샘플당비트수 (8, 16) WORD cbsize; // 추가적인바이트수 } WAVEFORMATEX; wformattag 1-채널또는 2-채널의 PCM (Pulse Code Modulation) 자료의경우이값은 WAVE_FORMAT_PCM이다. nchannels 사운드의채널수. 1일때는MONO이고 2일때는STEREO이다. nsamplespersec 샘플링주기, 즉주파수 (Mhz). 8.0 khz, 11.025 khz, 22.05 khz, 44.1 khz navgbytespersec 평균데이터-전송비율 (in bytes per second) nblockalign 블럭단위바이트 (in bytes). wformattag 의자료의최소구성단위이다. nchannels * wbitspersample / 8 wbitspersample 샘플당비트수 (8 또는 16) cbsize 추가적으로더필요한바이트수. 항상 0 이다. The Secondary Buffer 보조사운드버퍼구조체 : DSBUFFERDESC typedef struct { DWORD dwsize; // DSBUFFERDESC 구조체크기 DWORD dwflags; // 보조사운드버퍼의플래그 DWORD dwbufferbytes; // 사운드버퍼의크기 DWORD dwreserved; // 예약이끝난상태 DLPWAVEFORMATEX lpwfxformat; // WAVEFORMATEX 정보 GUID guid3dalgorithm; // 2 스피커가상알고리즘 GUID 식별자 } DSBUFFERDESC, *LPDSBUFFERDESC; DSBUFFERDESC structure dwsize DSBUFFERDESC 구조체의크기 (in bytes) dwflags 보조사운드버퍼의플래그정보를지정 dwbufferbytes 새로운사운드버퍼의크기 (in bytes) 이버퍼가가지고있는사운드자료의바이트수 dwreserved 사용하지않는다. 항상 0이다. lpwfxformat WAVEFORMATEX 구조체정보를받는다. guid3dalgorithm 사용하는두개의스피커가상알고리즘 GUID 식별자
DSBUFFERDESC structure dwflags 에자주사용되는플래그 DSBCAPS_CTRLALL: 버퍼는모든제어기능을가진다. DSBCAPS_CTRLDEFAULT: 버퍼는기본제어옵션을가진다. 이값은 DSBCAPS_CTRLVOLUME, DSBCAPS_CTRLFREQUENCY 를지정하는것과동일하지만, DirectX6.0 이후부터없어졌다. DSBCAPS_CTRLFREQUENCY: 버퍼가주파수제어기능을가진다. DSBCAPS_CTRLPAN: 버퍼가팬 (pan) 기능을가진다. DSBCAPS_CTRLVOLUME: 버퍼가볼륨제어기능을가진다. DSBCAPS_STATIC: 버퍼가정적사운드데이터에사용될것임을알린다. 대부분하드웨어 ( 사운드카드 ) 메모리에생성한다. DSBCAPS_LOCHARDWARE: 메모리가사용가능하다면하드웨어메모리에사운드버퍼를생성하며하드웨어믹싱을사용한다. DSBCAPS_LOCSOFTWARE: 시스템메모리 (RAM) 에사운드버퍼를생성하며소프트웨어믹싱을사용한다. DSBCAPS_PRIMARYBUFFER: 주사운드버퍼로생성한다. 이플래그를주지않으면기본값으로보조사운드버퍼로생성된다. Creating a Secondary Buffer DSBUFFERDESC 구조체를지정한후, 보조사운드버퍼를생성한다. HRESULT CreateSoundBuffer(LPCDSBUFFERDESC pcdsbufferdesc, LPDIRECTSOUNDBUFFER *ppdsbuffer, LPUNKNOWN punkouter); pcdsbufferdesc 이미지정된 DSBUFFERDESC 구조체를가리킨다. ppdsbuffer 새롭게생성된버퍼를가질변수를가리킨다. punkouter 제어하고자하는객체의 IUnknown interface 를가리킨다. 항상 NULL 이다. Creating a Secondary Buffer // Define a WAVEFORMATEX structure WAVEFORMATEX wfx; // Clear the structure to all zeros ZeroMemory(&wfx, sizeof(waveformatex)); // Set the format to WAVE_FORMAT_PCM wfx.wformattag = (WORD) WAVE_FORMAT_PCM; wfx.nchannels = 2; // set channels by 2 wfx.nsamplespersec = 22050; wfx.wbitspersample = 16; wfx.nblockalign = (WORD) (wfx.wbitspersample / 8 * wfx.nchannels); wfx.navgbypersec = (DWORD) (wfx.nsamplespersec * wfxnblockalign); Creating a Secondary Buffer DSBUFFERDESC dsbd; ZeroMemory(&dsbd, sizeof(dsbufferdesc)); dsbd.dwsize = sizeof(dsbufferdesc); dsbd.dwflags = 0; dsbd.dwbufferbytes = 64000; dsbd.guid3dalgorithm = GUID_NULL; dsbd.lpwfxformat = &wfx; LPDIRECTSOUNDBUFFER DSBuffer = NULL; hr = g_pds->createsoundbuffer(&dsbd, &DSBuffer, NULL); if (FAILED(hr)) return NULL;
Locking the Sound Buffer 사운드버퍼의잠금 사운드버퍼잠금은버퍼에서사운드데이터를제어하고바꿀수있게한다. 잠금후에는버퍼에사운드데이터를불러올수있다. 사용후에잠금상태를풀어줘야한다. HRESULT Lock( DWORD dwoffset, DWORD dwbytes, LPVOID *ppvaudioptr1, LPDWORD pdwaudiobytes1, LPVOID *ppvaudioptr2, DPDWORD pdwaudiobytes2, DWORD dwflags); Locking the Sound Buffer dwoffset 어디서부터잠글것인가를설정 dwbytes 얼마만큼잠글것인가를설정 (in bytes) ppaudioptr1 잠글버퍼에서첫번째영역의주소 pdwaduiobytes1 첫번째영역의길이 (in bytes) pdwaudioptr2 잠글버퍼에서두번째영역의주소 만약사운드데이터로버퍼전체를채운다면, NULL 을사용한다. pdwaudiobytes2 두번째영역의길이 (in bytes) pdwaudioptr2 가 NULL 이면, 이값도 NULL 을사용한다. Locking the Sound Buffer dwflags 어떻게잠글것인가를설정하는플래그 DSBLOCK_FROMWRITECURSOR: 버퍼의현재기록커서 (write cursor) 로부터잠겨지게된다. DSBLOCK_ENTIREBUFFER: 전체버퍼가잠겨지게된다. 이플래그가설정되면, dwbytes 값은무시한다. 두번째영역 (100 bytes) 1000 bytes 첫번째영역 (900 bytes) 첫번째영역의시작주소 pdwaudioptr1 두번째영역의시작주소 pdwaudioptr2 첫번째버퍼에있는사운드를재생하는동안에두번째버퍼에는나머지사운드부분을저장한다. 그리고, 첫번째버퍼에있는사운드재생이끝나면곧바로두번째버퍼에저장된사운드를재생하게된다. 이렇게해서, 사운드의처리속도를빠르게하고사운드재생이끊기지않고반복될수있게한다. Unlocking the Sound Buffer 사운드버퍼의잠금상태를풀어야한다. 사운드데이터를버퍼에복사하고난후, Unlock 을한다. HRESULT Unlock(LPVOID pvaudioptr1, DWORD dwaudiobytes1, LPVOID pvaudioptr2, DWORD dwaudiobytes2); pvaudioptr1 잠금에서사용됐던첫번째영역의주소 dwaudiobytes1 pvaudioptr1에사용됐던첫번째영역의길이 (in bytes) pvaudioptr2 잠금에서사용됐던두번째영역의주소 dwaudiobytes2 pvaudioptr2 에사용됐던두번째영역의길이 (in bytes)
Reading the Sound Data into the Buffer Reading the Sound Data into the Buffer Loading Sound Data 다이렉트사운드에는사운드파일을불러오는기능이없다. 따라서, DirectX DSK에포함되어있는 dsutil.cpp 사용하여사운드파일을불러오는것을할것이다. Loading Sound Data Process 1. CWaveFile 객체를생성한다. 2. WAV 파일을 Open한다. 3. WAV 데이터를담고있을보조버퍼를생성한다. 4. 버퍼를잠근다. 5. 사운드데이터를읽고복사한다. 6. 버퍼잠금상태를푼다. 1. CWaveFile 객체를생성한다. CWaveFile wavfile = new CWaveFile(); 2. WAV 파일을 Open 한다. 아래는 test.wav 파일을열고읽는예제이다. 만약파일에데이터가없다면 ( 즉, size=0) 이면중단한다. // open test.wav wavfile->open( test.wav, NULL, WAVEFILE_READ); // Check to make sure that the size of data within the wave file is valid if (wavfile->getsize() == 0) return false; 3. WAV 데이터를담고있을보조버퍼를생성한다. Reading the Sound Data into the Buffer 4. 버퍼를잠근다. HRESULT hr; VOID *pdslockedbuffer = NULL; // pointer to locked buffer memory DWOR dwdslockedbuffersize = 0; // size of the locked buffer // Start the beginning of the buffer hr = DSBuffer->Lock(0, // This assumes a buffer of 64000 bytes 64000, // The variable holds a pointer to the start of the buffer &pdslockedbuffer, // holds the size of the locked buffer &dwdslockedbuffersize, NULL, // No secondary is needed NULL, // No secondary is needed DSBLOCK_ENTIREBUFFER); // Lock the entire buffer if (FAILED(hr)) return NULL; Reading the Sound Data into the Buffer 5. 사운드데이터를읽고복사한다. WAV 파일을열고데이터를읽기전에 resetfile 을한다. 그리고나서, 데이터를 read 한다. HRESULT hr; // variable to hold the return code // the amount of data read from the wav file DWORD dwwavedataread = 0; // reset the WAV file to the beginning wavfile->resetfile(); // read the WAV file hr = wavfile->read((byte *) pdslockedbuffer, dwdslockedbuffersize, &dwwavedataread); if (FAILED(hr)) return NULL;
Reading the Sound Data into the Buffer 6. 버퍼잠금상태를푼다. DSBuffer->Unlock(pDSLockedBuffer, dwdslockedbuffersize, NULL, NULL); Playing Sound in a Buffer 다이렉트사운드버퍼에데이터가올려지면 Play 함수를사용하여사운드를재생한다. HRESULT Play(DWORD dwreserved1, DWORD dwpriority, DWORD dwflags); dwreserved1 항상 0 이다. dwpriority 사운드의재생우선순위 0 부터 0xFFFFFFFF 의값 버퍼생성시 DSBCAPS_LOCDEFER 플래그가지정되지않았다면, 0 으로한다. dwflags 사운드가어떻게재생될지를지정하는플래그, e.g. DSBPLAY_LOOPING DSBuffer->Play(0, 0, DSBPLAY_LOOPING); // 배경음악 loop sound Stopping a Sound 사운드재생의정지를위해서는 Stop 함수를사용한다. HRESULT Stop(); HRESULT hr; hr = DSBuffer->Stop(); if (FAILED(hr)) return false; Controling the Volume 사운드의볼륨조절은 SetVolume 함수를사용한다. 볼륨은 DSBVOLUME_MIN ( 음소거 ) 에서부터 DSBVOLUME_MAX ( 사운드의원래크기 ) 까지중에지정할수있다. HRESULT SetVolume(LONG lvolume); lvolume 0 (DSBVOLUME_MAX) 에서부터 -10000 (DSBVOLUME_MIN) 까지중의값으로지정할수있다. 현재재생중인사운드의볼륨을얻고자할때는 GetVolume 함수를사용한다. HRESULT GetVolume(LPLONG plvolume);
Panning the Sound 사운드가왼쪽오른쪽스피커사이를팬 (Panning) 하기위해서는 SetPan 함수를사용한다. 한쪽스피커에서사운드의크기를줄이면서다른쪽스피커의사운드의크기를늘리면서패닝효과를얻는다. 그래서사운드가마치움직여다니는것과같은효과를얻는다. HRESULT SetPan(LONG lpan); lpan DSBPAN_LEFT 에서부터 DSBPAN_RIGHT 까지의값을갖는다. DSBPAN_LEFT (-10000) 은오른쪽스피커의사운드가완전히안들릴때까지왼쪽스피커에서사운드크기를증가시킨다. DSBPAN_RIGHT (10000) 은반대편에서위와같이한다. DSBPAN_CENTER (0) 으로지정하면양쪽스피커의사운드크기를원음크기로되돌려놓는다. Panning the Sound 현재팬사운드값을받고자하면, GetPan 함수를사용한다. HRESULT GetPan(LPLONG plpan); NOTE: SetPan 또는 GetPan 함수를사용하기전에이콘트롤을사용한다고버퍼를지정해야한다. 즉, 보조버퍼생성시 DSBUFFERDESC 구조체에 DSBCAPS_CTRLPAN 플래그를설정해야한다. Reference 다이렉트사운드입문 http://telnet.or.kr/directx/htm/directsound.htm 다이렉트사운드 C/C++ 레퍼런스 http://telnet.or.kr/directx/htm/directsoundccreference. htm