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