상세 컨텐츠

본문 제목

다이렉트 사운드 간단 코드 설명

프로그래밍 관련/사운드

by AlrepondTech 2020. 9. 19. 02:05

본문

반응형

 

 

 

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

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

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

 

 

 

 

 

 


//출처 : http://www.cyworld.com/linuxzero/2622033

DirectSound 시작하기.. 초기화 & 설정

jumper 2009-06-30 09:31:36 주소복사

조회 47  스크랩 0

헤더/라이브러리 추가하기...
3줄로 요약하자...
#include <dsound.h>
#pragma comment(lib, "dsound") // 이 방식은 vc전용이다. lib추가 전처리기
#pragma comment(lib, "dxguid")

Direct Sound의 초기화...
필요한 변수
HWND hWnd;
LPDIRECTSOUND8 pDS;

hWnd
사운드를 플레이할 윈도우의 핸들이다.
pDS
direct sound를 제어할 변수이다.. 버퍼를 만들거나 기타등등

초기화 함수
DirectSoundCreate8(NULL, &pDS, NULL);
pDS->SetCooperativeLevel(hWnd, DSSCL_NORMAL);

DirectSoundCreate8
Direct Sound 객체를 생성한다.. 파라미터가 바뀔 일은 거의 없으니 자세한 내용은 msdn를 참조하라..
SetCooperativeLevel
협동 레벨을 설정한다... 다른 윈도우의 사운드와 어떤식으로 작동할지를 결정한다... 다른윈도우 소리를 음소거 시킬지, 포커스를 잃었을때 소리를 음소거시킬지 등... 특별한 일이 없다면 DSSCL_NORMAL로 설정하면 된다

그 다음으로 사운드포맷설정.. 이건 위보다 약간 길다.
사운드 포맷을 설정하기 위해서는 우선 Primary버퍼를 생성해야한다.
Primary버퍼에 포맷을 설정함으로써 DirectSound전체의 사운드포맷을 결정하는듯 하다..
그럼 바로 사운드버퍼생성 소스코드다.

DSBUFFERDESC dsbd = {0};
LPDIRECTSOUNDBUFFER pDSBPrimary;
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = DSBCAPS_PRIMARYBUFFER;
pDS->CreateSoundBuffer(&dsbd, &pDSBPrimary, NULL);

버퍼를 생성하는 것은 DSBUFFERDESC라는 구조체에 버퍼에 대한 정보를 담고 CreateSoundBuffer 함수를 호출하는 방식이다. (DirectDraw와 같은 방식이다.) 우선 Primary버퍼에 크게 설정할 내용은 없다.. (대부분 0으로 채워야한다.) 위와 같이 Primary버퍼를 생성했다면.. 이제 사운드 포맷을 설정해야한다.
다음은 그 소스코드

WAVEFORMATEX wfx = {0};
wfx.cbSize = sizeof(WAVEFORMATEX);
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = 44100;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
pDSBPrimary->SetFormat(&wfx);
pDSBPrimary->Release();

WAVEFORMATEX의 멤버는 원하는 값을 교체해도 된다. 설정후에 SetFormat함수로 설정한다.
그 후에 Primary버퍼를 Release한다. Primary버퍼는 실제 음원을 담는 버퍼는 아니기 때문에 Release해도 상관없는 듯하다..

 

 

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

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

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

 

 

DirectSound 시작하기.. 음원 재생

jumper 2009-06-30 09:32:17 주소복사

조회 173  스크랩 0

버퍼 생성하기
WAVEFORMATEX wfx = {0};
wfx.cbSize = sizeof(WAVEFORMATEX);
wfx.wFormatTag = WAVE_FORMAT_PCM;
wfx.nChannels = 2;
wfx.nSamplesPerSec = 44100;
wfx.wBitsPerSample = 16;
wfx.nBlockAlign = wfx.wBitsPerSample / 8 * wfx.nChannels;
wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;

DSBUFFERDESC dsbd = {0};
dsbd.dwSize = sizeof(DSBUFFERDESC);
dsbd.dwFlags = DSBCAPS_GLOBALFOCUS;
dsbd.lpwfxFormat = &wfx;
dsbd.dwBufferBytes = wfx.nAvgBytesPerSec * 4; // 버퍼의크기.. 여기선 4초길이의 크기로 정했다.

LPDIRECTSOUNDBUFFER pDSB;
pDS->CreateSoundBuffer(&dsbd, &pDSB, NULL);

LPDIRECTSOUNDBUFFER8 pDSB8;
pDSB->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*)&pDSB8);
pDSB->Release();

Primary버퍼생성과의 차이점은 dsbd의 설정과 DirectSoundBuffer8의 생성부분뿐이다.
dsbd.dwFlags
쓸수 있는 플래그를 알아보면..
DSBCAPS_GLOBALFOCUS
다른 프로그램에 포커스를 잃어도 계속 소리가 난다..
DSBCAPS_CTRL3D, FREQUENCY, FX, PAN, POSITIONNOTIFY, VOLUME
여러가지를 조정할수 있는 플래그.. 볼륨을 조절하고 싶다면 DSBCAPS_VOLUME를 추가할것..
기타 버퍼의 저장 위치를 결정하는 등의 플래그가 있다.. 알고 싶다면 msdn를 참조할 것.

LPDIRECTSOUNDBUFFER8 pDSB8;
이 부분은 COM을 알면 설명이 필요없다.
LPDIRECTSOUNDBUFFER으로 LPDIRECTSOUNDBUFFER8의 인터페이스를 만든 후 LPDIRECTSOUNDBUFFER를 없애고 LPDIRECTSOUNDBUFFER8만 남긴 것이다. (LPDIRECTSOUNDBUFFER8 이 더 좋은지는 모르겠지만;)


버퍼에 데이터넣기
버퍼의 수정은 Lock -> Data Copy -> Unlock의 과정을 거친다.

LPVOID lpAddr[2];
DWORD dwLen[2];
pDSB8->Lock(dwOffset, dwLockSize, &lpAddr[0], &dwLen[0], &lpAddr[1], &dwLen[1], 0);
// 이곳에 lpAddr[0]과 lpAddr[1]에 데이터 복사하는 코드
pDSB8->Unlock(lpAddr[0], dwLen[0], lpAddr[1], dwLen[1]);LPVOID lpAddr[2];

수정하는 데이터의 영역은 dwOffset ~ dwOffset+dwLockSize의 영역이다.
dwOffset + dwLockSize가 총버퍼크기를 초과할 경우 그 넘는 크기만큼을 offset 0부터 수정한다.
그럴 경우 lpAddr[1], dwLen[1]에 그 초과한 만큼의 데이터가 분담된다.
lpAddr[1]가 NULL이라면 dwOffset + dwLockSize가 총 버퍼를 크기를 넘지 않았단 얘기다.

소리 재생하기
매우 간단하다
pDSB8->Play(0, 0, DSBPLAY_LOOPING);
DSBPLAY_LOOPING 는 반복재생의 옵션이다. 필요없으면 0 을 대입하면 된다.



실제 음악파일을 재생하려면 wave, mp3등을 읽어서 raw sound data를 얻어오는 코드가 필요하다.

 

 

 

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

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

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

 

 

 

반응형

 

 

728x90

 

 

Sound 재생하기

jumper 2009-06-30 09:33:08 주소복사

조회 27  스크랩 0

이 장에서 설명할 모든 프로그램은 mmsystem.h를 include하고, winmm.lib 파일을 링크해야 한다.

관련 데이터 타입

타입 설명
HWAVEOUT 열려진 파형 오디오 출력 디바이스에 대한 핸들
HWAVEIN 열려진 파형 오디오 입력 디바이스에 대한 핸들
WAVEHDR wave 입력 또는 출력 데이터 블록에 대한 헤더로 쓰이는 구조체
WAVEFORMATEX wave 입력 또는 출력 장치에 의해 지원되는 데이터 포맷을 명시하는 구조체

WAVEFORMATEX 구조체의 멤버

변수 설명
wFormatTag 파형 오디오 형식. PCM이면 WAVE_FORMAT_PCM
nChannels 파형 오디오 데이터의 채널의 개수. 1은 모노, 2는 스테레오를 의미한다.
nSamplesPerSec 샘플링율(Sampling rate). 파형 오디오 형식이 PCM이면 이 값은 8.0kHz, 11.025kHz, 22.05kHz, 44.1kHz 중의 하나여야 한다
nAvgBytesPerSec 요구되는 average data-transfer rate. 파형 오디오 형식이 PCM이면 이 값은 샘플링율과 block alignment의 곱과 같아야 한다.
nBlockAlign block alignment. 파형 오디오 형식이 PCM이면 채널의 개수와 샘플당 비트 수를 8(bits per byte)로 나눈 값의 곱과 같아야 한다.
wBitsPerSample Bits per sample. 파형 오디오 형식이 PCM이면 이 값은 8(모노 일 때) 또는 16(스테레오 일 때)이어야 한다.
cbSize WAVEFORMATEX 구조체의 끝에 덧붙여질 추가 형식 정보의 크기

wave 출력(재생)

다음 함수들이 사용된다.

waveOutOpen 디바이스를 연다.
waveOutWrite 플레이를 시작한다.
waveOutPrepareHeader 헤더 초기화
waveOutUnprepareHeader 끝내기 위한 헤더 정리
waveOutReset  
waveOutClose  

wave의 출력을 위해서는 waveOutOpen함수를 사용하여 파형 오디오 출력 디바이스를 열어야 한다. 이 함수는 파라미터로 주어지는 디바이스 식별자와 관계된 디바이스를 열고 그 디바이스에 대한 핸들을 파라미터로 지정된 메모리 위치에 써놓음으로써 리턴 한다.

파형 오디오 출력 디바이스를 열었으면 이제 waveOutWrite함수를 사용하여 음성 데이터 블록을 오디오 출력 디바이스로 보내면 오디오 출력 디바이스는 이를 디코딩(디코딩)한다. waveOutWrite함수에 보낼 파형 오디오 데이터 블록을 지정하기 위해서는 WAVEHDR 구조체를 사용한다. 이 구조체는 데이터 블록에 대한 포인터와 데이터 블록의 크기와 몇 가지의 플래그를 포함한다. 이 데이터 블록은 사용 전에 파형 오디오 출력 디바이스가 사용 할 수 있게끔 waveOutPrepareHeader함수를 사용해 준비되어져야 한다.

데이터 블록을 waveOutWrite함수를 사용해 오디오 출력 디바이스에 보낸 후, 디바이스 드라이버가 그 데이터 블록에 대한 처리를 끝내고 나면 이 데이터 블록을 해제시킬 수 있다. WAVEHDR 구조의 멤버 변수인 lpData는 파형 오디오 데이터 샘플들에 대한 포인터이다.

파형 오디오 디코딩을 관리하도록 window procedure 함수 또는 애플리케이션에 의해 제공되는 파형 오디오 출력 장치에 대한 콜백 함수에는 다음과 같은 메시지들이 올 수 있다.

message 설명
MM_WOM_CLOSE waveOutClose함수를 사용하여 디바이스를 닫았을 때
MM_WOM_DONW 디바이스 드라이버가 waveOutWrite함수를 사용하여 보내진 데이터 블록에 대한 처리를 마쳤을 때
MM_WOM_OPEN waveOutOpen함수를 사용하여 디바이스를 열었을 때

이 메시지들에 대한 wParam은 항상 열려진 파형 오디오 디바이스에 대한 핸들 값이다. MM_WOM_DONE(WOM_DONE)의 경우에는 이 메시지의 파라미터로 전달받는 lParam의 값은 디코딩이 끝난 데이터 블록에 대한 WAVEHDR 구조체에 대한 포인터 값이다. 이를 가지고 디코딩이 끝난 데이터 블록을 해제(free)시킬 수 있다.

wave 입력(녹음)

waveInOpen 디바이스 장치를 연다. 버퍼가 가득찼을 경우 callback부분을 결정한다.
waveInPrepareHeader 헤더 초기화
waveInAddBuffer 디바이스 장치에 버퍼를 연결해 준다.
waveInStart 녹음을 시작한다.
waveInUnprepareHeader 끝내기 위한 헤더 정리
waveInStop 녹음을 끝낸다.
waveInReset  
waveInClose  

waveInOpen함수는 Wave 녹음 디바이스를 여는 함수이다. 이 함수에서 디바이스를 초기화를 한다. 디바이스를 받을 핸들을 주고, 디바이스 이름, 샘플링 방식, CallBack 함수, CallBack Window를 결정하여 디바이스를 초기화 한다. 다음 음원을 저장해둘 메모리를 잡는다.(GlobalAlloc) 그리고, 그 메모리에 Lock을 건다.(GlobalLock) 버퍼의 헤더를 초기화하여(waveInPrepareHeader, waveInAddBuffer) 녹음을 시작한다.(waveInStart) 그 후 녹음을 끝낸 후 메모리를 해제한다.(GlobalUnLock, GlobalFree)

WAVEFORMATEX fmt;
fmt.wFormatTag = WAVE_FORMAT_PCM;
fmt.nChannels = 1; // 1이면 Mono, 2이면 Stereo
fmt.nSamplesPerSec = 8000; // sample rate
fmt.nAvgBytesPerSec = 8000; // for buffer estimation
fmt.nBlockAlign = 1; // block size of data
fmt.wBitsPerSample = 8; // number of bits per sample of mono data
fmt.cbSize = 0; // the count of size of extra information

// MM_WIM_DATA 관련 메세지를 hwnd로 보내라
waveInOpen(&m_hWaveIn,WAVE_MAPPER,&fmt,(DWORD)hwnd,0,CALLBACK_WINDOW);

// 2초 동안 PCM방식으로 RECORDING 할려면 16kbyte가 필요하다.(2초*8KB=16KB)
m_hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, (DWORD)16000);

// DEVICE DRIVER에 넘겨줄 BUFFER의 헤더 구조체를 초기화한다.
WAVEHDR hdr;
hdr.lpData = (LPSTR)GlobalLock(m_hData);
hdr.dwBufferLength = 16000;
hdr.dwFlags = 0L;
hdr.dwLoops = 0L;

// 헤더를 넘겨준다.
waveInPrepareHeader(m_hWaveIn, &hdr, sizeof(WAVEHDR));

//Buffer를 Device Driver에게 넘겨 준다.
waveInAddBuffer(m_hWaveIn, &hdr, sizeof(WAVEHDR));

//녹음을 시작한다.
waveInStart( m_hWaveIn);

GlobalUnLock(hData);
GlobalFree(hData);

message 설명
MM_WIM_CLOSE waveInClose함수를 사용하여 디바이스를 닫았을 때
MM_WIM_DATA 디바이스 드라이버가 waveInAddBuffer함수를 사용하여 보내어진 데이터 블록을 인코딩된 오디오 데이터로 채웠을 
MM_WIM_OPEN waveInOpen함수를 사용하여 디바이스를 열었을 때

MM_WIM_DATA의 파라미터로 전달되는 lParam은 버퍼를 식별하는 WAVEHDR 구조체에 대한 포인터의 값을 가진다. 이 버퍼는 파형 오디오 데이터로 완전히 채워지지 않았을 수도 있다. 버퍼가 채워지기 전에 인코딩이 정지될 수도 있기 때문이다. 따라서 버퍼의 유용한 데이터의 양을 알아내기 위해 WAVEHDR 구조체의 멤버 변수인 dwBytesRecorded를 사용한다. lParam을 가지고 애플리케이션이 그 데이터 블록에 대한 사용(본 화상회의 애플리케이션의 경우 네트워크로 전송)을 끝마쳤을 때 그 데이터 블록을 해제할 수 있다.

wave 파일 구조

웨이브 파일은 aiff, mid 파일등과 같은 RIFF 파일 포맷의 일종이고, 이러한 포맷들은 전체적으로 1개의 RIFF chunk로 구성되어 있습니다. chunk란 것은,

id(4바이트) + size(4바이트) + data(size 바이트)

라는 형식을 만족시키는 2진수 덩어리라고 생각하시면 됩니다. id에는 chunk type이 4글자 ASCII로 저장되며, 미리 size가 제공되므로, 이러한 chunk type을 지원하지 않는 어플리케이션은 그 바이트수만큼 skip해야 함을 알려줍니다. size정보는 또한 C 프로그래머들에게 메모리 할당량을 미리 알려주는 역할도 합니다.

data 영역에는 어떤 정보든지 올 수 있으며, 이 영역이 의미하는 바는 id에 따라 달라집니다. 심지어 data 영역에 또다른 chunk가 위치하여 chunk들은 계층적으로 구성될 수도 있습니다. 마치 우리가 분석할 wave 파일의 경우처럼 말입니다.

RIFF chunk

앞에서 설명했듯이 wave 파일 전체는 1개의 RIFF chunk로 이루어져 있습니다. 이 chunk의 id는 "RIFF"입니다. 그 다음으로 size가 이어지며, 이 값은 filesize-8이 됩니다.(왜 그럴까요?) "wave 파일의 경우" 이 chunk는 다음과 같은 data를 가집니다.

설명 크기
chunk id 4 "RIFF"
chunk size 4 filesize-8
설명 크기
RIFF type 4 "WAVE"
포멧 chunk 24 아래에 설명
데이터 chunk 가변 아래에 설명

RIFF chunk의 데이터 영역에 어플리케이션이 정의한 또다른 chunk들이 와도 되지만 항상 첫 번째와 두 번째 chunk는 포맷 chunk와 데이터 chunk여야 합니다. 그러므로 이 포맷 chunk와 데이터 chunk만 알아보면 이제 wave 파일에 관한 이야기는 끝입니다.

포멧 chunk

이 chunk의 id는 "fmt "입니다. fmt라는 글자 뒤에 빈칸이 하나 들어있음에 주의하십시오. 이 chunk의 size는 항상 16입니다. 이어지는 data에는 wave 파일의 정보가 들어있습니다.

설명 크기
chunk id 4 "fmt "
chunk size 4 항상 16
설명 크기
wave 포멧 2 항상 1
현재 채널수 2 모노이면 1 / 스테레오이면 2
샘플링rate 4 11025, 22050, 44100 등등
1초당 바이트수 4 샘플링rate*채널/8
샘플당 사용할 바이트수 2  
샘플당 비트수 2  

데이터 chunk

이 chunk의 id는 "data"입니다. 이 chunk의 data에는 비로소 우리가 원하는 압축되지 않은 PCM 데이터가 들어있습니다. wave 파일 전체로 보면 44번째 바이트부터 PCM 데이터가 위치하는 것입니다. 만일 포맷을 알고 있다면 바로 이곳으로 접근할 수 있겠지요.

설명 크기
chunk id 4 "data"
chunk size 4  
설명 크기
PCM 데이터 가변  

샘플링 비트수가 8비트 이하인 경우, PCM 데이터는 unsigned 형태로 저장됩니다. 그러나 샘플링 비트수가 8비트 이상인 경우, PCM 데이터는 2의 보수 signed 형태로 저장됩니다. 이러한 불일치는 8비트 PCM이 널리 사용되고, 16비트는 아직 많이 사용되지 않을 무렵 Microsoft에서 정한 것입니다. 이것은 프로그래밍하기를 약간 까다롭게 만듭니다.

샘플당 비트수와 샘플당 사용할 바이트수가 따로 존재하므로, 이들 둘이 맞아떨어지지 않을 수가 있습니다.(샘플당 바이트수!=샘플당 비트수/8) 이 경우 left align되고, 남는 bit들은 0으로 채워집니다. 아래에 그 예제가 있습니다.(샘플당 바이트수=2, 샘플당 비트수=12)

1 0 1 0 0 0 1 1 1 0 0 1 0 0 0 0
샘플 데이터 padding bits

여러개의 채널이 존재할 경우, 한 개의 샘플 프레임에 여러개의 채널이 순서대로 옵니다. 즉, 각 채널별로 따로 곡의 처음부터 끝까지 저장되는 것이 아닙니다.

프레임 0 프레임 1 프레임 N
채널1 채널2 채널1 채널2 채널1 채널2

음향 메이나들에게는 익숙한 대표적인 멀티 채널 구성은 미리 정의되어 있습니다.

채널 1 2 3 4 5 6
stereo 왼쪽 오른쪽        
3 채널 왼쪽 오른쪽 가운데      
quad 왼쪽 앞 오른쪽 앞 왼쪽 뒤 오른쪽 뒤    
4 채널 왼쪽 가운데 오른쪽 서라운드    
6 채널 왼쪽 가운데 왼쪽 가운데 오른쪽 가운데 오른쪽 서라운드

멀티미디어 I/O

chunk를 단위로 하는 RIFF 파일들을 쉽게 억세스할 수 있도록 mmio계열의 I/O 함수가 API에서 지원됩니다.

함수 이름 설명
mmioOpen RIFF 파일을 연다.
mmioClose RIFF 파일을 닫는다.
mmioSeek RIFF 파일의 특정 위치를 찾는다.
mmioRead RIFF 파일에서 데이터를 읽는다.
mmioWrite RIFF 파일에 데이터를 쓴다.
mmioDescent 현재 위치에서 하위 chunk로 내려간다.
mmioAscent 현재 위치에서 위 chunk로 올라간다.
mmioCreateChunk 새로운 chunk를 만든다.
mmioFOURCC chunk 이름을 등록한다.
mmioStringToFOURCC 문자열을 chunk 이름으로 등록한다.

파일을 읽으려면

HMMIO hmmio = mmioOpen((LPSTR)"test.wav", NULL, MMIO_READ);

부모 chunk를 찾기 위해서는

MMCKINFO MMCkInfoParent;
MMCkInfoParent.ckid = mmioFOURCC('W','A','V','E');
mmioDescend(hmmio, &MMCkInfoParent, NULL, MMIO_FINDRIFF);

포멧 chunk를 찾기 위해서는

MMCKINFO MMCkInfoChild;
MMCkInfoChild.ckid = mmioFOURCC('f','m','t',' ');
mmioDescend(hmmio, &MMCkInfoChild, &MMCkInfoParent, MMIO_FINDCHUNK);

포멧 chunk에서 wave format을 얻어오기 위해서는

PCMWAVEFORMAT WaveFormat;
mmioRead(hmmio, (LPSTR)&WaveFormat, MMCkInfoChild.cksize);

다시 부모 chunk로 올라가기 위해서는

mmioAscend(hmmio, &MMCkInfoChild, 0);

데이터 chunk로 내려가기 위해서는

MMCkInfoChild.ckid = mmioFOURCC('d','a','t','a');
mmioDescend(hmmio, &MMCkInfoChild, &MMCkInfoParent, MMIO_FINDCHUNK);

데이터를 읽어오기 위해서는

DWORD lDatasize;
lDatasize = MMCkInfoChild.cksize;
unsigned char* pWave;
pWave = (unsigned char*)malloc(lDatasize);
mmioRead(hmmio, pWave,lDatasize);

데이터를 쓸 때는 mmioCreateChunk 함수를 사용하여 Descend 하고 mmioWrite 함수를 이용하여 내용을 쓴 다음 mmioAscend 함수를 사용하여 Ascend하면 chunk size의 내용은 Windows가 알아서 저장해 준다. 반드시 CreateChunk 수와 Ascend 수는 같아야 합니다.

MCI를 이용한 출력(재생)

MCI를 이용한 입력(녹음)

UINT wDeviceID;
MCI_OPEN_PARMS mciOpenParms;
MCI_RECORD_PARMS mciRecordParms;
MCI_SAVE_PARMS mciSaveParms;
mciOpenParms.lpstrDeviceType="waveaudio";
mciOpenParms.lpstrElementName="";
mciSendCommand(0,MCI_OPEN,MCI_OPEN_ELEMENT|MCI_OPEN_TYPE,(long int)(void *)&mciOpenParms);
wDeviceID=mciOpenParms.wDeviceID;
mciRecordParms.dwTo = millisecond단위의 길이가 들어옵니다.;
mciSendCommand(wDeviceID,MCI_RECORD,MCI_TO|MCI_WAIT,(long int)(void *)&mciRecordParms);
mciSaveParms.lpfilename = 웨이브 파일의 파일명이 들어옵니다;
mciSendCommand(wDeviceID,MCI_SAVE,MCI_SAVE_FILE|MCI_WAIT,(long int)(void *)&mciSaveParms);
mciSendCommand(wDeviceID,MCI_CLOSE,0,NULL);

 

 

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

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

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

 

 

반응형

'프로그래밍 관련 > 사운드' 카테고리의 다른 글

게임사운드 플레이(테스트)  (0) 2020.09.19
DirectSound C++ Document  (0) 2020.09.19
다이렉트사운드(DirectSound) 관련  (0) 2020.09.17
DirectSound 설명(best) 관련  (0) 2020.09.17
D3Direct Sound 관련  (0) 2020.09.17


관련글 더보기

댓글 영역