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 #include "WASAPI.h"
9 #include "WASAPINotificationClient.h"
10 
11 #include "MainWindow.h"
12 
13 // We define a global macro called 'g'. This can lead to issues when included code uses 'g' as a type or parameter name (like protobuf 3.7 does). As such, for now, we have to make this our last include.
14 #include "Global.h"
15 
16 // Now that Win7 is published, which includes public versions of these
17 // interfaces, we simply inherit from those but use the "old" IIDs.
18 
19 DEFINE_GUID(IID_IVistaAudioSessionControl2, 0x33969B1DL, 0xD06F, 0x4281, 0xB8, 0x37, 0x7E, 0xAA, 0xFD, 0x21, 0xA9, 0xC0);
20 MIDL_INTERFACE("33969B1D-D06F-4281-B837-7EAAFD21A9C0")
21 IVistaAudioSessionControl2 :
22 public IAudioSessionControl2 {
23 };
24 
25 DEFINE_GUID(IID_IAudioSessionQuery, 0x94BE9D30L, 0x53AC, 0x4802, 0x82, 0x9C, 0xF1, 0x3E, 0x5A, 0xD3, 0x47, 0x75);
26 MIDL_INTERFACE("94BE9D30-53AC-4802-829C-F13E5AD34775")
27 IAudioSessionQuery :
28 public IUnknown {
29 	virtual HRESULT STDMETHODCALLTYPE GetQueryInterface(IAudioSessionEnumerator **) = 0;
30 };
31 
32 /// Convert the configured 'wasapi/role' to an ERole.
33 static ERole WASAPIRoleFromSettings() {
34 	QString role = g.s.qsWASAPIRole.toLower().trimmed();
35 
36 	if (role == QLatin1String("console")) {
37 		return eConsole;
38 	} else if (role == QLatin1String("multimedia")) {
39 		return eMultimedia;
40 	}
41 
42 	return eCommunications;
43 }
44 
45 class WASAPIInputRegistrar : public AudioInputRegistrar {
46 	public:
47 		WASAPIInputRegistrar();
48 		virtual AudioInput *create();
49 		virtual const QList<audioDevice> getDeviceChoices();
50 		virtual void setDeviceChoice(const QVariant &, Settings &);
51 		virtual bool canEcho(const QString &) const;
52 		virtual bool canExclusive() const;
53 };
54 
55 class WASAPIOutputRegistrar : public AudioOutputRegistrar {
56 	public:
57 		WASAPIOutputRegistrar();
58 		virtual AudioOutput *create();
59 		virtual const QList<audioDevice> getDeviceChoices();
60 		virtual void setDeviceChoice(const QVariant &, Settings &);
61 		bool canMuteOthers() const;
62 		virtual bool canExclusive() const;
63 };
64 
65 class WASAPIInit : public DeferInit {
66 		WASAPIInputRegistrar *wirReg;
67 		WASAPIOutputRegistrar *worReg;
68 	public:
69 		WASAPIInit() : wirReg(NULL), worReg(NULL) { }
70 		void initialize();
71 		void destroy();
72 };
73 
74 static WASAPIInit wasapiinit;
75 
76 extern bool bIsWin7, bIsVistaSP1;
77 
78 void WASAPIInit::initialize() {
79 	wirReg = NULL;
80 	worReg = NULL;
81 
82 	if (! bIsVistaSP1) {
83 		qWarning("WASAPIInit: Requires Vista SP1");
84 		return;
85 	}
86 
87 	HMODULE hLib = LoadLibrary(L"AVRT.DLL");
88 	if (hLib == NULL) {
89 		qWarning("WASAPIInit: Failed to load avrt.dll");
90 		return;
91 	}
92 	FreeLibrary(hLib);
93 
94 	wirReg = new WASAPIInputRegistrar();
95 	worReg = new WASAPIOutputRegistrar();
96 }
97 
98 void WASAPIInit::destroy() {
99 	delete wirReg;
100 	delete worReg;
101 }
102 
103 
104 WASAPIInputRegistrar::WASAPIInputRegistrar() : AudioInputRegistrar(QLatin1String("WASAPI"), 10) {
105 }
106 
107 
108 /// Calls getMixFormat on given IAudioClient and checks whether it is compatible.
109 /// At the moment this means the format is either 32bit float or 16bit PCM.
110 ///
111 /// @param sourceName Name to prepend to log in case of error
112 /// @param deviceName Device name to refer to in case of error
113 /// @param audioClient IAudioClient to get and check mix format for
114 /// @param waveFormatEx WAVEFORMATEX structure to store getMixFormat result in
115 /// @param waveFormatExtensible If waveFormatEx is of type WAVEFORMATEXTENSIBLE receives a cast pointer.
116 /// @param sampleFormat Receives either SampleFloat or SampleShort as valid format
117 /// @return True if mix format is ok. False if incompatible or another error occured.
118 
119 template <typename SAMPLEFORMAT> // Template on SampleFormat enum as AudioOutput and AudioInput each define their own
120 bool getAndCheckMixFormat(const char* sourceName,
121                     const char* deviceName,
122                     IAudioClient* audioClient,
123                     WAVEFORMATEX **waveFormatEx,
124                     WAVEFORMATEXTENSIBLE **waveFormatExtensible,
125                     SAMPLEFORMAT *sampleFormat) {
126 
127 	*waveFormatEx = NULL;
128 	*waveFormatExtensible = NULL;
129 
130 	HRESULT hr = audioClient->GetMixFormat(waveFormatEx);
131 	if (FAILED(hr)) {
132 		qWarning("%s: %s GetMixFormat failed: hr=0x%08lx", sourceName, deviceName, hr);
133 		return false;
134 	}
135 
136 	if ((*waveFormatEx)->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
137 		(*waveFormatExtensible) = reinterpret_cast<WAVEFORMATEXTENSIBLE *>((*waveFormatEx));
138 		if ((*waveFormatExtensible)->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT) {
139 			*sampleFormat = SAMPLEFORMAT::SampleFloat;
140 		} else if ((*waveFormatExtensible)->SubFormat == KSDATAFORMAT_SUBTYPE_PCM) {
141 			*sampleFormat = SAMPLEFORMAT::SampleShort;
142 		} else {
143 			qWarning() << sourceName << ":" << deviceName << "Subformat is not IEEE Float or PCM but:" << (*waveFormatExtensible)->SubFormat;
144 			return false;
145 		}
146 	} else {
147 		if ((*waveFormatEx)->wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
148 			*sampleFormat = SAMPLEFORMAT::SampleFloat;
149 		} else if ((*waveFormatEx)->wFormatTag != WAVE_FORMAT_PCM) {
150 			*sampleFormat = SAMPLEFORMAT::SampleShort;
151 		} else {
152 			qWarning() << sourceName << ":" << deviceName << "format tag is not IEEE Float or PCM but:" << (*waveFormatEx)->wFormatTag;
153 			return false;
154 		}
155 	}
156 
157 	if (*sampleFormat == SAMPLEFORMAT::SampleFloat) {
158 		if ((*waveFormatEx)->wBitsPerSample != (sizeof(float) * 8)) {
159 			qWarning() << sourceName << ":" << deviceName << "unexpected number of bits per sample for IEEE Float:" << (*waveFormatEx)->wBitsPerSample;
160 			return false;
161 		}
162 	} else if (*sampleFormat == SAMPLEFORMAT::SampleShort) {
163 		if ((*waveFormatEx)->wBitsPerSample != (sizeof(short) * 8)) {
164 			qWarning() << sourceName << ":" << deviceName << "unexpected number of bits per sample for PCM:" << (*waveFormatEx)->wBitsPerSample;
165 			return false;
166 		}
167 	} else {
168 		qFatal("%s: %s unexpected sample format %lu", sourceName, deviceName, static_cast<unsigned long>(*sampleFormat));
169 		return false;
170 	}
171 
172 	return true;
173 }
174 
175 
176 AudioInput *WASAPIInputRegistrar::create() {
177 	return new WASAPIInput();
178 }
179 
180 const QList<audioDevice> WASAPIInputRegistrar::getDeviceChoices() {
181 	return WASAPISystem::mapToDevice(WASAPISystem::getInputDevices(), g.s.qsWASAPIInput);
182 }
183 
184 void WASAPIInputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) {
185 	s.qsWASAPIInput = choice.toString();
186 }
187 
188 bool WASAPIInputRegistrar::canEcho(const QString &outputsys) const {
189 	return (outputsys == name);
190 }
191 
192 bool WASAPIInputRegistrar::canExclusive() const {
193 	return true;
194 }
195 
196 WASAPIOutputRegistrar::WASAPIOutputRegistrar() : AudioOutputRegistrar(QLatin1String("WASAPI"), 10) {
197 }
198 
199 AudioOutput *WASAPIOutputRegistrar::create() {
200 	return new WASAPIOutput();
201 }
202 
203 const QList<audioDevice> WASAPIOutputRegistrar::getDeviceChoices() {
204 	return WASAPISystem::mapToDevice(WASAPISystem::getOutputDevices(), g.s.qsWASAPIOutput);
205 }
206 
207 void WASAPIOutputRegistrar::setDeviceChoice(const QVariant &choice, Settings &s) {
208 	s.qsWASAPIOutput = choice.toString();
209 }
210 
211 bool WASAPIOutputRegistrar::canMuteOthers() const {
212 	return true;
213 }
214 
215 bool WASAPIOutputRegistrar::canExclusive() const {
216 	return true;
217 }
218 
219 const QHash<QString, QString> WASAPISystem::getInputDevices() {
220 	return getDevices(eCapture);
221 }
222 
223 const QHash<QString, QString> WASAPISystem::getOutputDevices() {
224 	return getDevices(eRender);
225 }
226 
227 const QHash<QString, QString> WASAPISystem::getDevices(EDataFlow dataflow) {
228 	QHash<QString, QString> devices;
229 
230 	HRESULT hr;
231 
232 	IMMDeviceEnumerator *pEnumerator = NULL;
233 	IMMDeviceCollection *pCollection = NULL;
234 
235 	hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), reinterpret_cast<void **>(&pEnumerator));
236 
237 	if (! pEnumerator || FAILED(hr)) {
238 		qWarning("WASAPI: Failed to instatiate enumerator: hr=0x%08lx", hr);
239 	} else {
240 		hr = pEnumerator->EnumAudioEndpoints(dataflow, DEVICE_STATE_ACTIVE, &pCollection);
241 		if (! pCollection || FAILED(hr)) {
242 			qWarning("WASAPI: Failed to enumerate: hr=0x%08lx", hr);
243 		} else {
244 			devices.insert(QString(), tr("Default Device"));
245 
246 			UINT ndev = 0;
247 			pCollection->GetCount(&ndev);
248 			for (unsigned int idx=0;idx<ndev;++idx) {
249 				IMMDevice *pDevice = NULL;
250 				IPropertyStore *pStore = NULL;
251 
252 				pCollection->Item(idx, &pDevice);
253 				pDevice->OpenPropertyStore(STGM_READ, &pStore);
254 
255 				LPWSTR strid = NULL;
256 				pDevice->GetId(&strid);
257 
258 				PROPVARIANT varName;
259 				PropVariantInit(&varName);
260 
261 				pStore->GetValue(PKEY_Device_FriendlyName, &varName);
262 
263 				devices.insert(QString::fromWCharArray(strid), QString::fromWCharArray(varName.pwszVal));
264 
265 				PropVariantClear(&varName);
266 				CoTaskMemFree(strid);
267 
268 				pStore->Release();
269 				pDevice->Release();
270 			}
271 			pCollection->Release();
272 		}
273 		pEnumerator->Release();
274 	}
275 
276 	return devices;
277 }
278 
279 const QList<audioDevice> WASAPISystem::mapToDevice(const QHash<QString, QString>& devs, const QString& match) {
280 	QList<audioDevice> qlReturn;
281 
282 	QStringList qlDevices = devs.keys();
283 	qSort(qlDevices);
284 
285 	if (qlDevices.contains(match)) {
286 		qlDevices.removeAll(match);
287 		qlDevices.prepend(match);
288 	}
289 
290 	foreach(const QString &dev, qlDevices) {
291 		qlReturn << audioDevice(devs.value(dev), dev);
292 	}
293 	return qlReturn;
294 }
295 
296 WASAPIInput::WASAPIInput() {
297 };
298 
299 WASAPIInput::~WASAPIInput() {
300 	bRunning = false;
301 	wait();
302 }
303 
304 static IMMDevice *openNamedOrDefaultDevice(const QString& name, EDataFlow dataFlow, ERole role) {
305 	HRESULT hr;
306 	IMMDeviceEnumerator *pEnumerator = NULL;
307 
308 	hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), reinterpret_cast<void **>(&pEnumerator));
309 	if (!pEnumerator || FAILED(hr)) {
310 		qWarning("WASAPI: Failed to instantiate enumerator: hr=0x%08lx", hr);
311 		return NULL;
312 	}
313 
314 	IMMDevice *pDevice = NULL;
315 	// Try to find a device pointer for |name|.
316 	if (!name.isEmpty()) {
317 		STACKVAR(wchar_t, devname, name.length() + 1);
318 		int len = name.toWCharArray(devname);
319 		devname[len] = 0;
320 		hr = pEnumerator->GetDevice(devname, &pDevice);
321 		if (FAILED(hr)) {
322 			qWarning("WASAPI: Failed to open selected device %s %ls (df=%d, e=%d, hr=0x%08lx), falling back to default", qPrintable(name), devname, dataFlow, role, hr);
323 		} else {
324 			WASAPINotificationClient::get().enlistDeviceAsUsed(devname);
325 		}
326 	}
327 
328 	// Use the default device if |pDevice| is still NULL.
329 	// We retrieve the actual device name for the currently selected default device and
330 	// open the device by it's real name to work around triggering the automatic
331 	// ducking behavior.
332 	if (!pDevice) {
333 		hr = pEnumerator->GetDefaultAudioEndpoint(dataFlow, role, &pDevice);
334 		if (FAILED(hr)) {
335 			qWarning("WASAPI: Failed to open device: df=%d, e=%d, hr=0x%08lx", dataFlow, role, hr);
336 			goto cleanup;
337 		}
338 		wchar_t *devname = NULL;
339 		hr = pDevice->GetId(&devname);
340 		if (FAILED(hr)) {
341 			qWarning("WASAPI: Failed to query device: df=%d, e=%d, hr=0x%08lx", dataFlow, role, hr);
342 			goto cleanup;
343 		}
344 		pDevice->Release();
345 		hr = pEnumerator->GetDevice(devname, &pDevice);
346 		if (FAILED(hr)) {
347 			qWarning("WASAPI: Failed to reopen default device: df=%d, e=%d, hr=0x%08lx", dataFlow, role, hr);
348 			goto cleanup;
349 		}
350 		WASAPINotificationClient::get().enlistDefaultDeviceAsUsed(devname);
351 		CoTaskMemFree(devname);
352 	}
353 
354 cleanup:
355 	if (pEnumerator)
356 		pEnumerator->Release();
357 
358 	return pDevice;
359 }
360 
361 void WASAPIInput::run() {
362 	HRESULT hr;
363 	IMMDevice *pMicDevice = NULL;
364 	IAudioClient *pMicAudioClient = NULL;
365 	IAudioCaptureClient *pMicCaptureClient = NULL;
366 	IMMDevice *pEchoDevice = NULL;
367 	IAudioClient *pEchoAudioClient = NULL;
368 	IAudioCaptureClient *pEchoCaptureClient = NULL;
369 	WAVEFORMATEX *micpwfx = NULL, *echopwfx = NULL;
370 	WAVEFORMATEXTENSIBLE *micpwfxe = NULL, *echopwfxe = NULL;
371 	WAVEFORMATEXTENSIBLE wfe;
372 	UINT32 bufferFrameCount;
373 	UINT32 numFramesAvailable;
374 	UINT32 micPacketLength = 0, echoPacketLength = 0;
375 	UINT32 allocLength;
376 	UINT64 devicePosition;
377 	UINT64 qpcPosition;
378 	HANDLE hEvent;
379 	BYTE *pData;
380 	DWORD flags;
381 	DWORD dwTaskIndex = 0;
382 	HANDLE hMmThread;
383 	float *tbuff = NULL;
384 	short *sbuff = NULL;
385 	bool doecho = g.s.doEcho();
386 	REFERENCE_TIME def, min, latency, want;
387 	bool exclusive = false;
388 
389 	CoInitialize(NULL);
390 
391 	hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
392 
393 	hMmThread = AvSetMmThreadCharacteristics(L"Pro Audio", &dwTaskIndex);
394 	if (hMmThread == NULL) {
395 		qWarning("WASAPIInput: Failed to set Pro Audio thread priority");
396 	}
397 
398 	// Open mic device.
399 	pMicDevice = openNamedOrDefaultDevice(g.s.qsWASAPIInput, eCapture, WASAPIRoleFromSettings());
400 	if (!pMicDevice)
401 		goto cleanup;
402 
403 	// Open echo capture device.
404 	if (doecho) {
405 		pEchoDevice = openNamedOrDefaultDevice(g.s.qsWASAPIOutput, eRender, WASAPIRoleFromSettings());
406 		if (!pEchoDevice)
407 			doecho = false;
408 	}
409 
410 	hr = pMicDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void **) &pMicAudioClient);
411 	if (FAILED(hr)) {
412 		qWarning("WASAPIInput: Activate Mic AudioClient failed: hr=0x%08lx", hr);
413 		goto cleanup;
414 	}
415 
416 	def = min = latency = 0;
417 
418 	pMicAudioClient->GetDevicePeriod(&def, &min);
419 
420 	want = qMax<REFERENCE_TIME>(min, 100000);
421 	qWarning("WASAPIInput: Latencies %lld %lld => %lld", def, min, want);
422 
423 	if (g.s.bExclusiveInput && ! doecho) {
424 		for (int channels = 1; channels<=2; ++channels) {
425 			ZeroMemory(&wfe, sizeof(wfe));
426 			wfe.Format.cbSize = 0;
427 			wfe.Format.wFormatTag = WAVE_FORMAT_PCM;
428 			wfe.Format.nChannels = channels;
429 			wfe.Format.nSamplesPerSec = 48000;
430 			wfe.Format.wBitsPerSample = 16;
431 			wfe.Format.nBlockAlign = wfe.Format.nChannels * wfe.Format.wBitsPerSample / 8;
432 			wfe.Format.nAvgBytesPerSec = wfe.Format.nBlockAlign * wfe.Format.nSamplesPerSec;
433 
434 			micpwfxe = &wfe;
435 			micpwfx = reinterpret_cast<WAVEFORMATEX *>(&wfe);
436 
437 			hr = pMicAudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, want, want, micpwfx, NULL);
438 			if (SUCCEEDED(hr)) {
439 				eMicFormat = SampleShort;
440 				exclusive = true;
441 				qWarning("WASAPIInput: Successfully opened exclusive mode");
442 				break;
443 			}
444 
445 			micpwfxe = NULL;
446 			micpwfx = NULL;
447 		}
448 	}
449 
450 	if (!  micpwfxe) {
451 		if (g.s.bExclusiveInput)
452 			qWarning("WASAPIInput: Failed to open exclusive mode.");
453 
454 		if (!getAndCheckMixFormat("WASAPIInput", "Mic", pMicAudioClient,
455 		                          &micpwfx, &micpwfxe, &eMicFormat)) {
456 			goto cleanup;
457 		}
458 
459 		hr = pMicAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, micpwfx, NULL);
460 		if (FAILED(hr)) {
461 			qWarning("WASAPIInput: Mic Initialize failed: hr=0x%08lx", hr);
462 			if (hr == E_ACCESSDENIED) {
463 				g.mw->msgBox(tr("Access to the microphone was denied. Please check that your operating system's microphone settings allow Mumble to use the microphone."));
464 			}
465 			goto cleanup;
466 		}
467 	}
468 
469 	qWarning() << "WASAPIInput: Mic Stream format" << eMicFormat;
470 
471 	pMicAudioClient->GetStreamLatency(&latency);
472 	hr = pMicAudioClient->GetBufferSize(&bufferFrameCount);
473 	qWarning("WASAPIInput: Stream Latency %lld (%d)", latency, bufferFrameCount);
474 
475 	hr = pMicAudioClient->GetService(__uuidof(IAudioCaptureClient), (void**)&pMicCaptureClient);
476 	if (FAILED(hr)) {
477 		qWarning("WASAPIInput: Mic GetService failed: hr=0x%08lx", hr);
478 		goto cleanup;
479 	}
480 
481 	pMicAudioClient->SetEventHandle(hEvent);
482 	if (FAILED(hr)) {
483 		qWarning("WASAPIInput: Failed to set mic event: hr=0x%08lx", hr);
484 		goto cleanup;
485 	}
486 
487 	hr = pMicAudioClient->Start();
488 	if (FAILED(hr)) {
489 		qWarning("WASAPIInput: Failed to start mic: hr=0x%08lx", hr);
490 		goto cleanup;
491 	}
492 
493 	iMicChannels = micpwfx->nChannels;
494 	iMicFreq = micpwfx->nSamplesPerSec;
495 
496 	if (doecho) {
497 		hr = pEchoDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void **) &pEchoAudioClient);
498 		if (FAILED(hr)) {
499 			qWarning("WASAPIInput: Activate Echo AudioClient failed: hr=0x%08lx", hr);
500 			goto cleanup;
501 		}
502 
503 		if (!getAndCheckMixFormat("WASAPIInput", "Echo", pEchoAudioClient,
504 		                          &echopwfx, &echopwfxe, &eEchoFormat)) {
505 			goto cleanup;
506 		}
507 
508 		hr = pEchoAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_LOOPBACK, 0, 0, echopwfx, NULL);
509 		if (FAILED(hr)) {
510 			qWarning("WASAPIInput: Echo Initialize failed: hr=0x%08lx", hr);
511 			goto cleanup;
512 		}
513 
514 		hr = pEchoAudioClient->GetBufferSize(&bufferFrameCount);
515 		hr = pEchoAudioClient->GetService(__uuidof(IAudioCaptureClient), (void**)&pEchoCaptureClient);
516 		if (FAILED(hr)) {
517 			qWarning("WASAPIInput: Echo GetService failed: hr=0x%08lx", hr);
518 			goto cleanup;
519 		}
520 
521 		pEchoAudioClient->SetEventHandle(hEvent);
522 		if (FAILED(hr)) {
523 			qWarning("WASAPIInput: Failed to set echo event: hr=0x%08lx", hr);
524 			goto cleanup;
525 		}
526 
527 		hr = pEchoAudioClient->Start();
528 		if (FAILED(hr)) {
529 			qWarning("WASAPIInput: Failed to start Echo: hr=0x%08lx", hr);
530 			goto cleanup;
531 		}
532 
533 		qWarning() << "WASAPIInput: Echo Stream format" << eEchoFormat;
534 
535 		iEchoChannels = echopwfx->nChannels;
536 		iEchoFreq = echopwfx->nSamplesPerSec;
537 	}
538 
539 	initializeMixer();
540 
541 	allocLength = (iMicLength / 2) * micpwfx->nChannels;
542 
543 	if (exclusive) {
544 		sbuff = new short[allocLength];
545 		while (bRunning && ! FAILED(hr)) {
546 			hr = pMicCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, &devicePosition, &qpcPosition);
547 			if (hr != AUDCLNT_S_BUFFER_EMPTY) {
548 				if (FAILED(hr))
549 					goto cleanup;
550 
551 				UINT32 nFrames = numFramesAvailable * micpwfx->nChannels;
552 				if (nFrames > allocLength) {
553 					delete [] sbuff;
554 					allocLength = nFrames;
555 					sbuff = new short[allocLength];
556 				}
557 
558 				memcpy(sbuff, pData, nFrames * sizeof(short));
559 				hr = pMicCaptureClient->ReleaseBuffer(numFramesAvailable);
560 				if (FAILED(hr))
561 					goto cleanup;
562 				addMic(sbuff, numFramesAvailable);
563 			}
564 			if (! FAILED(hr))
565 				WaitForSingleObject(hEvent, 100);
566 		}
567 	} else {
568 		tbuff = new float[allocLength];
569 		while (bRunning && ! FAILED(hr)) {
570 			hr = pMicCaptureClient->GetNextPacketSize(&micPacketLength);
571 			if (! FAILED(hr) && iEchoChannels)
572 				hr = pEchoCaptureClient->GetNextPacketSize(&echoPacketLength);
573 			if (FAILED(hr)) {
574 				qWarning("WASAPIInput: GetNextPacketSize failed: hr=0x%08lx", hr);
575 				goto cleanup;
576 			}
577 
578 			while ((micPacketLength > 0) || (echoPacketLength > 0)) {
579 				if (echoPacketLength > 0) {
580 					hr = pEchoCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, &devicePosition, &qpcPosition);
581 					if (FAILED(hr)) {
582 						qWarning("WASAPIInput: GetBuffer failed: hr=0x%08lx", hr);
583 						goto cleanup;
584 					}
585 
586 					UINT32 nFrames = numFramesAvailable * echopwfx->nChannels;
587 					if (nFrames > allocLength) {
588 						delete [] tbuff;
589 						allocLength = nFrames;
590 						tbuff = new float[allocLength];
591 					}
592 					memcpy(tbuff, pData, nFrames * sizeof(float));
593 					hr = pEchoCaptureClient->ReleaseBuffer(numFramesAvailable);
594 					if (FAILED(hr)) {
595 						qWarning("WASAPIInput: ReleaseBuffer failed: hr=0x%08lx", hr);
596 						goto cleanup;
597 					}
598 					addEcho(tbuff, numFramesAvailable);
599 				} else if (micPacketLength > 0) {
600 					hr = pMicCaptureClient->GetBuffer(&pData, &numFramesAvailable, &flags, &devicePosition, &qpcPosition);
601 					if (FAILED(hr)) {
602 						qWarning("WASAPIInput: GetBuffer failed: hr=0x%08lx", hr);
603 						goto cleanup;
604 					}
605 
606 					UINT32 nFrames = numFramesAvailable * micpwfx->nChannels;
607 					if (nFrames > allocLength) {
608 						delete [] tbuff;
609 						allocLength = nFrames;
610 						tbuff = new float[allocLength];
611 					}
612 					memcpy(tbuff, pData, nFrames * sizeof(float));
613 					hr = pMicCaptureClient->ReleaseBuffer(numFramesAvailable);
614 					if (FAILED(hr)) {
615 						qWarning("WASAPIInput: ReleaseBuffer failed: hr=0x%08lx", hr);
616 						goto cleanup;
617 					}
618 					addMic(tbuff, numFramesAvailable);
619 				}
620 				hr = pMicCaptureClient->GetNextPacketSize(&micPacketLength);
621 				if (! FAILED(hr) && iEchoChannels)
622 					hr = pEchoCaptureClient->GetNextPacketSize(&echoPacketLength);
623 			}
624 			if (! FAILED(hr))
625 				WaitForSingleObject(hEvent, 2000);
626 		}
627 	}
628 
629 cleanup:
630 	if (micpwfx && !exclusive)
631 		CoTaskMemFree(micpwfx);
632 	if (echopwfx)
633 		CoTaskMemFree(echopwfx);
634 
635 	if (pMicAudioClient) {
636 		pMicAudioClient->Stop();
637 		pMicAudioClient->Release();
638 	}
639 	if (pMicCaptureClient)
640 		pMicCaptureClient->Release();
641 	if (pMicDevice)
642 		pMicDevice->Release();
643 
644 	if (pEchoAudioClient) {
645 		pEchoAudioClient->Stop();
646 		pEchoAudioClient->Release();
647 	}
648 	if (pEchoCaptureClient)
649 		pEchoCaptureClient->Release();
650 	if (pEchoDevice)
651 		pEchoDevice->Release();
652 
653 	if (hMmThread != NULL)
654 		AvRevertMmThreadCharacteristics(hMmThread);
655 
656 	if (hEvent != NULL)
657 		CloseHandle(hEvent);
658 
659 	delete [] tbuff;
660 	delete [] sbuff;
661 }
662 
663 WASAPIOutput::WASAPIOutput() {
664 }
665 
666 WASAPIOutput::~WASAPIOutput() {
667 	bRunning = false;
668 	wait();
669 }
670 
671 void WASAPIOutput::setVolumes(IMMDevice *pDevice, bool talking) {
672 	HRESULT hr;
673 
674 	if (! talking) {
675 		QMap<ISimpleAudioVolume *, VolumePair>::const_iterator i;
676 		for (i=qmVolumes.constBegin(); i != qmVolumes.constEnd(); ++i) {
677 			float fVolume = 1.0f;
678 			hr = i.key()->GetMasterVolume(&fVolume);
679 			if (qFuzzyCompare(i.value().second, fVolume))
680 				hr = i.key()->SetMasterVolume(i.value().first, NULL);
681 			i.key()->Release();
682 		}
683 		qmVolumes.clear();
684 		return;
685 	}
686 
687 	IAudioSessionManager2 *pAudioSessionManager = NULL;
688 	int max = 0;
689 	DWORD dwMumble = GetCurrentProcessId();
690 
691 	qmVolumes.clear();
692 	if (qFuzzyCompare(g.s.fOtherVolume, 1.0f))
693 		return;
694 
695 	// FIXME: Try to keep the session object around when returning volume.
696 
697 	if (SUCCEEDED(hr = pDevice->Activate(bIsWin7 ? __uuidof(IAudioSessionManager2) : __uuidof(IAudioSessionManager), CLSCTX_ALL, NULL, (void **) &pAudioSessionManager))) {
698 		IAudioSessionEnumerator *pEnumerator = NULL;
699 		IAudioSessionQuery *pMysticQuery = NULL;
700 		if (! bIsWin7) {
701 			if (SUCCEEDED(hr = pAudioSessionManager->QueryInterface(IID_IAudioSessionQuery, (void **) &pMysticQuery))) {
702 				hr = pMysticQuery->GetQueryInterface(&pEnumerator);
703 			}
704 		} else {
705 			hr = pAudioSessionManager->GetSessionEnumerator(&pEnumerator);
706 		}
707 
708 		QSet<QUuid> seen;
709 
710 		if (SUCCEEDED(hr)) {
711 			if (SUCCEEDED(hr = pEnumerator->GetCount(&max))) {
712 				for (int i=0;i<max;++i) {
713 					IAudioSessionControl *pControl = NULL;
714 					if (SUCCEEDED(hr = pEnumerator->GetSession(i, &pControl))) {
715 						setVolumeForSessionControl(pControl, dwMumble, seen);
716 						pControl->Release();
717 					}
718 				}
719 			}
720 			pEnumerator->Release();
721 		}
722 		if (pMysticQuery)
723 			pMysticQuery->Release();
724 		pAudioSessionManager->Release();
725 	}
726 }
727 
728 bool WASAPIOutput::setVolumeForSessionControl2(IAudioSessionControl2 *control2, const DWORD mumblePID, QSet<QUuid> &seen) {
729 	HRESULT hr;
730 	DWORD pid;
731 
732 	// Don't set the volume for our own control
733 	if (FAILED(hr = control2->GetProcessId(&pid)) || (pid == mumblePID))
734 		return true;
735 
736 	// Don't work on expired audio sessions
737 	AudioSessionState ass;
738 	if (FAILED(hr = control2->GetState(&ass)) || (ass == AudioSessionStateExpired))
739 		return false;
740 
741 	// Don't act twice on the same session
742 	GUID group;
743 	if (FAILED(hr = control2->GetGroupingParam(&group)))
744 		return false;
745 
746 	QUuid quuid(group);
747 	if (seen.contains(quuid))
748 		return true;
749 
750 	seen.insert(quuid);
751 
752 	// Adjust volume
753 	ISimpleAudioVolume *pVolume = NULL;
754 	if (FAILED(hr = control2->QueryInterface(__uuidof(ISimpleAudioVolume), (void **) &pVolume)))
755 		return false;
756 
757 	BOOL bMute = TRUE;
758 	bool keep = false;
759 	if (SUCCEEDED(hr = pVolume->GetMute(&bMute)) && ! bMute) {
760 		float fVolume = 1.0f;
761 		if (SUCCEEDED(hr = pVolume->GetMasterVolume(&fVolume)) && ! qFuzzyCompare(fVolume,0.0f)) {
762 			float fSetVolume = fVolume * g.s.fOtherVolume;
763 			if (SUCCEEDED(hr = pVolume->SetMasterVolume(fSetVolume, NULL))) {
764 				hr = pVolume->GetMasterVolume(&fSetVolume);
765 				qmVolumes.insert(pVolume, VolumePair(fVolume,fSetVolume));
766 				keep = true;
767 			}
768 		}
769 	}
770 
771 	if (! keep)
772 		pVolume->Release();
773 
774 	return true;
775 }
776 
777 bool WASAPIOutput::setVolumeForSessionControl(IAudioSessionControl *control, const DWORD mumblePID, QSet<QUuid> &seen) {
778 	HRESULT hr;
779 	IAudioSessionControl2 *pControl2 = NULL;
780 
781 	if (!SUCCEEDED(hr = control->QueryInterface(bIsWin7 ? __uuidof(IAudioSessionControl2) : IID_IVistaAudioSessionControl2, (void **) &pControl2)))
782 		return false;
783 
784 	bool result = setVolumeForSessionControl2(pControl2, mumblePID, seen);
785 
786 	pControl2->Release();
787 	return result;
788 }
789 
790 static void SetDuckingOptOut(IMMDevice *pDevice) {
791 	if (!bIsWin7)
792 		return;
793 
794 	HRESULT hr;
795 	IAudioSessionManager2 *pSessionManager2 = NULL;
796 	IAudioSessionControl *pSessionControl = NULL;
797 	IAudioSessionControl2 *pSessionControl2 = NULL;
798 
799 	// Get session manager & control1+2 to disable ducking
800 	hr = pDevice->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL, NULL, reinterpret_cast<void**>(&pSessionManager2));
801 	if (FAILED(hr)) {
802 		qWarning("WASAPIOutput: Activate AudioSessionManager2 failed: hr=0x%08lx", hr);
803 		goto cleanup;
804 	}
805 
806 	hr = pSessionManager2->GetAudioSessionControl(NULL, 0, &pSessionControl);
807 	if (FAILED(hr)) {
808 		qWarning("WASAPIOutput: GetAudioSessionControl failed: hr=0x%08lx", hr);
809 		goto cleanup;
810 	}
811 
812 	hr = pSessionControl->QueryInterface(__uuidof(IAudioSessionControl2), reinterpret_cast<void**>(&pSessionControl2));
813 	if (FAILED(hr)) {
814 		qWarning("WASAPIOutput: Querying SessionControl2 failed: hr=0x%08lx", hr);
815 		goto cleanup;
816 	}
817 
818 	hr = pSessionControl2->SetDuckingPreference(TRUE);
819 	if (FAILED(hr)) {
820 		qWarning("WASAPIOutput: Failed to set ducking preference: hr=0x%08lx", hr);
821 		goto cleanup;
822 	}
823 
824 cleanup:
825 	if (pSessionControl2)
826 		pSessionControl2->Release();
827 
828 	if (pSessionControl)
829 		pSessionControl->Release();
830 
831 	if (pSessionManager2)
832 		pSessionManager2->Release();
833 }
834 
835 void WASAPIOutput::run() {
836 	HRESULT hr;
837 	IMMDevice *pDevice = NULL;
838 	IAudioClient *pAudioClient = NULL;
839 	IAudioRenderClient *pRenderClient = NULL;
840 	WAVEFORMATEX *pwfx = NULL;
841 	WAVEFORMATEXTENSIBLE *pwfxe = NULL;
842 	UINT32 bufferFrameCount;
843 	REFERENCE_TIME def, min, latency, want;
844 	UINT32 numFramesAvailable;
845 	HANDLE hEvent;
846 	BYTE *pData;
847 	DWORD dwTaskIndex = 0;
848 	HANDLE hMmThread;
849 	int ns = 0;
850 	unsigned int chanmasks[32];
851 	QMap<DWORD, float> qmVolumes;
852 	bool lastspoke = false;
853 	REFERENCE_TIME bufferDuration = (g.s.iOutputDelay > 1) ? (g.s.iOutputDelay + 1) * 100000 : 0;
854 	bool exclusive = false;
855 	bool mixed = false;
856 
857 	CoInitialize(NULL);
858 
859 	hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
860 
861 	hMmThread = AvSetMmThreadCharacteristics(L"Pro Audio", &dwTaskIndex);
862 	if (hMmThread == NULL) {
863 		qWarning("WASAPIOutput: Failed to set Pro Audio thread priority");
864 	}
865 
866 	// Open the output device.
867 	pDevice = openNamedOrDefaultDevice(g.s.qsWASAPIOutput, eRender, WASAPIRoleFromSettings());
868 	if (!pDevice)
869 		goto cleanup;
870 
871 	// Opt-out of the Windows 7 ducking behavior
872 	SetDuckingOptOut(pDevice);
873 
874 	hr = pDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void **) &pAudioClient);
875 	if (FAILED(hr)) {
876 		qWarning("WASAPIOutput: Activate AudioClient failed: hr=0x%08lx", hr);
877 		goto cleanup;
878 	}
879 
880 	pAudioClient->GetDevicePeriod(&def, &min);
881 	want = qMax<REFERENCE_TIME>(min, 100000);
882 	qWarning("WASAPIOutput: Latencies %lld %lld => %lld", def, min, want);
883 
884 	if (g.s.bExclusiveOutput) {
885 		hr = pAudioClient->GetMixFormat(&pwfx);
886 		if (FAILED(hr)) {
887 			qWarning("WASAPIOutput: GetMixFormat failed: hr=0x%08lx", hr);
888 			goto cleanup;
889 		}
890 
891 		if (pwfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
892 			pwfxe = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(pwfx);
893 		}
894 
895 		if (!g.s.bPositionalAudio) {
896 			// Override mix format and request stereo
897 			pwfx->nChannels = 2;
898 			if (pwfxe) {
899 				pwfxe->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
900 			}
901 		}
902 
903 		pwfx->cbSize = 0;
904 		if (pwfxe) {
905 			pwfxe->SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
906 		} else {
907 			pwfx->wFormatTag = WAVE_FORMAT_PCM;
908 		}
909 		pwfx->nSamplesPerSec = 48000;
910 		pwfx->wBitsPerSample = 16;
911 		pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
912 		pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
913 
914 		hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_EXCLUSIVE, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, want, want, pwfx, NULL);
915 		if (SUCCEEDED(hr)) {
916 			eSampleFormat = SampleShort;
917 			exclusive = true;
918 			qWarning("WASAPIOutput: Successfully opened exclusive mode");
919 		} else {
920 			CoTaskMemFree(pwfx);
921 
922 			pwfxe = NULL;
923 			pwfx = NULL;
924 		}
925 	}
926 
927 	if (!pwfx) {
928 		if (g.s.bExclusiveOutput)
929 			qWarning("WASAPIOutput: Failed to open exclusive mode.");
930 
931 		if (!getAndCheckMixFormat("WASAPIOutput", "Output", pAudioClient,
932 		                          &pwfx, &pwfxe, &eSampleFormat)) {
933 			goto cleanup;
934 		}
935 
936 		if (!g.s.bPositionalAudio) {
937 			pwfx->nChannels = 2;
938 			pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8;
939 			pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec;
940 
941 			if (pwfxe) {
942 				pwfxe->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
943 			}
944 
945 			WAVEFORMATEX *closestFormat = NULL;
946 			hr = pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, pwfx, &closestFormat);
947 			if (hr == S_FALSE) {
948 				qWarning("WASAPIOutput: Driver says no to 2 channel output. Closest format: %d channels @ %lu kHz", closestFormat->nChannels, static_cast<unsigned long>(closestFormat->nSamplesPerSec));
949 				CoTaskMemFree(pwfx);
950 
951 				// Fall back to whatever the device offers.
952 
953 				if (!getAndCheckMixFormat("WASAPIOutput", "Output", pAudioClient,
954 				                          &pwfx, &pwfxe, &eSampleFormat)) {
955 					CoTaskMemFree(closestFormat);
956 					goto cleanup;
957 				}
958 			} else if (FAILED(hr)) {
959 				qWarning("WASAPIOutput: IsFormatSupported failed: hr=0x%08lx", hr);
960 			}
961 
962 			CoTaskMemFree(closestFormat);
963 		}
964 
965 		hr = pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, bufferDuration, 0, pwfx, NULL);
966 		if (FAILED(hr)) {
967 			qWarning("WASAPIOutput: Initialize failed: hr=0x%08lx", hr);
968 			goto cleanup;
969 		}
970 	}
971 
972 	qWarning() << "WASAPIOutput: Output stream format" << eSampleFormat;
973 
974 	pAudioClient->GetStreamLatency(&latency);
975 	pAudioClient->GetBufferSize(&bufferFrameCount);
976 	qWarning("WASAPIOutput: Stream Latency %lld (%d)", latency, bufferFrameCount);
977 
978 	iMixerFreq = pwfx->nSamplesPerSec;
979 
980 	qWarning("WASAPIOutput: Periods %lldus %lldus (latency %lldus)", def / 10LL, min / 10LL, latency / 10LL);
981 	qWarning("WASAPIOutput: Buffer is %dus (%d)", (bufferFrameCount * 1000000) / iMixerFreq, g.s.iOutputDelay);
982 
983 	hr = pAudioClient->GetService(__uuidof(IAudioRenderClient), (void**)&pRenderClient);
984 	if (FAILED(hr)) {
985 		qWarning("WASAPIOutput: GetService failed: hr=0x%08lx", hr);
986 		goto cleanup;
987 	}
988 
989 	pAudioClient->SetEventHandle(hEvent);
990 	if (FAILED(hr)) {
991 		qWarning("WASAPIOutput: Failed to set event: hr=0x%08lx", hr);
992 		goto cleanup;
993 	}
994 
995 	hr = pAudioClient->Start();
996 	if (FAILED(hr)) {
997 		qWarning("WASAPIOutput: Failed to start: hr=0x%08lx", hr);
998 		goto cleanup;
999 	}
1000 
1001 	if (pwfxe) {
1002 		for (int i=0;i<32;i++) {
1003 			if (pwfxe->dwChannelMask & (1 << i)) {
1004 				chanmasks[ns++] = 1 << i;
1005 			}
1006 		}
1007 	} else {
1008 		qWarning("WASAPIOutput: No chanmask available. Assigning in order.");
1009 
1010 		for (int i = 0; i < pwfx->nChannels && i < 32; ++i) {
1011 			chanmasks[ns++] = 1 << i;
1012 		}
1013 	}
1014 
1015 	if (ns != pwfx->nChannels) {
1016 		qWarning("WASAPIOutput: Chanmask bits doesn't match number of channels.");
1017 	}
1018 
1019 	iChannels = pwfx->nChannels;
1020 	initializeMixer(chanmasks);
1021 
1022 	numFramesAvailable = 0;
1023 
1024 	while (bRunning && ! FAILED(hr)) {
1025 		if (!exclusive) {
1026 			// Attenuate stream volumes.
1027 			if (lastspoke != (g.bAttenuateOthers || mixed)) {
1028 				lastspoke = g.bAttenuateOthers || mixed;
1029 				setVolumes(pDevice, lastspoke);
1030 			}
1031 
1032 			hr = pAudioClient->GetCurrentPadding(&numFramesAvailable);
1033 			if (FAILED(hr)) {
1034 				qWarning("WASAPIOutput: GetCurrentPadding failed: hr=0x%08lx", hr);
1035 				goto cleanup;
1036 			}
1037 		}
1038 
1039 		UINT32 packetLength = bufferFrameCount - numFramesAvailable;
1040 
1041 		while (packetLength > 0) {
1042 			hr = pRenderClient->GetBuffer(packetLength, &pData);
1043 			if (FAILED(hr)) {
1044 				qWarning("WASAPIOutput: GetBuffer failed: hr=0x%08lx", hr);
1045 				goto cleanup;
1046 			}
1047 
1048 			mixed = mix(reinterpret_cast<float *>(pData), packetLength);
1049 			if (mixed)
1050 				hr = pRenderClient->ReleaseBuffer(packetLength, 0);
1051 			else
1052 				hr = pRenderClient->ReleaseBuffer(packetLength, AUDCLNT_BUFFERFLAGS_SILENT);
1053 			if (FAILED(hr)) {
1054 				qWarning("WASAPIOutput: ReleaseBuffer failed: hr=0x%08lx", hr);
1055 				goto cleanup;
1056 			}
1057 
1058 			// Exclusive mode rendering ends here.
1059 			if (exclusive)
1060 				break;
1061 
1062 			if (!g.s.bAttenuateOthers && !g.bAttenuateOthers) {
1063 				mixed = false;
1064 			}
1065 
1066 			if (lastspoke != (g.bAttenuateOthers || mixed)) {
1067 				lastspoke = g.bAttenuateOthers || mixed;
1068 				setVolumes(pDevice, lastspoke);
1069 			}
1070 
1071 			hr = pAudioClient->GetCurrentPadding(&numFramesAvailable);
1072 			if (FAILED(hr)) {
1073 				qWarning("WASAPIOutput: GetCurrentPadding failed: hr=0x%08lx", hr);
1074 				goto cleanup;
1075 			}
1076 
1077 			packetLength = bufferFrameCount - numFramesAvailable;
1078 		}
1079 		if (! FAILED(hr))
1080 			WaitForSingleObject(hEvent, exclusive ? 100 : 2000);
1081 	}
1082 
1083 cleanup:
1084 	if (pwfx)
1085 		CoTaskMemFree(pwfx);
1086 
1087 	if (pDevice) {
1088 		setVolumes(pDevice, false);
1089 	}
1090 
1091 	if (pAudioClient) {
1092 		pAudioClient->Stop();
1093 		pAudioClient->Release();
1094 	}
1095 	if (pRenderClient)
1096 		pRenderClient->Release();
1097 	if (pDevice)
1098 		pDevice->Release();
1099 
1100 	if (hMmThread != NULL)
1101 		AvRevertMmThreadCharacteristics(hMmThread);
1102 
1103 	if (hEvent != NULL)
1104 		CloseHandle(hEvent);
1105 }
1106