1 // Copyright 2005-2019 The Mumble Developers. All rights reserved.
2 // Use of this source code is governed by a BSD-style license
3 // that can be found in the LICENSE file at the root of the
4 // Mumble source tree or at <https://www.mumble.info/LICENSE>.
5 
6 #include "mumble_pch.hpp"
7 
8 #define DIRECTSOUND_VERSION 0x1000
9 
10 #include <mmsystem.h>
11 #include <dsound.h>
12 #include <ks.h>
13 #include <ksmedia.h>
14 
15 #include "DirectSound.h"
16 
17 #include "MainWindow.h"
18 #include "Plugins.h"
19 #include "User.h"
20 #include "Global.h"
21 
22 // from os_win.cpp
23 extern HWND mumble_mw_hwnd;
24 
25 #undef FAILED
26 #define FAILED(Status) (static_cast<HRESULT>(Status)<0)
27 
28 // #define MY_DEFERRED DS3D_DEFERRED
29 #define MY_DEFERRED DS3D_IMMEDIATE
30 
31 #define NBLOCKS 50
32 
33 class DXAudioOutputRegistrar : public AudioOutputRegistrar {
34 	public:
35 		DXAudioOutputRegistrar();
36 		virtual AudioOutput *create();
37 		virtual const QList<audioDevice> getDeviceChoices();
38 		virtual void setDeviceChoice(const QVariant &, Settings &);
39 
40 };
41 
42 class DXAudioInputRegistrar : public AudioInputRegistrar {
43 	public:
44 		DXAudioInputRegistrar();
45 		virtual AudioInput *create();
46 		virtual const QList<audioDevice> getDeviceChoices();
47 		virtual void setDeviceChoice(const QVariant &, Settings &);
48 		virtual bool canEcho(const QString &) const;
49 
50 };
51 
52 class DirectSoundInit : public DeferInit {
53 		DXAudioInputRegistrar *airReg;
54 		DXAudioOutputRegistrar *aorReg;
55 	public:
DirectSoundInit()56 		DirectSoundInit() : airReg(NULL), aorReg(NULL) {}
57 		void initialize();
58 		void destroy();
59 };
60 
61 static DirectSoundInit dsinit;
62 
initialize()63 void DirectSoundInit::initialize() {
64 	airReg = NULL;
65 	aorReg = NULL;
66 
67 #ifdef USE_WASAPI
68 	OSVERSIONINFOEXW ovi;
69 	memset(&ovi, 0, sizeof(ovi));
70 
71 	ovi.dwOSVersionInfoSize=sizeof(ovi);
72 	GetVersionEx(reinterpret_cast<OSVERSIONINFOW *>(&ovi));
73 
74 #ifdef QT_NO_DEBUG
75 	if ((ovi.dwMajorVersion > 6) || ((ovi.dwMajorVersion == 6) && (ovi.dwBuildNumber >= 6001))) {
76 		HMODULE hLib = LoadLibrary(L"AVRT.DLL");
77 		if (hLib != NULL) {
78 			FreeLibrary(hLib);
79 			qWarning("DirectSound: Disabled as WASAPI is available");
80 			return;
81 		}
82 	}
83 #endif
84 #endif
85 
86 	airReg = new DXAudioInputRegistrar();
87 	aorReg = new DXAudioOutputRegistrar();
88 }
89 
destroy()90 void DirectSoundInit::destroy() {
91 	delete airReg;
92 	delete aorReg;
93 }
94 
95 
DXAudioOutputRegistrar()96 DXAudioOutputRegistrar::DXAudioOutputRegistrar() : AudioOutputRegistrar(QLatin1String("DirectSound")) {
97 }
98 
create()99 AudioOutput *DXAudioOutputRegistrar::create() {
100 	return new DXAudioOutput();
101 }
102 
103 typedef QPair<QString, GUID> dsDevice;
104 
DSEnumProc(LPGUID lpGUID,const WCHAR * lpszDesc,const WCHAR *,void * ctx)105 static BOOL CALLBACK DSEnumProc(LPGUID lpGUID, const WCHAR* lpszDesc,
106                                 const WCHAR*, void *ctx) {
107 	if (lpGUID) {
108 		QList<dsDevice> *l =reinterpret_cast<QList<dsDevice> *>(ctx);
109 		*l << dsDevice(QString::fromUtf16(reinterpret_cast<const ushort*>(lpszDesc)), *lpGUID);
110 	}
111 
112 	return(true);
113 }
114 
getDeviceChoices()115 const QList<audioDevice> DXAudioOutputRegistrar::getDeviceChoices() {
116 	QList<dsDevice> qlOutput;
117 
118 	qlOutput << dsDevice(DXAudioOutput::tr("Default DirectSound Voice Output"), DSDEVID_DefaultVoicePlayback);
119 	DirectSoundEnumerate(DSEnumProc, reinterpret_cast<void *>(&qlOutput));
120 
121 	QList<audioDevice> qlReturn;
122 
123 	const GUID *lpguid = NULL;
124 
125 	if (! g.s.qbaDXOutput.isEmpty()) {
126 		lpguid = reinterpret_cast<LPGUID>(g.s.qbaDXOutput.data());
127 	} else {
128 		lpguid = &DSDEVID_DefaultVoicePlayback;
129 	}
130 
131 	foreach(dsDevice d, qlOutput) {
132 		if (d.second == *lpguid) {
133 			qlReturn << audioDevice(d.first, QByteArray(reinterpret_cast<const char *>(&d.second), sizeof(GUID)));
134 		}
135 	}
136 	foreach(dsDevice d, qlOutput) {
137 		if (d.second != *lpguid) {
138 			qlReturn << audioDevice(d.first, QByteArray(reinterpret_cast<const char *>(&d.second), sizeof(GUID)));
139 		}
140 	}
141 	return qlReturn;
142 }
143 
setDeviceChoice(const QVariant & choice,Settings & s)144 void DXAudioOutputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) {
145 	s.qbaDXOutput = choice.toByteArray();
146 }
147 
DXAudioInputRegistrar()148 DXAudioInputRegistrar::DXAudioInputRegistrar() : AudioInputRegistrar(QLatin1String("DirectSound")) {
149 }
150 
create()151 AudioInput *DXAudioInputRegistrar::create() {
152 	return new DXAudioInput();
153 }
154 
getDeviceChoices()155 const QList<audioDevice> DXAudioInputRegistrar::getDeviceChoices() {
156 	QList<dsDevice> qlInput;
157 
158 	qlInput << dsDevice(DXAudioInput::tr("Default DirectSound Voice Input"), DSDEVID_DefaultVoiceCapture);
159 	DirectSoundCaptureEnumerate(DSEnumProc, reinterpret_cast<void *>(&qlInput));
160 
161 	QList<audioDevice> qlReturn;
162 
163 	const GUID *lpguid = NULL;
164 
165 	if (! g.s.qbaDXInput.isEmpty()) {
166 		lpguid = reinterpret_cast<LPGUID>(g.s.qbaDXInput.data());
167 	} else {
168 		lpguid = &DSDEVID_DefaultVoiceCapture;
169 	}
170 
171 	foreach(dsDevice d, qlInput) {
172 		if (d.second == *lpguid) {
173 			qlReturn << audioDevice(d.first, QByteArray(reinterpret_cast<const char *>(&d.second), sizeof(GUID)));
174 		}
175 	}
176 	foreach(dsDevice d, qlInput) {
177 		if (d.second != *lpguid) {
178 			qlReturn << audioDevice(d.first, QByteArray(reinterpret_cast<const char *>(&d.second), sizeof(GUID)));
179 		}
180 	}
181 	return qlReturn;
182 }
183 
setDeviceChoice(const QVariant & choice,Settings & s)184 void DXAudioInputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) {
185 	s.qbaDXInput = choice.toByteArray();
186 }
187 
canEcho(const QString &) const188 bool DXAudioInputRegistrar::canEcho(const QString &) const {
189 	return false;
190 }
191 
DXAudioOutput()192 DXAudioOutput::DXAudioOutput() {
193 }
194 
~DXAudioOutput()195 DXAudioOutput::~DXAudioOutput() {
196 	bRunning = false;
197 	wait();
198 }
199 
run()200 void DXAudioOutput::run() {
201 	HRESULT hr;
202 	DSBUFFERDESC        dsbdesc;
203 	WAVEFORMATEXTENSIBLE wfx;
204 	WAVEFORMATEXTENSIBLE wfxSet;
205 	int ns = 0;
206 	unsigned int chanmasks[32];
207 
208 	LPDIRECTSOUND8             pDS = NULL;
209 	LPDIRECTSOUNDBUFFER       pDSBPrimary = NULL;
210 	LPDIRECTSOUNDBUFFER       pDSBOutput = NULL;
211 	LPDIRECTSOUNDNOTIFY       pDSNotify = NULL;
212 
213 	int iLastwriteblock;
214 	LPVOID aptr1, aptr2;
215 	DWORD nbytes1, nbytes2;
216 
217 	int playblock;
218 	int nowriteblock;
219 	DWORD dwPlayPosition, dwWritePosition;
220 
221 	unsigned int iByteSize;
222 
223 	bool bOk;
224 	DWORD dwSpeakerConfig;
225 
226 	bool failed = false;
227 
228 	bOk = false;
229 	DWORD dwMask = 0;
230 	bool bHead = false;
231 
232 	ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC));
233 	dsbdesc.dwSize  = sizeof(DSBUFFERDESC);
234 	dsbdesc.dwFlags = DSBCAPS_PRIMARYBUFFER;
235 
236 	if (! g.s.qbaDXOutput.isEmpty()) {
237 		LPGUID lpguid = reinterpret_cast<LPGUID>(g.s.qbaDXOutput.data());
238 		if (FAILED(hr = DirectSoundCreate8(lpguid, &pDS, NULL))) {
239 			failed = true;
240 		}
241 	}
242 
243 	if (! pDS && FAILED(hr = DirectSoundCreate8(&DSDEVID_DefaultVoicePlayback, &pDS, NULL))) {
244 		qWarning("DXAudioOutput: DirectSoundCreate failed: hr=0x%08lx", hr);
245 		goto cleanup;
246 	} else if (FAILED(hr = pDS->SetCooperativeLevel(mumble_mw_hwnd, DSSCL_PRIORITY))) {
247 		qWarning("DXAudioOutput: SetCooperativeLevel failed: hr=0x%08lx", hr);
248 		goto cleanup;
249 	} else if (FAILED(hr = pDS->CreateSoundBuffer(&dsbdesc, &pDSBPrimary, NULL))) {
250 		qWarning("DXAudioOutput: CreateSoundBuffer (Primary) failed: hr=0x%08lx", hr);
251 		goto cleanup;
252 	}
253 
254 	pDS->GetSpeakerConfig(&dwSpeakerConfig);
255 
256 
257 	switch (DSSPEAKER_CONFIG(dwSpeakerConfig)) {
258 		case DSSPEAKER_HEADPHONE:
259 			dwMask = KSAUDIO_SPEAKER_STEREO;
260 			bHead = true;
261 			break;
262 		case DSSPEAKER_MONO:
263 			dwMask = KSAUDIO_SPEAKER_MONO;
264 			break;
265 		case DSSPEAKER_QUAD:
266 			dwMask = KSAUDIO_SPEAKER_QUAD;
267 			break;
268 		case DSSPEAKER_STEREO:
269 			dwMask = KSAUDIO_SPEAKER_STEREO;
270 			break;
271 		case DSSPEAKER_SURROUND:
272 			dwMask = KSAUDIO_SPEAKER_SURROUND;
273 			break;
274 		case DSSPEAKER_5POINT1:
275 			dwMask = KSAUDIO_SPEAKER_5POINT1;
276 			break;
277 		case DSSPEAKER_7POINT1:
278 			dwMask = KSAUDIO_SPEAKER_7POINT1;
279 			break;
280 		case DSSPEAKER_7POINT1_SURROUND:
281 			dwMask = KSAUDIO_SPEAKER_7POINT1_SURROUND;
282 			break;
283 		case DSSPEAKER_5POINT1_SURROUND:
284 			dwMask = KSAUDIO_SPEAKER_5POINT1_SURROUND;
285 			break;
286 		default:
287 			dwMask = 0;
288 			break;
289 	}
290 
291 	if (! g.s.doPositionalAudio())
292 		dwMask = KSAUDIO_SPEAKER_STEREO;
293 
294 	for (int i=0;i<32;i++) {
295 		if (dwMask & (1 << i)) {
296 			chanmasks[ns++] = 1 << i;
297 		}
298 	}
299 
300 	iMixerFreq = SAMPLE_RATE;
301 	iChannels = ns;
302 	eSampleFormat = SampleShort;
303 
304 	iByteSize = iFrameSize * sizeof(short) * ns;
305 
306 	ZeroMemory(&wfxSet, sizeof(wfxSet));
307 	wfxSet.Format.wFormatTag = WAVE_FORMAT_PCM;
308 
309 	ZeroMemory(&wfx, sizeof(wfx));
310 	wfx.Format.wFormatTag = WAVE_FORMAT_PCM;
311 	wfx.Format.nChannels = qMax(ns, 2);
312 	wfx.Format.nSamplesPerSec = SAMPLE_RATE;
313 	wfx.Format.nBlockAlign = sizeof(short) * wfx.Format.nChannels;
314 	wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
315 	wfx.Format.wBitsPerSample = 16;
316 
317 	if (FAILED(hr = pDSBPrimary->SetFormat(reinterpret_cast<WAVEFORMATEX *>(&wfx)))) {
318 		qWarning("DXAudioOutput: SetFormat failed: hr=0x%08lx", hr);
319 		goto cleanup;
320 	}
321 	if (FAILED(hr = pDSBPrimary->GetFormat(reinterpret_cast<WAVEFORMATEX *>(&wfxSet), sizeof(wfxSet), NULL))) {
322 		qWarning("DXAudioOutput: GetFormat failed: hr=0x%08lx", hr);
323 		goto cleanup;
324 	}
325 
326 	qWarning("DXAudioOutput: Primary buffer of %ld Hz, %d channels, %d bits",wfxSet.Format.nSamplesPerSec,wfxSet.Format.nChannels,wfxSet.Format.wBitsPerSample);
327 
328 	ZeroMemory(&wfx, sizeof(wfx));
329 	wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
330 	wfx.Format.nChannels = qMax(ns, 2);
331 	wfx.Format.nSamplesPerSec = SAMPLE_RATE;
332 	wfx.Format.nBlockAlign = sizeof(short) * wfx.Format.nChannels;
333 	wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign;
334 	wfx.Format.wBitsPerSample = 16;
335 	wfx.Format.cbSize = 32;
336 	wfx.Samples.wValidBitsPerSample = wfx.Format.wBitsPerSample;
337 	wfx.dwChannelMask = dwMask;
338 	wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
339 
340 	ZeroMemory(&dsbdesc, sizeof(DSBUFFERDESC));
341 	dsbdesc.dwSize          = sizeof(DSBUFFERDESC);
342 	dsbdesc.dwFlags         = DSBCAPS_GLOBALFOCUS|DSBCAPS_GETCURRENTPOSITION2;
343 	dsbdesc.dwFlags	 |= DSBCAPS_CTRLPOSITIONNOTIFY;
344 	dsbdesc.dwBufferBytes = wfx.Format.nChannels * iFrameSize * sizeof(short) * NBLOCKS;
345 	dsbdesc.lpwfxFormat     = reinterpret_cast<WAVEFORMATEX *>(&wfx);
346 
347 	if (FAILED(hr = pDS->CreateSoundBuffer(&dsbdesc, &pDSBOutput, NULL))) {
348 		qWarning("DXAudioOutputUser: CreateSoundBuffer (Secondary) failed: hr=0x%08lx", hr);
349 		goto cleanup;
350 	}
351 
352 
353 	if (FAILED(hr = pDSBOutput->QueryInterface(IID_IDirectSoundNotify, reinterpret_cast<void **>(&pDSNotify)))) {
354 		qWarning("DXAudioOutputUser: QueryInterface (Notify) failed: hr=0x%08lx", hr);
355 		goto cleanup;
356 	}
357 
358 	qWarning("DXAudioOutputUser: New %dHz output buffer of %ld bytes", SAMPLE_RATE, dsbdesc.dwBufferBytes);
359 
360 	if (failed)
361 		g.mw->msgBox(tr("Opening chosen DirectSound Output failed. Default device will be used."));
362 
363 	initializeMixer(chanmasks, bHead);
364 
365 	if (FAILED(hr = pDSBOutput->Lock(0, 0, &aptr1, &nbytes1, &aptr2, &nbytes2, DSBLOCK_ENTIREBUFFER))) {
366 		qWarning("DXAudioOutputUser: Initial Lock failed: hr=0x%08lx", hr);
367 		goto cleanup;
368 	}
369 
370 	if (aptr1)
371 		ZeroMemory(aptr1, nbytes1);
372 	if (aptr2)
373 		ZeroMemory(aptr2, nbytes2);
374 
375 	if (FAILED(hr = pDSBOutput->Unlock(aptr1, nbytes1, aptr2, nbytes2))) {
376 		qWarning("DXAudioOutputUser: Initial Unlock failed: hr=0x%08lx", hr);
377 		goto cleanup;
378 	}
379 
380 	if (FAILED(hr = pDSBOutput->Play(0, 0, DSBPLAY_LOOPING))) {
381 		qWarning("DXAudioOutputUser: Play failed: hr=0x%08lx", hr);
382 		goto cleanup;
383 	}
384 
385 	iLastwriteblock = (NBLOCKS - 1 + g.s.iOutputDelay) % NBLOCKS;
386 
387 	bOk = true;
388 
389 	while (bRunning && ! FAILED(hr)) {
390 		if (FAILED(hr = pDSBOutput->GetCurrentPosition(&dwPlayPosition, &dwWritePosition))) {
391 			qWarning("DXAudioOutputUser: GetCurrentPosition failed: hr=0x%08lx", hr);
392 			break;
393 		}
394 
395 		playblock = dwWritePosition / iByteSize;
396 		nowriteblock = (playblock + g.s.iOutputDelay + 1) % NBLOCKS;
397 
398 		for (int block=(iLastwriteblock + 1) % NBLOCKS;(!FAILED(hr)) && (block!=nowriteblock);block=(block + 1) % NBLOCKS) {
399 			iLastwriteblock = block;
400 
401 			if (FAILED(hr = pDSBOutput->Lock(block * iByteSize, iByteSize, &aptr1, &nbytes1, &aptr2, &nbytes2, 0))) {
402 				qWarning("DXAudioOutput: Lock block %u (%d bytes) failed: hr=0x%08lx",block, iByteSize, hr);
403 				break;
404 			}
405 			if (aptr2 || nbytes2) {
406 				qWarning("DXAudioOutput: Split buffer");
407 				break;
408 			}
409 			if (!aptr1 || ! nbytes1) {
410 				qWarning("DXAudioOutput: Zerolock");
411 				break;
412 			}
413 			if (! mix(reinterpret_cast<short *>(aptr1), iFrameSize))
414 				ZeroMemory(aptr1, iByteSize);
415 
416 			if (FAILED(hr = pDSBOutput->Unlock(aptr1, nbytes1, aptr2, nbytes2))) {
417 				qWarning("DXAudioOutput: Unlock %p(%lu) %p(%lu) failed: hr=0x%08lx",aptr1,nbytes1,aptr2,nbytes2,hr);
418 				break;
419 			}
420 
421 			if (FAILED(hr = pDSBOutput->GetCurrentPosition(&dwPlayPosition, &dwWritePosition))) {
422 				qWarning("DXAudioOutputUser: GetCurrentPosition failed: hr=0x%08lx", hr);
423 				break;
424 			}
425 
426 			playblock = dwWritePosition / iByteSize;
427 			nowriteblock = (playblock + g.s.iOutputDelay + 1) % NBLOCKS;
428 		}
429 		if (! FAILED(hr))
430 			msleep(19);
431 	}
432 
433 	if (FAILED(hr)) {
434 		g.mw->msgBox(tr("Lost DirectSound output device."));
435 	}
436 cleanup:
437 	if (! bOk) {
438 		g.mw->msgBox(tr("Opening chosen DirectSound Output failed. No audio will be heard."));
439 		return;
440 	}
441 
442 	if (pDSNotify)
443 		pDSNotify->Release();
444 	if (pDSBOutput) {
445 		pDSBOutput->Stop();
446 		pDSBOutput->Release();
447 	}
448 	if (pDSBPrimary)
449 		pDSBPrimary->Release();
450 	if (pDS)
451 		pDS->Release();
452 }
453 
454 #define NBUFFBLOCKS 50
455 
DXAudioInput()456 DXAudioInput::DXAudioInput() {
457 }
458 
~DXAudioInput()459 DXAudioInput::~DXAudioInput() {
460 	bRunning = false;
461 	wait();
462 }
463 
run()464 void DXAudioInput::run() {
465 	LPDIRECTSOUNDCAPTURE8      pDSCapture;
466 	LPDIRECTSOUNDCAPTUREBUFFER pDSCaptureBuffer;
467 	LPDIRECTSOUNDNOTIFY        pDSNotify;
468 
469 	DWORD dwBufferSize;
470 	bool bOk;
471 	DWORD dwReadPosition;
472 	DWORD dwCapturePosition;
473 
474 	LPVOID aptr1, aptr2;
475 	DWORD nbytes1, nbytes2;
476 
477 	HRESULT       hr;
478 	WAVEFORMATEX  wfx;
479 	DSCBUFFERDESC dscbd;
480 
481 	pDSCapture = NULL;
482 	pDSCaptureBuffer = NULL;
483 	pDSNotify = NULL;
484 
485 	bOk = false;
486 
487 	bool failed = false;
488 
489 	Timer t;
490 
491 	ZeroMemory(&wfx, sizeof(wfx));
492 	wfx.wFormatTag = WAVE_FORMAT_PCM;
493 
494 	ZeroMemory(&dscbd, sizeof(dscbd));
495 	dscbd.dwSize = sizeof(dscbd);
496 
497 	dscbd.dwBufferBytes = dwBufferSize = iFrameSize * sizeof(short) * NBUFFBLOCKS;
498 	dscbd.lpwfxFormat = &wfx;
499 
500 	wfx.nChannels = 1;
501 	wfx.nSamplesPerSec = iSampleRate;
502 	wfx.nBlockAlign = 2;
503 	wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign;
504 	wfx.wBitsPerSample = 16;
505 
506 	// Create IDirectSoundCapture using the preferred capture device
507 	if (! g.s.qbaDXInput.isEmpty()) {
508 		LPGUID lpguid = reinterpret_cast<LPGUID>(g.s.qbaDXInput.data());
509 		if (FAILED(hr = DirectSoundCaptureCreate8(lpguid, &pDSCapture, NULL))) {
510 			failed = true;
511 		}
512 	}
513 
514 	if (! pDSCapture && FAILED(hr = DirectSoundCaptureCreate8(&DSDEVID_DefaultVoiceCapture, &pDSCapture, NULL)))
515 		qWarning("DXAudioInput: DirectSoundCaptureCreate failed: hr=0x%08lx", hr);
516 	else if (FAILED(hr = pDSCapture->CreateCaptureBuffer(&dscbd, &pDSCaptureBuffer, NULL)))
517 		qWarning("DXAudioInput: CreateCaptureBuffer failed: hr=0x%08lx", hr);
518 	else if (FAILED(hr = pDSCaptureBuffer->QueryInterface(IID_IDirectSoundNotify, reinterpret_cast<void **>(&pDSNotify))))
519 		qWarning("DXAudioInput: QueryInterface (Notify) failed: hr=0x%08lx", hr);
520 	else
521 		bOk = true;
522 
523 
524 
525 	if (failed)
526 		g.mw->msgBox(tr("Opening chosen DirectSound Input failed. Default device will be used."));
527 
528 	qWarning("DXAudioInput: Initialized");
529 
530 	if (! bOk)
531 		goto cleanup;
532 
533 	if (FAILED(hr = pDSCaptureBuffer->Start(DSCBSTART_LOOPING))) {
534 		qWarning("DXAudioInput: Start failed: hr=0x%08lx", hr);
535 	} else {
536 		DWORD dwReadyBytes = 0;
537 		DWORD dwLastReadPos = 0;
538 		float safety = 2.0f;
539 
540 		while (bRunning) {
541 			bool firstsleep = true;
542 			bool didsleep = false;
543 
544 			do {
545 				if (FAILED(hr = pDSCaptureBuffer->GetCurrentPosition(&dwCapturePosition, &dwReadPosition))) {
546 					qWarning("DXAudioInput: GetCurrentPosition failed: hr=0x%08lx", hr);
547 					bRunning = false;
548 					break;
549 				}
550 				if (dwReadPosition < dwLastReadPos)
551 					dwReadyBytes = (dwBufferSize - dwLastReadPos) + dwReadPosition;
552 				else
553 					dwReadyBytes = dwReadPosition - dwLastReadPos;
554 
555 				if (static_cast<size_t>(dwReadyBytes) < sizeof(short) * iFrameSize) {
556 					double msecleft = 20.0 - (dwReadyBytes * 20.0) / (sizeof(short) * iFrameSize);
557 
558 					if (didsleep)
559 						safety *= 1.1f;
560 					else if (firstsleep)
561 						safety *= 0.998f;
562 
563 					int msec = static_cast<int>(msecleft + (firstsleep ? safety : 0.0));
564 
565 					msleep(msec);
566 
567 					didsleep = true;
568 					firstsleep = false;
569 				}
570 			} while (static_cast<size_t>(dwReadyBytes) < sizeof(short) * iFrameSize);
571 
572 			// Desynchonized?
573 			if (dwReadyBytes > (dwBufferSize / 2)) {
574 				qWarning("DXAudioInput: Lost synchronization");
575 				dwLastReadPos = dwReadPosition;
576 			} else if (bRunning) {
577 				if (FAILED(hr = pDSCaptureBuffer->Lock(dwLastReadPos, sizeof(short) * iFrameSize, &aptr1, &nbytes1, &aptr2, &nbytes2, 0))) {
578 					qWarning("DXAudioInput: Lock from %lu (%zu bytes) failed: hr=0x%08lx", static_cast<unsigned long>(dwLastReadPos), sizeof(short) * iFrameSize, hr);
579 					bRunning = false;
580 					break;
581 				}
582 
583 				if (aptr1 && nbytes1)
584 					CopyMemory(psMic, aptr1, nbytes1);
585 
586 				if (aptr2 && nbytes2)
587 					CopyMemory(psMic+nbytes1/2, aptr2, nbytes2);
588 
589 				if (FAILED(hr = pDSCaptureBuffer->Unlock(aptr1, nbytes1, aptr2, nbytes2))) {
590 					qWarning("DXAudioInput: Unlock failed: hr=0x%08lx", hr);
591 					bRunning = false;
592 					break;
593 				}
594 
595 				dwLastReadPos = (dwLastReadPos + sizeof(short) * iFrameSize) % dwBufferSize;
596 
597 				encodeAudioFrame();
598 			}
599 		}
600 		if (! FAILED(hr))
601 			pDSCaptureBuffer->Stop();
602 	}
603 	if (FAILED(hr)) {
604 		g.mw->msgBox(tr("Lost DirectSound input device."));
605 	}
606 
607 cleanup:
608 	if (! bOk) {
609 		g.mw->msgBox(tr("Opening chosen DirectSound Input device failed. No microphone capture will be done."));
610 	}
611 	if (pDSNotify)
612 		pDSNotify->Release();
613 	if (pDSCaptureBuffer)
614 		pDSCaptureBuffer->Release();
615 	if (pDSCapture)
616 		pDSCapture->Release();
617 }
618