1 #include "DSoundVoiceAdapter.h"
2 #include "RakAssert.h"
3 #include "RakPeerInterface.h"
4
5 /// To test sending to myself
6 //#define _TEST_LOOPBACK
7
8 DSoundVoiceAdapter DSoundVoiceAdapter::instance;
9
DSoundVoiceAdapter()10 DSoundVoiceAdapter::DSoundVoiceAdapter()
11 {
12 rakVoice = 0;
13 ds = 0;
14 dsC = 0;
15 dsbIncoming = 0;
16 dsbOutgoing = 0;
17 mute = false;
18 memset(incomingBufferNotifications,0,sizeof(incomingBufferNotifications));
19 memset(outgoingBufferNotifications,0,sizeof(outgoingBufferNotifications));
20 }
21
Instance()22 DSoundVoiceAdapter* DSoundVoiceAdapter::Instance()
23 {
24 return &instance;
25 }
26
SetupAdapter(RakVoice * rakVoice,HWND hwnd,DWORD dwCoopLevel)27 bool DSoundVoiceAdapter::SetupAdapter(RakVoice *rakVoice, HWND hwnd, DWORD dwCoopLevel)
28 {
29 // Check if it was already initialized
30 RakAssert(ds == 0);
31 if (ds != 0)
32 return false;
33
34 HRESULT hr;
35
36 if (FAILED(hr = DirectSoundCreate8(NULL, &ds, NULL)))
37 {
38 DXTRACE_ERR_MSGBOX(L"DirectSoundCreate8, when initiliazing DirectSound", hr);
39 return false;
40 }
41
42 if (FAILED(hr = ds->SetCooperativeLevel(hwnd, dwCoopLevel)))
43 {
44 DXTRACE_ERR_MSGBOX(L"IDirectSound8::SetCooperativeLevel", hr);
45 Release();
46 return false;
47 }
48
49 // Check if the rest of the initialization fails, and if so, release any allocated resources
50 if (SetupAdapter(rakVoice))
51 {
52 return true;
53 }
54 else
55 {
56 Release();
57 return false;
58 }
59 }
60
SetupAdapter(RakVoice * rakVoice,IDirectSound8 * pDS)61 bool DSoundVoiceAdapter::SetupAdapter(RakVoice *rakVoice, IDirectSound8 *pDS)
62 {
63 // Check if it was already initialized
64 RakAssert(ds == 0);
65 if (ds != 0)
66 return false;
67
68 // User provided his own device object, so use it and add another reference
69 pDS->AddRef();
70 ds = pDS;
71
72 // Check if the rest of the initialization fails, and if so, release any allocated resources
73 if (SetupAdapter(rakVoice))
74 {
75 return true;
76 }
77 else
78 {
79 Release();
80 return false;
81 }
82 }
83
SetupAdapter(RakVoice * rakVoice)84 bool DSoundVoiceAdapter::SetupAdapter(RakVoice *rakVoice)
85 {
86 RakAssert(rakVoice);
87 // Make sure rakVoice was initialized
88 RakAssert((rakVoice->IsInitialized())&&(rakVoice->GetRakPeerInterface()!=NULL));
89
90 this->rakVoice = rakVoice;
91
92 if (!SetupIncomingBuffer())
93 return false;
94
95 return SetupOutgoingBuffer();
96 }
97
SetupIncomingBuffer()98 bool DSoundVoiceAdapter::SetupIncomingBuffer()
99 {
100
101 //
102 //
103 // Create the buffer for incoming sound
104 //
105 //
106
107 WAVEFORMATEX wfx;
108 DSBUFFERDESC dsbdesc;
109 LPDIRECTSOUNDBUFFER pDsb = NULL;
110 HRESULT hr;
111 // Set up WAV format structure.
112 memset(&wfx, 0, sizeof(WAVEFORMATEX));
113 wfx.wFormatTag = WAVE_FORMAT_PCM;
114 wfx.nChannels = 1;
115 wfx.nSamplesPerSec = rakVoice->GetSampleRate();
116 wfx.nBlockAlign = 2;
117 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
118 wfx.wBitsPerSample = 16;
119 // Set up DSBUFFERDESC structure.
120 memset(&dsbdesc, 0, sizeof(DSBUFFERDESC));
121 dsbdesc.dwSize = sizeof(DSBUFFERDESC);
122 dsbdesc.dwFlags = DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS | DSBCAPS_CTRLPOSITIONNOTIFY
123 | DSBCAPS_LOCSOFTWARE; // Create in software, because DX documentation says its the best option when using notifications
124 dsbdesc.dwBufferBytes = rakVoice->GetBufferSizeBytes()*FRAMES_IN_SOUND;
125 dsbdesc.lpwfxFormat = &wfx;
126 // Create buffer.
127 if (FAILED(hr = ds->CreateSoundBuffer(&dsbdesc, &pDsb, NULL)))
128 {
129 DXTRACE_ERR_MSGBOX(L"IDirectSound8::CreateSoundBuffer, when creating buffer for incoming sound )", hr);
130 return false;
131 }
132 hr = pDsb->QueryInterface(IID_IDirectSoundBuffer8, (LPVOID*) &dsbIncoming);
133 pDsb->Release();
134 if (FAILED(hr))
135 {
136 DXTRACE_ERR_MSGBOX(L"IDirectSoundBuffer::QueryInterface, when getting IDirectSoundBuffer8 interface for incoming sound", hr);
137 return false;
138 }
139 //
140 // Setup the notification events
141 //
142 for (int i=0; i<FRAMES_IN_SOUND; i++)
143 {
144 incomingBufferNotifications[i].dwOffset = i*rakVoice->GetBufferSizeBytes();
145 if ((incomingBufferNotifications[i].hEventNotify = CreateEvent(NULL, TRUE, FALSE, NULL))==NULL)
146 {
147 DXTRACE_ERR_MSGBOX(L"CreateEvent", GetLastError());
148 return false;
149 }
150 }
151 IDirectSoundNotify8 *dsbNotify=0;
152 if (FAILED(hr=dsbIncoming->QueryInterface(IID_IDirectSoundNotify8, (LPVOID*) &dsbNotify)))
153 {
154 DXTRACE_ERR_MSGBOX(L"IDirectSoundBuffer8::QueryInterface, when getting IDirectSoundNotify8 interface for incoming sound", hr);
155 return false;
156 }
157 hr = dsbNotify->SetNotificationPositions(FRAMES_IN_SOUND, incomingBufferNotifications);
158 dsbNotify->Release();
159 if (FAILED(hr))
160 {
161 DXTRACE_ERR_MSGBOX(L"IDirectSoundNotify8::SetNotificationPositions, when setting notifications for incoming sound", hr);
162 return false;
163 }
164
165 if (FAILED(hr = dsbIncoming->Play(0,0,DSBPLAY_LOOPING)))
166 {
167 DXTRACE_ERR_MSGBOX(L"IDirectSoundBuffer8::Play, when starting incoming sound buffer", hr);
168 return false;
169 }
170
171 return true;
172 }
173
SetupOutgoingBuffer()174 bool DSoundVoiceAdapter::SetupOutgoingBuffer()
175 {
176 HRESULT hr;
177
178 //
179 //
180 // Create the buffer for outgoing sound
181 //
182 //
183 if (FAILED(hr=DirectSoundCaptureCreate8(NULL, &dsC, NULL)))
184 {
185 DXTRACE_ERR_MSGBOX(L"DirectSoundCaptureCreate8", hr);
186 return false;
187 }
188
189 // Set up WAV format structure.
190 WAVEFORMATEX wfx;
191 memset(&wfx, 0, sizeof(WAVEFORMATEX));
192 wfx.wFormatTag = WAVE_FORMAT_PCM;
193 wfx.nChannels = 1;
194 wfx.nSamplesPerSec = rakVoice->GetSampleRate();
195 wfx.nBlockAlign = 2;
196 wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
197 wfx.wBitsPerSample = 16;
198 // Set up DSCBUFFERDESC structure.
199 DSCBUFFERDESC dscbd;
200 memset(&dscbd, 0, sizeof(DSCBUFFERDESC));
201 dscbd.dwSize = sizeof(DSCBUFFERDESC);
202 dscbd.dwFlags = 0;
203 dscbd.dwBufferBytes = rakVoice->GetBufferSizeBytes()*FRAMES_IN_SOUND;
204 dscbd.dwReserved = 0;
205 dscbd.lpwfxFormat = &wfx;
206 dscbd.dwFXCount = 0;
207 dscbd.lpDSCFXDesc = NULL;
208 // Create capture buffer.
209 LPDIRECTSOUNDCAPTUREBUFFER pDscb = NULL;
210 if (FAILED(hr = dsC->CreateCaptureBuffer(&dscbd, &pDscb, NULL)))
211 {
212 DXTRACE_ERR_MSGBOX(L"IDirectSoundCapture8::CreateCaptureBuffer, when creating buffer for outgoing sound )", hr);
213 return false;
214 }
215 hr = pDscb->QueryInterface(IID_IDirectSoundCaptureBuffer8, (LPVOID*) &dsbOutgoing);
216 pDscb->Release();
217 if (FAILED(hr))
218 {
219 DXTRACE_ERR_MSGBOX(L"IDirectSoundBuffer::QueryInterface, when getting IDirectSoundCaptureBuffer8 interface for outgoing sound", hr);
220 return false;
221 }
222 //
223 // Setup the notification events
224 //
225 for (int i=0; i<FRAMES_IN_SOUND; i++)
226 {
227 outgoingBufferNotifications[i].dwOffset = i*rakVoice->GetBufferSizeBytes();
228 if ((outgoingBufferNotifications[i].hEventNotify = CreateEvent(NULL, TRUE, FALSE, NULL))==NULL)
229 {
230 DXTRACE_ERR_MSGBOX(L"CreateEvent", GetLastError());
231 return false;
232 }
233 }
234 IDirectSoundNotify8 *dsbNotify=0;
235 if (FAILED(hr=dsbOutgoing->QueryInterface(IID_IDirectSoundNotify8, (LPVOID*) &dsbNotify)))
236 {
237 DXTRACE_ERR_MSGBOX(L"IDirectSoundCaptureBuffer8::QueryInterface, when getting IDirectSoundNotify8 interface for outgoing sound", hr);
238 return false;
239 }
240 hr = dsbNotify->SetNotificationPositions(FRAMES_IN_SOUND, outgoingBufferNotifications);
241 dsbNotify->Release();
242 if (FAILED(hr))
243 {
244 DXTRACE_ERR_MSGBOX(L"IDirectSoundNotify8::SetNotificationPositions, when setting notifications for outgoing sound", hr);
245 return false;
246 }
247
248 if (FAILED(hr = dsbOutgoing->Start(DSCBSTART_LOOPING)))
249 {
250 DXTRACE_ERR_MSGBOX(L"IDirectSoundCaptureBuffer8::Start, when starting outgoing sound buffer", hr);
251 return false;
252 }
253
254 return true;
255 }
256
Release()257 void DSoundVoiceAdapter::Release()
258 {
259 // Release DirectSound buffer used for incoming voice
260 if (dsbIncoming)
261 {
262 dsbIncoming->Stop();
263 dsbIncoming->Release();
264 dsbIncoming = 0;
265 }
266
267 // Release DirectSound buffer used for outgoing voice
268 if (dsbOutgoing)
269 {
270 dsbOutgoing->Stop();
271 dsbOutgoing->Release();
272 dsbOutgoing = 0;
273 }
274
275 // Release DirectSound device object
276 if (ds)
277 {
278 ds->Release();
279 ds = 0;
280 }
281
282 if (dsC)
283 {
284 dsC->Release();
285 dsC = 0;
286 }
287
288 // Release the notification events
289 for (int i=0; i<FRAMES_IN_SOUND;i++)
290 {
291 if (incomingBufferNotifications[i].hEventNotify!=0 && CloseHandle(incomingBufferNotifications[i].hEventNotify)==0)
292 {
293 DXTRACE_ERR_MSGBOX(L"CloseHandle", GetLastError());
294 }
295 if (outgoingBufferNotifications[i].hEventNotify!=0 && CloseHandle(outgoingBufferNotifications[i].hEventNotify)==0)
296 {
297 DXTRACE_ERR_MSGBOX(L"CloseHandle", GetLastError());
298 }
299 }
300 memset(incomingBufferNotifications,0, sizeof(incomingBufferNotifications));
301 memset(outgoingBufferNotifications,0, sizeof(outgoingBufferNotifications));
302 }
303
GetDSDeviceObject()304 IDirectSound8* DSoundVoiceAdapter::GetDSDeviceObject()
305 {
306 return ds;
307 }
308
Update()309 void DSoundVoiceAdapter::Update()
310 {
311 HRESULT hr;
312 void *audioPtr;
313 DWORD audioPtrbytes;
314
315 for (int i=0; i<FRAMES_IN_SOUND; i++)
316 {
317 //
318 // Update incoming sound
319 //
320 if (WaitForSingleObject(incomingBufferNotifications[i].hEventNotify, 0)==WAIT_OBJECT_0)
321 {
322 // The lock offset is the buffer right before the one the event refers to
323 DWORD dwOffset = (i==0) ? incomingBufferNotifications[FRAMES_IN_SOUND-1].dwOffset : incomingBufferNotifications[i-1].dwOffset;
324 hr = dsbIncoming->Lock(dwOffset, rakVoice->GetBufferSizeBytes(), &audioPtr, &audioPtrbytes, NULL, NULL, 0);
325 RakAssert(audioPtrbytes==rakVoice->GetBufferSizeBytes());
326 if (SUCCEEDED(hr))
327 {
328 rakVoice->ReceiveFrame(audioPtr);
329 dsbIncoming->Unlock(audioPtr, audioPtrbytes, NULL, 0);
330 }
331 ResetEvent(incomingBufferNotifications[i].hEventNotify);
332 }
333
334 //
335 // Update outgoing sound
336 //
337 if (WaitForSingleObject(outgoingBufferNotifications[i].hEventNotify, 0)==WAIT_OBJECT_0)
338 {
339
340 /* If we're set to mute, we don't send anything, and just reset the event */
341 if (!mute)
342 {
343 // The lock offset is the buffer right before the one the event refers to
344 DWORD dwOffset = (i==0) ? outgoingBufferNotifications[FRAMES_IN_SOUND-1].dwOffset : outgoingBufferNotifications[i-1].dwOffset;
345 hr = dsbOutgoing->Lock(dwOffset, rakVoice->GetBufferSizeBytes(), &audioPtr, &audioPtrbytes, NULL, NULL, 0);
346 RakAssert(audioPtrbytes==rakVoice->GetBufferSizeBytes());
347 if (SUCCEEDED(hr))
348 {
349 BroadcastFrame(audioPtr);
350 dsbOutgoing->Unlock(audioPtr, audioPtrbytes, NULL, 0);
351 }
352 }
353 ResetEvent(outgoingBufferNotifications[i].hEventNotify);
354 }
355
356 }
357 }
358
BroadcastFrame(void * ptr)359 void DSoundVoiceAdapter::BroadcastFrame(void *ptr)
360 {
361 #ifndef _TEST_LOOPBACK
362 unsigned i;
363
364 unsigned int numPeers = rakVoice->GetRakPeerInterface()->GetMaximumNumberOfPeers();
365 for (i=0; i < numPeers; i++)
366 {
367 rakVoice->SendFrame(rakVoice->GetRakPeerInterface()->GetGUIDFromIndex(i), ptr);
368 }
369 #else
370 rakVoice->SendFrame(UNASSIGNED_SYSTEM_ADDRESS, ptr);
371 #endif
372
373 }
374
SetMute(bool mute)375 void DSoundVoiceAdapter::SetMute(bool mute)
376 {
377 this->mute = mute;
378 }
379