Thinking Different




Tutorial 14 - Direct Sound



원문 : http://www.rastertek.com/dx11tut14.html



이번 튜토리얼에서는 DirectX 11에서의 Direct sound 기초와 .wav 파일을 로드하고 재생하는 방법을 다룹니다. 이 튜토리얼은 이전의 DirectX 11 튜토리얼들에 기초합니다. 튜토리얼을 시작하기에 앞서 DirectX 11에서의 Direct sound와 사운드 포맷을 몇 가지 알아보도록 하겠습니다.


우선, DirectX 11의 Direct sound API는 DirectX 8의 그것과 같습니다. 크게 다른 점이 있다면 최근의 윈도우 OS에서 하드웨어 레벨의 사운드 믹싱이 되지 않는다는 것입니다. 그 이유인즉슨, 모든 하드웨어 호출이 이제는 모두 보안 계층으로 들어갔기 때문에 접근이 허용되지 않게 되었기 때문입니다. 오래된 사운드 카드들은 상대적으로 빠른 DMA(Direct Memory Access)를 지원하지만 오늘날의 윈도우 보안 체계에서 작동하지 않습니다. 따라서 현재는 사운드 믹싱이 소프트웨어 레벨에서 이루어지기 때문에 이 API들은 직접적인 하드웨어 가속이 지원되지 않습니다.


Direct sound의 장점은 원하는 어떤 종류의 오디오 포맷이든지 잘 지원한다는 것입니다. 이 튜토리얼에서는 .wav 파일을 사용하지만 이것 대신 .mp3나 다른 포맷도 사용 가능합니다. 또한 여러분만의 포맷을 만들어 사용하는 것도 가능합니다. Direct sound는 너무 간단해서 단지 사운드 버퍼를 생성하고 불러올 오디오 포맷을 버퍼의 포맷에 복사하기만 해도 재생 준비는 끝이 납니다. 곧 어째서 이 단순함이 많은 어플리케이션들이 direct sound를 사용하게 되었는지 알게 되실 겁니다.


알아두어야 할 것은 Direct sound에서는 1차 버퍼와 2차 버퍼로 두 버퍼를 나누어 사용한다는 것입니다. 1차 버퍼는 사운드 카드나 USB 헤드셋과 같은 것에 있는 메인 사운드 버퍼입니다. 2차 버퍼는 실제로 어플리케이션에서 생성하고 그곳에 소리가 로드되는 메모리 영역입니다. 2차 버퍼의 소리를 재생할 때 Direct sound API에서는 이들을 1차 버퍼의 소리들과 잘 혼합하여 재생하게 됩니다. 만약 여러 개의 2차 버퍼들을 동시에 재생한다면 전부 혼합되어 1차 버퍼에서 재생됩니다. 또한 이 버퍼들은 앞뒤가 이어져 있기 때문에(circular) 무한히 반복 재생되게 할 수 있습니다.


튜토리얼을 시작하기 위해 먼저 업데이트 된 프레임워크를 살펴 보겠습니다. 새로운 클래스인 SoundClass 클래스가 DirectSound와 .wav 기능을 담당합니다. 다른 클래스들은 단순함을 유지하기 위해 없앴습니다.



프레임워크







새롭게 추가된 SoundClass를 살펴보겠습니다. 이 클래스는 DirectSound 기능과 .wav 오디오로드 및 재생 기능을 캡슐화합니다.


Soundclass.h


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#pragma once
 
class SoundClass
{
private:
    struct WaveHeaderType
    {
        char chunkId[4];
        unsigned long chunkSize;
        char format[4];
        char subChunkId[4];
        unsigned long subChunkSize;
        unsigned short audioFormat;
        unsigned short numChannels;
        unsigned long sampleRate;
        unsigned long bytesPerSecond;
        unsigned short blockAlign;
        unsigned short bitsPerSample;
        char dataChunkId[4];
        unsigned long dataSize;
    };
 
public:
    SoundClass();
    SoundClass(const SoundClass&);
    ~SoundClass();
 
    bool Initialize(HWND);
    void Shutdown();
 
private:
    bool InitializeDirectSound(HWND);
    void ShutdownDirectSound();
 
    bool LoadWaveFile(char*, IDirectSoundBuffer8**);
    void ShutdownWaveFile(IDirectSoundBuffer8**);
 
    bool PlayWaveFile();
 
private:
    IDirectSound8* m_DirectSound = nullptr;
    IDirectSoundBuffer* m_primaryBuffer = nullptr;
    IDirectSoundBuffer8* m_secondaryBuffer1 = nullptr;
};
cs



Soundclass.cpp


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
#include "stdafx.h"
#include "SoundClass.h"
 
 
SoundClass::SoundClass()
{
}
 
 
SoundClass::SoundClass(const SoundClass& other)
{
}
 
 
SoundClass::~SoundClass()
{
}
 
bool SoundClass::Initialize(HWND hwnd)
{
    // Initialize direct sound and the primary sound buffer.
    if (!InitializeDirectSound(hwnd))
    {
        return false;
    }
 
    // Load a wave audio file onto a secondary buffer.
    if (!LoadWaveFile("../Dx11Demo_14/data/sound01.wav"&m_secondaryBuffer1))
    {
        return false;
    }
 
    return PlayWaveFile();
}
 
 
void SoundClass::Shutdown()
{
    // Release the secondary buffer.
    ShutdownWaveFile(&m_secondaryBuffer1);
 
    // Shutdown the Direct Sound API.
    ShutdownDirectSound();
}
 
 
bool SoundClass::InitializeDirectSound(HWND hwnd)
{
    // Initialize the direct sound interface pointer for the default sound device.
    if (FAILED(DirectSoundCreate8(NULL&m_DirectSound, NULL)))
    {
        return false;
    }
 
    // Set the cooperative level to priority so the format of the primary sound buffer can be modified.
    if (FAILED(m_DirectSound->SetCooperativeLevel(hwnd, DSSCL_PRIORITY)))
    {
        return false;
    }
 
    // Setup the primary buffer description.
    DSBUFFERDESC bufferDesc;
    bufferDesc.dwSize = sizeof(DSBUFFERDESC);
    bufferDesc.dwFlags = DSBCAPS_PRIMARYBUFFER | DSBCAPS_CTRLVOLUME;
    bufferDesc.dwBufferBytes = 0;
    bufferDesc.dwReserved = 0;
    bufferDesc.lpwfxFormat = NULL;
    bufferDesc.guid3DAlgorithm = GUID_NULL;
 
    // Get control of the primary sound buffer on the default sound device.
    if (FAILED(m_DirectSound->CreateSoundBuffer(&bufferDesc, &m_primaryBuffer, NULL)))
    {
        return false;
    }
 
    // Setup the format of the primary sound bufffer.
    // In this case it is a .WAV file recorded at 44,100 samples per second in 16-bit stereo (cd audio format).
    WAVEFORMATEX waveFormat;
    waveFormat.wFormatTag = WAVE_FORMAT_PCM;
    waveFormat.nSamplesPerSec = 44100;
    waveFormat.wBitsPerSample = 16;
    waveFormat.nChannels = 2;
    waveFormat.nBlockAlign = (waveFormat.wBitsPerSample / 8* waveFormat.nChannels;
    waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
    waveFormat.cbSize = 0;
 
    // Set the primary buffer to be the wave format specified.
    if (FAILED(m_primaryBuffer->SetFormat(&waveFormat)))
    {
        return false;
    }
 
    return true;
}
 
 
void SoundClass::ShutdownDirectSound()
{
    // Release the primary sound buffer pointer.
    if (m_primaryBuffer)
    {
        m_primaryBuffer->Release();
        m_primaryBuffer = 0;
    }
 
    // Release the direct sound interface pointer.
    if (m_DirectSound)
    {
        m_DirectSound->Release();
        m_DirectSound = 0;
    }
}
 
 
bool SoundClass::LoadWaveFile(char* filename, IDirectSoundBuffer8** secondaryBuffer)
{
    // Open the wave file in binary.
    FILE* filePtr = nullptr;
    int error = fopen_s(&filePtr, filename, "rb");
    if (error != 0)
    {
        return false;
    }
 
    // Read in the wave file header.
    WaveHeaderType waveFileHeader;
    unsigned int count = fread(&waveFileHeader, sizeof(waveFileHeader), 1, filePtr);
    if (count != 1)
    {
        return false;
    }
 
    // Check that the chunk ID is the RIFF format.
    if ((waveFileHeader.chunkId[0!= 'R'|| (waveFileHeader.chunkId[1!= 'I'||
        (waveFileHeader.chunkId[2!= 'F'|| (waveFileHeader.chunkId[3!= 'F'))
    {
        return false;
    }
 
    // Check that the file format is the WAVE format.
    if ((waveFileHeader.format[0!= 'W'|| (waveFileHeader.format[1!= 'A'||
        (waveFileHeader.format[2!= 'V'|| (waveFileHeader.format[3!= 'E'))
    {
        return false;
    }
 
    // Check that the sub chunk ID is the fmt format.
    if ((waveFileHeader.subChunkId[0!= 'f'|| (waveFileHeader.subChunkId[1!= 'm'||
        (waveFileHeader.subChunkId[2!= 't'|| (waveFileHeader.subChunkId[3!= ' '))
    {
        return false;
    }
 
    // Check that the audio format is WAVE_FORMAT_PCM.
    if (waveFileHeader.audioFormat != WAVE_FORMAT_PCM)
    {
        return false;
    }
 
    // Check that the wave file was recorded in stereo format.
    if (waveFileHeader.numChannels != 2)
    {
        return false;
    }
 
    // Check that the wave file was recorded at a sample rate of 44.1 KHz.
    if (waveFileHeader.sampleRate != 44100)
    {
        return false;
    }
 
    // Ensure that the wave file was recorded in 16 bit format.
    if (waveFileHeader.bitsPerSample != 16)
    {
        return false;
    }
 
    // Check for the data chunk header.
    if ((waveFileHeader.dataChunkId[0!= 'd'|| (waveFileHeader.dataChunkId[1!= 'a'||
        (waveFileHeader.dataChunkId[2!= 't'|| (waveFileHeader.dataChunkId[3!= 'a'))
    {
        return false;
    }
 
    // Set the wave format of secondary buffer that this wave file will be loaded onto.
    WAVEFORMATEX waveFormat;
    waveFormat.wFormatTag = WAVE_FORMAT_PCM;
    waveFormat.nSamplesPerSec = 44100;
    waveFormat.wBitsPerSample = 16;
    waveFormat.nChannels = 2;
    waveFormat.nBlockAlign = (waveFormat.wBitsPerSample / 8* waveFormat.nChannels;
    waveFormat.nAvgBytesPerSec = waveFormat.nSamplesPerSec * waveFormat.nBlockAlign;
    waveFormat.cbSize = 0;
 
    // Set the buffer description of the secondary sound buffer that the wave file will be loaded onto.
    DSBUFFERDESC bufferDesc;
    bufferDesc.dwSize = sizeof(DSBUFFERDESC);
    bufferDesc.dwFlags = DSBCAPS_CTRLVOLUME;
    bufferDesc.dwBufferBytes = waveFileHeader.dataSize;
    bufferDesc.dwReserved = 0;
    bufferDesc.lpwfxFormat = &waveFormat;
    bufferDesc.guid3DAlgorithm = GUID_NULL;
 
    // Create a temporary sound buffer with the specific buffer settings.
    IDirectSoundBuffer* tempBuffer = nullptr;
    if (FAILED(m_DirectSound->CreateSoundBuffer(&bufferDesc, &tempBuffer, NULL)))
    {
        return false;
    }
 
    // Test the buffer format against the direct sound 8 interface and create the secondary buffer.
    if (FAILED(tempBuffer->QueryInterface(IID_IDirectSoundBuffer8, (void**)&*secondaryBuffer)))
    {
        return false;
    }
 
    // Release the temporary buffer.
    tempBuffer->Release();
    tempBuffer = 0;
 
    // Move to the beginning of the wave data which starts at the end of the data chunk header.
    fseek(filePtr, sizeof(WaveHeaderType), SEEK_SET);
 
    // Create a temporary buffer to hold the wave file data.
    unsigned char* waveData = new unsigned char[waveFileHeader.dataSize];
    if (!waveData)
    {
        return false;
    }
 
    // Read in the wave file data into the newly created buffer.
    count = fread(waveData, 1, waveFileHeader.dataSize, filePtr);
    if (count != waveFileHeader.dataSize)
    {
        return false;
    }
 
    // Close the file once done reading.
    error = fclose(filePtr);
    if (error != 0)
    {
        return false;
    }
 
    // Lock the secondary buffer to write wave data into it.
    unsigned char* bufferPtr = nullptr;
    unsigned long bufferSize = 0;
    if (FAILED((*secondaryBuffer)->Lock(0, waveFileHeader.dataSize, (void**)&bufferPtr, (DWORD*)&bufferSize, NULL00)))
    {
        return false;
    }
 
    // Copy the wave data into the buffer.
    memcpy(bufferPtr, waveData, waveFileHeader.dataSize);
 
    // Unlock the secondary buffer after the data has been written to it.
    if (FAILED((*secondaryBuffer)->Unlock((void*)bufferPtr, bufferSize, NULL0)))
    {
        return false;
    }
 
    // Release the wave data since it was copied into the secondary buffer.
    delete[] waveData;
    waveData = 0;
 
    return true;
}
 
 
void SoundClass::ShutdownWaveFile(IDirectSoundBuffer8** secondaryBuffer)
{
    // Release the secondary sound buffer.
    if (*secondaryBuffer)
    {
        (*secondaryBuffer)->Release();
        *secondaryBuffer = 0;
    }
}
 
 
bool SoundClass::PlayWaveFile()
{
    // Set position at the beginning of the sound buffer.
    if (FAILED(m_secondaryBuffer1->SetCurrentPosition(0)))
    {
        return false;
    }
 
    // Set volume of the buffer to 100%.
    if (FAILED(m_secondaryBuffer1->SetVolume(DSBVOLUME_MAX)))
    {
        return false;
    }
 
    // Play the contents of the secondary sound buffer.
    if (FAILED(m_secondaryBuffer1->Play(000)))
    {
        return false;
    }
 
    return true;
}
cs






Systemclass.h


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#pragma once
 
 
class InputClass;
class GraphicsClass;
class SoundClass;
 
 
class SystemClass
{
public:
    SystemClass();
    SystemClass(const SystemClass&);
    ~SystemClass();
 
    bool Initialize();
    void Shutdown();
    void Run();
 
    LRESULT CALLBACK MessageHandler(HWND, UINT, WPARAM, LPARAM);
 
private:
    bool Frame();
    void InitializeWindows(int&int&);
    void ShutdownWindows();
 
private:
    LPCWSTR m_applicationName;
    HINSTANCE m_hinstance;
    HWND m_hwnd;
 
    InputClass* m_Input = nullptr;
    GraphicsClass* m_Graphics = nullptr;
    SoundClass* m_Sound = nullptr;
};
 
static LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
static SystemClass* ApplicationHandle = 0;
cs



Systemclass.cpp


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#include "stdafx.h"
#include "inputclass.h"
#include "graphicsclass.h"
#include "soundclass.h"
#include "systemclass.h"
 
 
SystemClass::SystemClass()
{
}
 
 
SystemClass::SystemClass(const SystemClass& other)
{
}
 
 
SystemClass::~SystemClass()
{
}
 
 
bool SystemClass::Initialize()
{
    // 윈도우 창 가로, 세로 넓이 변수 초기화
    int screenWidth = 0;
    int screenHeight = 0;
 
    // 윈도우 생성 초기화
    InitializeWindows(screenWidth, screenHeight);
 
    // m_Input 객체 생성. 이 클래스는 추후 사용자의 키보드 입력 처리에 사용됩니다.
    m_Input = new InputClass;
    if (!m_Input)
    {
        return false;
    }
 
    // m_Input 객체 초기화
    if(!m_Input->Initialize(m_hinstance, m_hwnd, screenWidth, screenHeight))
    {
        MessageBox(m_hwnd, L"Could not initialize the input object.", L"Error", MB_OK);
        return false;
    }
 
    // m_Graphics 객체 생성.  그래픽 랜더링을 처리하기 위한 객체입니다.
    m_Graphics = new GraphicsClass;
    if (!m_Graphics)
    {
        return false;
    }
 
    // m_Graphics 객체 초기화.
    if(!m_Graphics->Initialize(screenWidth, screenHeight, m_hwnd))
    {
        return false;
    }
 
    // Create the sound object.
    m_Sound = new SoundClass;
    if(!m_Sound)
    {
        return false;
    }
 
    // Initialize the sound object.
    if(!m_Sound->Initialize(m_hwnd))
    {
        MessageBox(m_hwnd, L"Could not initialize Direct Sound.", L"Error", MB_OK);
        return false;
    }
 
    return true;
}
 
 
void SystemClass::Shutdown()
{
    // Release the sound object.
    if(m_Sound)
    {
        m_Sound->Shutdown();
        delete m_Sound;
        m_Sound = 0;
    }
 
    // m_Graphics 객체 반환
    if (m_Graphics)
    {
        m_Graphics->Shutdown();
        delete m_Graphics;
        m_Graphics = 0;
    }
 
    // m_Input 객체 반환
    if (m_Input)
    {
        m_Input->Shutdown();
        delete m_Input;
        m_Input = 0;
    }
 
    // Window 종료 처리
    ShutdownWindows();
}
 
 
void SystemClass::Run()
{
    // 메시지 구조체 생성 및 초기화
    MSG msg;
    ZeroMemory(&msg, sizeof(MSG));
 
    // 사용자로부터 종료 메시지를 받을때까지 메시지루프를 돕니다
    while (true)
    {
        // 윈도우 메시지를 처리합니다
        if (PeekMessage(&msg, NULL00, PM_REMOVE))
        {
            // 종료 메시지를 받을 경우 메시지 루프를 탈출합니다
            if (msg.message == WM_QUIT)
                break;
 
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        else
        {
            // 그 외에는 Frame 함수를 처리합니다.
            if (!Frame())
            {
                MessageBox(m_hwnd, L"Frame Processing Failed", L"Error", MB_OK);
                break;
            }
        }
        
        // 사용자가 ESC키를 눌렀는지 확인 후 종료 처리함
        if(m_Input->IsEscapePressed() == true)
        {
            break;
        }
    }
}
 
 
bool SystemClass::Frame()
{
    int mouseX = 0, mouseY = 0;
 
    // 입력 프레임 처리를 수행합니다
    if(!m_Input->Frame())
    {
        return false;
    }
 
    // 그래픽 객체의 Frame을 처리합니다
    if(!m_Graphics->Frame())
    {
        return false;
    }
 
    return m_Graphics->Render();
}
 
 
LRESULT CALLBACK SystemClass::MessageHandler(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam)
{
    return DefWindowProc(hwnd, umsg, wparam, lparam);
}
 
 
void SystemClass::InitializeWindows(int& screenWidth, int& screenHeight)
{
    // 외부 포인터를 이 객체로 지정합니다
    ApplicationHandle = this;
 
    // 이 프로그램의 인스턴스를 가져옵니다
    m_hinstance = GetModuleHandle(NULL);
 
    // 프로그램 이름을 지정합니다
    m_applicationName = L"Dx11Demo_14";
 
    // windows 클래스를 아래와 같이 설정합니다.
    WNDCLASSEX wc;
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = m_hinstance;
    wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
    wc.hIconSm = wc.hIcon;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = m_applicationName;
    wc.cbSize = sizeof(WNDCLASSEX);
 
    // windows class를 등록합니다
    RegisterClassEx(&wc);
 
    // 모니터 화면의 해상도를 읽어옵니다
    screenWidth = GetSystemMetrics(SM_CXSCREEN);
    screenHeight = GetSystemMetrics(SM_CYSCREEN);
 
    int posX = 0;
    int posY = 0;
 
    // FULL_SCREEN 변수 값에 따라 화면을 설정합니다.
    if (FULL_SCREEN)
    {
        // 풀스크린 모드로 지정했다면 모니터 화면 해상도를 데스크톱 해상도로 지정하고 색상을 32bit로 지정합니다.
        DEVMODE dmScreenSettings;
        memset(&dmScreenSettings, 0sizeof(dmScreenSettings));
        dmScreenSettings.dmSize = sizeof(dmScreenSettings);
        dmScreenSettings.dmPelsWidth = (unsigned long)screenWidth;
        dmScreenSettings.dmPelsHeight = (unsigned long)screenHeight;
        dmScreenSettings.dmBitsPerPel = 32;
        dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
 
        // 풀스크린으로 디스플레이 설정을 변경합니다.
        ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);
    }
    else
    {
        // 윈도우 모드의 경우 800 * 600 크기를 지정합니다.
        screenWidth = 800;
        screenHeight = 600;
 
        // 윈도우 창을 가로, 세로의 정 가운데 오도록 합니다.
        posX = (GetSystemMetrics(SM_CXSCREEN) - screenWidth) / 2;
        posY = (GetSystemMetrics(SM_CYSCREEN) - screenHeight) / 2;
    }
 
    // 윈도우를 생성하고 핸들을 구합니다.
    m_hwnd = CreateWindowEx(WS_EX_APPWINDOW, m_applicationName, m_applicationName,
        WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_POPUP,
        posX, posY, screenWidth, screenHeight, NULLNULL, m_hinstance, NULL);
 
    // 윈도우를 화면에 표시하고 포커스를 지정합니다
    ShowWindow(m_hwnd, SW_SHOW);
    SetForegroundWindow(m_hwnd);
    SetFocus(m_hwnd);
}
 
 
void SystemClass::ShutdownWindows()
{
    // 풀스크린 모드였다면 디스플레이 설정을 초기화합니다.
    if (FULL_SCREEN)
    {
        ChangeDisplaySettings(NULL0);
    }
 
    // 창을 제거합니다
    DestroyWindow(m_hwnd);
    m_hwnd = NULL;
 
    // 프로그램 인스턴스를 제거합니다
    UnregisterClass(m_applicationName, m_hinstance);
    m_hinstance = NULL;
 
    // 외부포인터 참조를 초기화합니다
    ApplicationHandle = NULL;
}
 
 
LRESULT CALLBACK WndProc(HWND hwnd, UINT umessage, WPARAM wparam, LPARAM lparam)
{
    switch (umessage)
    {
        // 윈도우 종료를 확인합니다
    case WM_DESTROY:
    {
        PostQuitMessage(0);
        return 0;
    }
 
    // 윈도우가 닫히는지 확인합니다
    case WM_CLOSE:
    {
        PostQuitMessage(0);
        return 0;
    }
 
    // 그 외의 모든 메시지들은 시스템 클래스의 메시지 처리로 넘깁니다.
    default:
    {
        return ApplicationHandle->MessageHandler(hwnd, umessage, wparam, lparam);
    }
    }
}
cs



출력 화면


제대로 컴파일 되어 실행이 되었다면 아래와 같은 간단한 효과음을 들을 수 있습니다.




마치면서


이제 엔진에서 Direct Sound의 기본적인 기능을 지원합니다. 프로그램을 시작하게 되면 하나의 웨이브 파일을 재생합니다.



연습문제


1. 프로그램을 다시 컴파일하고 스테레오로 웨이브 파일을 재생하는지 확인해 보십시오. esc 키를 눌러 윈도우를 닫습니다.


2. sound01.wav 파일을 여러분의 44.1KHz 16bit 2채널의 오디오 파일로 바꾸고 다시 프로그램을 실행해 보십시오.


3. 두 개의 웨이브 파일을 불러 동시에 재생하도록 프로그램을 고쳐 보십시오.


4. 음악을 한번만 재생하는 것이 아니라 반복되도록 해 보십시오.



소스코드


소스코드 : Dx11Demo_14.zip