1 /* Webcamoid, webcam capture application.
2 * Copyright (C) 2016 Gonzalo Exequiel Pedone
3 *
4 * Webcamoid is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * Webcamoid is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with Webcamoid. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Web-Site: http://webcamoid.github.io/
18 */
19
20 #include <QMap>
21 #include <QVector>
22 #include <akaudiopacket.h>
23 #include <audioclient.h>
24 #include <objbase.h>
25 #include <initguid.h>
26 #include <propkeydef.h>
27
28 #include "audiodevwasapi.h"
29
30 DEFINE_PROPERTYKEY(PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);
31
32 #define MAX_ERRORS_READ_WRITE 5
33 #define EVENT_TIMEOUT 1000
34
35 using ErrorCodesMap = QMap<HRESULT, QString>;
36
initErrorCodesMap()37 inline ErrorCodesMap initErrorCodesMap()
38 {
39 ErrorCodesMap errorCodes = {
40 // COM library errors.
41 {REGDB_E_CLASSNOTREG , "REGDB_E_CLASSNOTREG" },
42 {CLASS_E_NOAGGREGATION, "CLASS_E_NOAGGREGATION"},
43 {E_NOINTERFACE , "E_NOINTERFACE" },
44 {E_POINTER , "E_POINTER" },
45
46 // IMMDeviceEnumerator errors.
47 {E_INVALIDARG , "E_INVALIDARG" },
48 {E_NOTFOUND , "E_NOTFOUND" },
49 {E_OUTOFMEMORY, "E_OUTOFMEMORY"},
50
51 // IAudioClient errors.
52 {AUDCLNT_E_ALREADY_INITIALIZED , "AUDCLNT_E_ALREADY_INITIALIZED" },
53 {AUDCLNT_E_WRONG_ENDPOINT_TYPE , "AUDCLNT_E_WRONG_ENDPOINT_TYPE" },
54 {AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED , "AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED" },
55 {AUDCLNT_E_BUFFER_SIZE_ERROR , "AUDCLNT_E_BUFFER_SIZE_ERROR" },
56 {AUDCLNT_E_CPUUSAGE_EXCEEDED , "AUDCLNT_E_CPUUSAGE_EXCEEDED" },
57 {AUDCLNT_E_DEVICE_INVALIDATED , "AUDCLNT_E_DEVICE_INVALIDATED" },
58 {AUDCLNT_E_DEVICE_IN_USE , "AUDCLNT_E_DEVICE_IN_USE" },
59 {AUDCLNT_E_ENDPOINT_CREATE_FAILED , "AUDCLNT_E_ENDPOINT_CREATE_FAILED" },
60 {AUDCLNT_E_INVALID_DEVICE_PERIOD , "AUDCLNT_E_INVALID_DEVICE_PERIOD" },
61 {AUDCLNT_E_UNSUPPORTED_FORMAT , "AUDCLNT_E_UNSUPPORTED_FORMAT" },
62 {AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED , "AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED" },
63 {AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL, "AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL"},
64 {AUDCLNT_E_SERVICE_NOT_RUNNING , "AUDCLNT_E_SERVICE_NOT_RUNNING" },
65 {AUDCLNT_E_NOT_INITIALIZED , "AUDCLNT_E_NOT_INITIALIZED" },
66 {AUDCLNT_E_NOT_STOPPED , "AUDCLNT_E_NOT_STOPPED" },
67 {AUDCLNT_E_EVENTHANDLE_NOT_SET , "AUDCLNT_E_EVENTHANDLE_NOT_SET" },
68 {AUDCLNT_E_INVALID_SIZE , "AUDCLNT_E_INVALID_SIZE" },
69 {AUDCLNT_E_OUT_OF_ORDER , "AUDCLNT_E_OUT_OF_ORDER" },
70 {AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED , "AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED" },
71 {AUDCLNT_E_BUFFER_ERROR , "AUDCLNT_E_BUFFER_ERROR" },
72 {AUDCLNT_E_BUFFER_TOO_LARGE , "AUDCLNT_E_BUFFER_TOO_LARGE" },
73 {AUDCLNT_E_BUFFER_OPERATION_PENDING , "AUDCLNT_E_BUFFER_OPERATION_PENDING" }
74 };
75
76 return errorCodes;
77 }
78
79 Q_GLOBAL_STATIC_WITH_ARGS(ErrorCodesMap, errorCodes, (initErrorCodesMap()))
80
81 class AudioDevWasapiPrivate
82 {
83 public:
84 AudioDevWasapi *self;
85 QString m_error;
86 QStringList m_sources;
87 QStringList m_sinks;
88 QString m_defaultSink;
89 QString m_defaultSource;
90 QMap<QString, QString> m_descriptionMap;
91 QMap<QString, QList<AkAudioCaps::SampleFormat>> m_supportedFormats;
92 QMap<QString, QList<AkAudioCaps::ChannelLayout>> m_supportedLayouts;
93 QMap<QString, QList<int>> m_supportedSampleRates;
94 QMap<QString, AkAudioCaps> m_preferredInputCaps;
95 QMap<QString, AkAudioCaps> m_preferredOutputCaps;
96 QByteArray m_audioBuffer;
97 AkAudioCaps m_curCaps;
98 QString m_curDevice;
99 IMMDeviceEnumerator *m_deviceEnumerator {nullptr};
100 IMMDevice *m_pDevice {nullptr};
101 IAudioClient *m_pAudioClient {nullptr};
102 IAudioCaptureClient *m_pCaptureClient {nullptr};
103 IAudioRenderClient *m_pRenderClient {nullptr};
104 HANDLE m_hEvent {nullptr};
105 ULONG m_cRef {1};
106 int m_samples {0};
107
108 explicit AudioDevWasapiPrivate(AudioDevWasapi *self);
109 bool waveFormatFromCaps(WAVEFORMATEX *wfx,
110 const AkAudioCaps &caps) const;
111 AkAudioCaps capsFromWaveFormat(WAVEFORMATEX *wfx) const;
112 void fillDeviceInfo(const QString &device,
113 QList<AkAudioCaps::SampleFormat> *supportedFormats,
114 QList<AkAudioCaps::ChannelLayout> *supportedLayouts,
115 QList<int> *supportedSampleRates) const;
116 AkAudioCaps preferredCaps(const QString &device,
117 EDataFlow dataFlow) const;
118 };
119
AudioDevWasapi(QObject * parent)120 AudioDevWasapi::AudioDevWasapi(QObject *parent):
121 AudioDev(parent)
122 {
123 this->d = new AudioDevWasapiPrivate(this);
124
125 // Create DeviceEnumerator
126 HRESULT hr;
127
128 // Get device enumerator.
129 if (FAILED(hr = CoCreateInstance(__uuidof(MMDeviceEnumerator),
130 nullptr,
131 CLSCTX_ALL,
132 __uuidof(IMMDeviceEnumerator),
133 reinterpret_cast<void **>(&this->d->m_deviceEnumerator)))) {
134 return;
135 }
136
137 if (FAILED(hr = this->d->m_deviceEnumerator->RegisterEndpointNotificationCallback(this))) {
138 this->d->m_deviceEnumerator->Release();
139 this->d->m_deviceEnumerator = nullptr;
140
141 return;
142 }
143
144 this->updateDevices();
145 }
146
~AudioDevWasapi()147 AudioDevWasapi::~AudioDevWasapi()
148 {
149 this->uninit();
150 this->d->m_deviceEnumerator->UnregisterEndpointNotificationCallback(this);
151 this->d->m_deviceEnumerator->Release();
152 delete this->d;
153 }
154
error() const155 QString AudioDevWasapi::error() const
156 {
157 return this->d->m_error;
158 }
159
defaultInput()160 QString AudioDevWasapi::defaultInput()
161 {
162 return this->d->m_defaultSource;
163 }
164
defaultOutput()165 QString AudioDevWasapi::defaultOutput()
166 {
167 return this->d->m_defaultSink;
168 }
169
inputs()170 QStringList AudioDevWasapi::inputs()
171 {
172 return this->d->m_sources;
173 }
174
outputs()175 QStringList AudioDevWasapi::outputs()
176 {
177 return this->d->m_sinks;
178 }
179
description(const QString & device)180 QString AudioDevWasapi::description(const QString &device)
181 {
182 return this->d->m_descriptionMap.value(device);
183 }
184
185 // Get native format for the default audio device.
preferredFormat(const QString & device)186 AkAudioCaps AudioDevWasapi::preferredFormat(const QString &device)
187 {
188 if (this->d->m_preferredOutputCaps.contains(device))
189 return this->d->m_preferredOutputCaps[device];
190
191 if (this->d->m_preferredInputCaps.contains(device))
192 return this->d->m_preferredInputCaps[device];
193
194 return AkAudioCaps();
195 }
196
supportedFormats(const QString & device)197 QList<AkAudioCaps::SampleFormat> AudioDevWasapi::supportedFormats(const QString &device)
198 {
199 return this->d->m_supportedFormats.value(device);
200 }
201
supportedChannelLayouts(const QString & device)202 QList<AkAudioCaps::ChannelLayout> AudioDevWasapi::supportedChannelLayouts(const QString &device)
203 {
204 return this->d->m_supportedLayouts.value(device);
205 }
206
supportedSampleRates(const QString & device)207 QList<int> AudioDevWasapi::supportedSampleRates(const QString &device)
208 {
209 return this->d->m_supportedSampleRates.value(device);
210 }
211
init(const QString & device,const AkAudioCaps & caps)212 bool AudioDevWasapi::init(const QString &device, const AkAudioCaps &caps)
213 {
214 return this->init(device, caps, false);
215 }
216
init(const QString & device,const AkAudioCaps & caps,bool justActivate)217 bool AudioDevWasapi::init(const QString &device,
218 const AkAudioCaps &caps,
219 bool justActivate)
220 {
221 if (!this->d->m_deviceEnumerator) {
222 this->d->m_error = "Device enumerator not created.";
223 emit this->errorChanged(this->d->m_error);
224
225 return false;
226 }
227
228 // Clear audio buffer.
229 this->d->m_audioBuffer.clear();
230
231 HRESULT hr;
232
233 // Get audio device.
234 if (FAILED(hr = this->d->m_deviceEnumerator->GetDevice(device.toStdWString().c_str(),
235 &this->d->m_pDevice))) {
236 this->d->m_error = "GetDevice: " + errorCodes->value(hr);
237 emit this->errorChanged(this->d->m_error);
238 this->uninit();
239
240 return false;
241 }
242
243 // Get an instance for the audio client.
244 if (FAILED(hr = this->d->m_pDevice->Activate(__uuidof(IAudioClient),
245 CLSCTX_ALL,
246 nullptr,
247 reinterpret_cast<void **>(&this->d->m_pAudioClient)))) {
248 this->d->m_error = "Activate: " + errorCodes->value(hr);
249 emit this->errorChanged(this->d->m_error);
250 this->uninit();
251
252 return false;
253 }
254
255 // Just get the audio client instance.
256 if (justActivate)
257 return true;
258
259 // Get the minimum size of the buffer in 100-nanosecond units,
260 // this means you must do:
261 //
262 // bufferSize(seconds) = 100e-9 * hnsRequestedDuration
263 //
264 // to get the size of the buffer in seconds.
265 //
266 REFERENCE_TIME hnsRequestedDuration;
267 this->d->m_pAudioClient->GetDevicePeriod(nullptr, &hnsRequestedDuration);
268
269 // Accumulate a minimum of 1 sec. of audio in the buffer.
270 REFERENCE_TIME minDuration = 10e6;
271
272 if (hnsRequestedDuration < minDuration)
273 hnsRequestedDuration = minDuration;
274
275 // Set audio device format.
276 WAVEFORMATEX wfx;
277 WAVEFORMATEX *ptrWfx = &wfx;
278 WAVEFORMATEX *closestWfx = nullptr;
279 this->d->waveFormatFromCaps(&wfx, caps);
280
281 if (FAILED(this->d->m_pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
282 &wfx,
283 &closestWfx))) {
284 this->uninit();
285
286 return false;
287 }
288
289 if (closestWfx)
290 ptrWfx = closestWfx;
291
292 this->d->m_curCaps = this->d->capsFromWaveFormat(ptrWfx);
293
294 if (FAILED(hr = this->d->m_pAudioClient->Initialize(AUDCLNT_SHAREMODE_SHARED,
295 AUDCLNT_STREAMFLAGS_EVENTCALLBACK,
296 hnsRequestedDuration,
297 hnsRequestedDuration,
298 ptrWfx,
299 nullptr))) {
300
301 if (closestWfx)
302 CoTaskMemFree(closestWfx);
303
304 this->d->m_error = "Initialize: " + errorCodes->value(hr);
305 emit this->errorChanged(this->d->m_error);
306 this->uninit();
307
308 return false;
309 }
310
311 if (closestWfx)
312 CoTaskMemFree(closestWfx);
313
314 // Create an event handler for checking when an aundio frame is required
315 // for reading or writing.
316 this->d->m_hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
317
318 if (!this->d->m_hEvent) {
319 this->d->m_error = "CreateEvent: Error creating event handler";
320 emit this->errorChanged(this->d->m_error);
321 this->uninit();
322
323 return false;
324 }
325
326 // Set event handler.
327 if (FAILED(this->d->m_pAudioClient->SetEventHandle(this->d->m_hEvent))) {
328 this->d->m_error = "SetEventHandle: " + errorCodes->value(hr);
329 emit this->errorChanged(this->d->m_error);
330 this->uninit();
331
332 return false;
333 }
334
335 // Get audio capture/render client.
336 if (this->inputs().contains(device))
337 hr = this->d->m_pAudioClient->GetService(__uuidof(IAudioCaptureClient),
338 reinterpret_cast<void **>(&this->d->m_pCaptureClient));
339 else
340 hr = this->d->m_pAudioClient->GetService(__uuidof(IAudioRenderClient),
341 reinterpret_cast<void **>(&this->d->m_pRenderClient));
342
343 if (FAILED(hr)) {
344 this->d->m_error = "GetService: " + errorCodes->value(hr);
345 emit this->errorChanged(this->d->m_error);
346 this->uninit();
347
348 return false;
349 }
350
351 // Start audio client.
352 if (FAILED(hr = this->d->m_pAudioClient->Start())) {
353 this->d->m_error = "Start: " + errorCodes->value(hr);
354 emit this->errorChanged(this->d->m_error);
355 this->uninit();
356
357 return false;
358 }
359
360 this->d->m_curDevice = device;
361 this->d->m_samples = qMax(this->latency() * caps.rate() / 1000, 1);
362
363 return true;
364 }
365
read()366 QByteArray AudioDevWasapi::read()
367 {
368 int bufferSize = this->d->m_samples
369 * this->d->m_curCaps.bps()
370 * this->d->m_curCaps.channels()
371 / 8;
372
373 int nErrors = 0;
374
375 // Read audio samples until audio buffer is full.
376 while (this->d->m_audioBuffer.size() < bufferSize
377 && nErrors < MAX_ERRORS_READ_WRITE) {
378 // Wait until an audio frame can be read.
379 if (WaitForSingleObject(this->d->m_hEvent, EVENT_TIMEOUT) != WAIT_OBJECT_0) {
380 nErrors++;
381
382 continue;
383 }
384
385 HRESULT hr;
386 UINT32 samplesCount = 0;
387
388 // Get the size in samples of the captured audio frame.
389 if (FAILED(hr = this->d->m_pCaptureClient->GetNextPacketSize(&samplesCount))) {
390 this->d->m_error = "GetNextPacketSize: " + errorCodes->value(hr);
391 emit this->errorChanged(this->d->m_error);
392
393 return QByteArray();
394 }
395
396 // Check if empty.
397 if (samplesCount < 1)
398 continue;
399
400 BYTE *pData = nullptr;
401 DWORD flags = 0;
402
403 // Read audio buffer.
404 if (FAILED(hr = this->d->m_pCaptureClient->GetBuffer(&pData,
405 &samplesCount,
406 &flags,
407 nullptr,
408 nullptr))) {
409 this->d->m_error = "GetBuffer: " + errorCodes->value(hr);
410 emit this->errorChanged(this->d->m_error);
411
412 return QByteArray();
413 }
414
415 size_t bufferSize =
416 samplesCount
417 * size_t(this->d->m_curCaps.bps()
418 * this->d->m_curCaps.channels())
419 / 8;
420
421 // This flag means we must ignore the incoming buffer and write zeros
422 // to it.
423 if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
424 pData = new BYTE[bufferSize];
425 memset(pData, 0, bufferSize);
426 }
427
428 // Copy audio frame to the audio buffer.
429 QByteArray buffer(reinterpret_cast<const char *>(pData), int(bufferSize));
430
431 if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
432 delete [] pData;
433
434 this->d->m_audioBuffer.append(buffer);
435
436 // Remove read samples from the audio device.
437 if (FAILED(hr = this->d->m_pCaptureClient->ReleaseBuffer(samplesCount))) {
438 this->d->m_error = "ReleaseBuffer: " + errorCodes->value(hr);
439 emit this->errorChanged(this->d->m_error);
440
441 return QByteArray();
442 }
443 }
444
445 // In case of error and if the buffer is empty, return.
446 if (this->d->m_audioBuffer.isEmpty())
447 return QByteArray();
448
449 QByteArray buffer = this->d->m_audioBuffer.mid(0, bufferSize);
450 this->d->m_audioBuffer.remove(0, bufferSize);
451
452 return buffer;
453 }
454
write(const AkAudioPacket & packet)455 bool AudioDevWasapi::write(const AkAudioPacket &packet)
456 {
457 this->d->m_audioBuffer.append(packet.buffer());
458 int nErrors = 0;
459
460 while (!this->d->m_audioBuffer.isEmpty()
461 && nErrors < MAX_ERRORS_READ_WRITE) {
462 // Wait until an audio frame can be writen.
463 if (WaitForSingleObject(this->d->m_hEvent, EVENT_TIMEOUT) != WAIT_OBJECT_0) {
464 nErrors++;
465
466 continue;
467 }
468
469 HRESULT hr;
470 UINT32 samplesCount;
471
472 // Get audio buffer size in samples.
473 if (FAILED(hr = this->d->m_pAudioClient->GetBufferSize(&samplesCount))) {
474 this->d->m_error = "GetBufferSize: " + errorCodes->value(hr);
475 emit this->errorChanged(this->d->m_error);
476
477 return false;
478 }
479
480 UINT32 numSamplesPadding;
481
482 // Get the number of samples already present in the audio buffer.
483 if (FAILED(hr = this->d->m_pAudioClient->GetCurrentPadding(&numSamplesPadding))) {
484 this->d->m_error = "GetCurrentPadding: " + errorCodes->value(hr);
485 emit this->errorChanged(this->d->m_error);
486
487 return false;
488 }
489
490 // Calculate the difference. This is the number of samples we can write
491 // to the audio buffer.
492 UINT32 availableSamples = samplesCount - numSamplesPadding;
493
494 // This is probably an impossible but well... check it.
495 if (availableSamples < 1)
496 continue;
497
498 // Check how many samples we can write to the audio buffer.
499 UINT32 samplesInBuffer = UINT32(this->d->m_audioBuffer.size()
500 * 8
501 / this->d->m_curCaps.bps()
502 / this->d->m_curCaps.channels());
503 UINT32 samplesToWrite = qMin(availableSamples, samplesInBuffer);
504
505 BYTE *pData = nullptr;
506
507 // Get the audio buffer.
508 if (FAILED(hr = this->d->m_pRenderClient->GetBuffer(samplesToWrite, &pData))) {
509 this->d->m_error = "GetBuffer: " + errorCodes->value(hr);
510 emit this->errorChanged(this->d->m_error);
511
512 return false;
513 }
514
515 // Copy the maximum number of audio samples we can write to the audio
516 // buffer.
517 size_t bufferSize =
518 samplesToWrite
519 * size_t(this->d->m_curCaps.bps()
520 * this->d->m_curCaps.channels())
521 / 8;
522
523 memcpy(pData, this->d->m_audioBuffer.constData(), bufferSize);
524 this->d->m_audioBuffer.remove(0, int(bufferSize));
525
526 // Tell audio device how many samples we had written.
527 if (FAILED(hr = this->d->m_pRenderClient->ReleaseBuffer(samplesToWrite, 0))) {
528 this->d->m_error = "ReleaseBuffer: " + errorCodes->value(hr);
529 emit this->errorChanged(this->d->m_error);
530
531 return false;
532 }
533 }
534
535 return true;
536 }
537
uninit()538 bool AudioDevWasapi::uninit()
539 {
540 bool ok = true;
541 HRESULT hr;
542
543 // Stop audio device.
544 if (this->d->m_pAudioClient && FAILED(hr = this->d->m_pAudioClient->Stop())) {
545 this->d->m_error = "Stop: " + errorCodes->value(hr);
546 emit this->errorChanged(this->d->m_error);
547 ok = false;
548 }
549
550 // Release interfaces.
551 if (this->d->m_pCaptureClient) {
552 this->d->m_pCaptureClient->Release();
553 this->d->m_pCaptureClient = nullptr;
554 }
555
556 if (this->d->m_pRenderClient) {
557 this->d->m_pRenderClient->Release();
558 this->d->m_pRenderClient = nullptr;
559 }
560
561 if (this->d->m_pAudioClient) {
562 this->d->m_pAudioClient->Release();
563 this->d->m_pAudioClient = nullptr;
564 }
565
566 if (this->d->m_pDevice) {
567 this->d->m_pDevice->Release();
568 this->d->m_pDevice = nullptr;
569 }
570
571 if (this->d->m_hEvent) {
572 CloseHandle(this->d->m_hEvent);
573 this->d->m_hEvent = nullptr;
574 }
575
576 this->d->m_curDevice.clear();
577
578 return ok;
579 }
580
QueryInterface(const IID & riid,void ** ppvObject)581 HRESULT AudioDevWasapi::QueryInterface(const IID &riid, void **ppvObject)
582 {
583 if (riid == __uuidof(IUnknown)
584 || riid == __uuidof(IMMNotificationClient))
585 *ppvObject = static_cast<IMMNotificationClient *>(this);
586 else {
587 *ppvObject = nullptr;
588
589 return E_NOINTERFACE;
590 }
591
592 this->AddRef();
593
594 return S_OK;
595 }
596
AddRef()597 ULONG AudioDevWasapi::AddRef()
598 {
599 return InterlockedIncrement(&this->d->m_cRef);
600 }
601
Release()602 ULONG AudioDevWasapi::Release()
603 {
604 ULONG lRef = InterlockedDecrement(&this->d->m_cRef);
605
606 if (lRef == 0)
607 delete this;
608
609 return lRef;
610 }
611
AudioDevWasapiPrivate(AudioDevWasapi * self)612 AudioDevWasapiPrivate::AudioDevWasapiPrivate(AudioDevWasapi *self):
613 self(self)
614 {
615 }
616
waveFormatFromCaps(WAVEFORMATEX * wfx,const AkAudioCaps & caps) const617 bool AudioDevWasapiPrivate::waveFormatFromCaps(WAVEFORMATEX *wfx,
618 const AkAudioCaps &caps) const
619 {
620 if (!wfx)
621 return false;
622
623 wfx->wFormatTag = caps.format() == AkAudioCaps::SampleFormat_flt?
624 WAVE_FORMAT_IEEE_FLOAT: WAVE_FORMAT_PCM;
625 wfx->nChannels = WORD(caps.channels());
626 wfx->nSamplesPerSec = DWORD(caps.rate());
627 wfx->wBitsPerSample = WORD(caps.bps());
628 wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8;
629 wfx->nAvgBytesPerSec = wfx->nSamplesPerSec * wfx->nBlockAlign;
630 wfx->cbSize = 0;
631
632 return true;
633 }
634
capsFromWaveFormat(WAVEFORMATEX * wfx) const635 AkAudioCaps AudioDevWasapiPrivate::capsFromWaveFormat(WAVEFORMATEX *wfx) const
636 {
637 if (!wfx)
638 return AkAudioCaps();
639
640 auto sampleType =
641 wfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT?
642 AkAudioCaps::SampleType_float:
643 AkAudioCaps::SampleType_int;
644 auto sampleFormat =
645 AkAudioCaps::sampleFormatFromProperties(sampleType,
646 int(wfx->wBitsPerSample),
647 Q_BYTE_ORDER);
648
649 return AkAudioCaps(sampleFormat,
650 AkAudioCaps::defaultChannelLayout(int(wfx->nChannels)),
651 int(wfx->nSamplesPerSec));
652 }
653
fillDeviceInfo(const QString & device,QList<AkAudioCaps::SampleFormat> * supportedFormats,QList<AkAudioCaps::ChannelLayout> * supportedLayouts,QList<int> * supportedSampleRates) const654 void AudioDevWasapiPrivate::fillDeviceInfo(const QString &device,
655 QList<AkAudioCaps::SampleFormat> *supportedFormats,
656 QList<AkAudioCaps::ChannelLayout> *supportedLayouts,
657 QList<int> *supportedSampleRates) const
658 {
659 if (!this->m_deviceEnumerator)
660 return;
661
662 IMMDevice *pDevice = nullptr;
663 IAudioClient *pAudioClient = nullptr;
664
665 // Test if the device is already running,
666 if (this->m_curDevice != device) {
667 // Get audio device.
668 if (FAILED(this->m_deviceEnumerator->GetDevice(device.toStdWString().c_str(),
669 &pDevice)))
670 return;
671
672 // Get an instance for the audio client.
673 if (FAILED(pDevice->Activate(__uuidof(IAudioClient),
674 CLSCTX_ALL,
675 nullptr,
676 reinterpret_cast<void **>(&pAudioClient)))) {
677 pDevice->Release();
678
679 return;
680 }
681 } else {
682 pDevice = this->m_pDevice;
683 pAudioClient = this->m_pAudioClient;
684 }
685
686 static const QVector<AkAudioCaps::SampleFormat> preferredFormats {
687 AkAudioCaps::SampleFormat_flt,
688 AkAudioCaps::SampleFormat_s32,
689 AkAudioCaps::SampleFormat_s16,
690 AkAudioCaps::SampleFormat_u8,
691 };
692
693 for (auto &format: preferredFormats)
694 for (int channels = 1; channels < 3; channels++)
695 for (auto &rate: self->commonSampleRates()) {
696 WAVEFORMATEX wfx;
697 WAVEFORMATEX *closestWfx = nullptr;
698 AkAudioCaps audioCaps(format,
699 AkAudioCaps::defaultChannelLayout(channels),
700 rate);
701 this->waveFormatFromCaps(&wfx, audioCaps);
702
703 if (SUCCEEDED(pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
704 &wfx,
705 &closestWfx))) {
706 AkAudioCaps::SampleFormat sampleFormat;
707 AkAudioCaps::ChannelLayout layout;
708 int sampleRate;
709
710 if (closestWfx) {
711 auto sampleType =
712 closestWfx->wFormatTag == WAVE_FORMAT_IEEE_FLOAT?
713 AkAudioCaps::SampleType_float:
714 AkAudioCaps::SampleType_int;
715 sampleFormat =
716 AkAudioCaps::sampleFormatFromProperties(sampleType,
717 int(closestWfx->wBitsPerSample),
718 Q_BYTE_ORDER);
719 layout = AkAudioCaps::defaultChannelLayout(int(closestWfx->nChannels));
720 sampleRate = int(closestWfx->nSamplesPerSec);
721 CoTaskMemFree(closestWfx);
722 } else {
723 sampleFormat = format;
724 layout = AkAudioCaps::defaultChannelLayout(channels);
725 sampleRate = rate;
726 }
727
728 if (!supportedFormats->contains(sampleFormat))
729 supportedFormats->append(sampleFormat);
730
731 if (layout != AkAudioCaps::Layout_none
732 && !supportedLayouts->contains(layout))
733 supportedLayouts->append(layout);
734
735 if (!supportedSampleRates->contains(sampleRate))
736 supportedSampleRates->append(sampleRate);
737 }
738 }
739
740 if (this->m_curDevice != device) {
741 pAudioClient->Release();
742 pDevice->Release();
743 }
744
745 std::sort(supportedFormats->begin(), supportedFormats->end());
746 }
747
preferredCaps(const QString & device,EDataFlow dataFlow) const748 AkAudioCaps AudioDevWasapiPrivate::preferredCaps(const QString &device,
749 EDataFlow dataFlow) const
750 {
751 if (!this->m_deviceEnumerator)
752 return AkAudioCaps();
753
754 IMMDevice *pDevice = nullptr;
755 IAudioClient *pAudioClient = nullptr;
756
757 // Test if the device is already running,
758 if (this->m_curDevice != device) {
759 // Get audio device.
760 if (FAILED(this->m_deviceEnumerator->GetDevice(device.toStdWString().c_str(),
761 &pDevice)))
762 return AkAudioCaps();
763
764 // Get an instance for the audio client.
765 if (FAILED(pDevice->Activate(__uuidof(IAudioClient),
766 CLSCTX_ALL,
767 nullptr,
768 reinterpret_cast<void **>(&pAudioClient)))) {
769 pDevice->Release();
770
771 return AkAudioCaps();
772 }
773 } else {
774 pDevice = this->m_pDevice;
775 pAudioClient = this->m_pAudioClient;
776 }
777
778 AkAudioCaps caps = dataFlow == eCapture?
779 AkAudioCaps(AkAudioCaps::SampleFormat_u8,
780 AkAudioCaps::Layout_mono,
781 8000):
782 AkAudioCaps(AkAudioCaps::SampleFormat_s16,
783 AkAudioCaps::Layout_stereo,
784 44100);
785
786 WAVEFORMATEX wfx;
787 WAVEFORMATEX *closestWfx = nullptr;
788 this->waveFormatFromCaps(&wfx, caps);
789
790 if (SUCCEEDED(pAudioClient->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED,
791 &wfx,
792 &closestWfx))) {
793 if (closestWfx) {
794 caps = this->capsFromWaveFormat(closestWfx);
795 CoTaskMemFree(closestWfx);
796 }
797 }
798
799 if (this->m_curDevice != device) {
800 pAudioClient->Release();
801 pDevice->Release();
802 }
803
804 return caps;
805 }
806
OnDeviceStateChanged(LPCWSTR pwstrDeviceId,DWORD dwNewState)807 HRESULT AudioDevWasapi::OnDeviceStateChanged(LPCWSTR pwstrDeviceId,
808 DWORD dwNewState)
809 {
810 Q_UNUSED(pwstrDeviceId)
811 Q_UNUSED(dwNewState)
812
813 return S_OK;
814 }
815
OnDeviceAdded(LPCWSTR pwstrDeviceId)816 HRESULT AudioDevWasapi::OnDeviceAdded(LPCWSTR pwstrDeviceId)
817 {
818 Q_UNUSED(pwstrDeviceId)
819
820 // Device was installed
821
822 return S_OK;
823 }
824
OnDeviceRemoved(LPCWSTR pwstrDeviceId)825 HRESULT AudioDevWasapi::OnDeviceRemoved(LPCWSTR pwstrDeviceId)
826 {
827 Q_UNUSED(pwstrDeviceId)
828
829 // Device was uninstalled
830
831 return S_OK;
832 }
833
OnDefaultDeviceChanged(EDataFlow flow,ERole role,LPCWSTR pwstrDeviceId)834 HRESULT AudioDevWasapi::OnDefaultDeviceChanged(EDataFlow flow,
835 ERole role,
836 LPCWSTR pwstrDeviceId)
837 {
838 if (role != eMultimedia)
839 return S_OK;
840
841 QString deviceId = QString::fromWCharArray(pwstrDeviceId);
842
843 if (flow == eCapture) {
844 this->d->m_defaultSource = deviceId;
845 emit this->defaultInputChanged(deviceId);
846 } else if (flow == eRender) {
847 this->d->m_defaultSink = deviceId;
848 emit this->defaultOutputChanged(deviceId);
849 }
850
851 return S_OK;
852 }
853
OnPropertyValueChanged(LPCWSTR pwstrDeviceId,const PROPERTYKEY key)854 HRESULT AudioDevWasapi::OnPropertyValueChanged(LPCWSTR pwstrDeviceId,
855 const PROPERTYKEY key)
856 {
857 Q_UNUSED(pwstrDeviceId)
858 Q_UNUSED(key)
859
860 this->updateDevices();
861
862 return S_OK;
863 }
864
updateDevices()865 void AudioDevWasapi::updateDevices()
866 {
867 if (!this->d->m_deviceEnumerator) {
868 this->d->m_error = "Device enumerator not created.";
869 emit this->errorChanged(this->d->m_error);
870
871 return;
872 }
873
874 decltype(this->d->m_sources) inputs;
875 decltype(this->d->m_sinks) outputs;
876 decltype(this->d->m_defaultSink) defaultSink;
877 decltype(this->d->m_defaultSource) defaultSource;
878 decltype(this->d->m_descriptionMap) descriptionMap;
879 decltype(this->d->m_supportedFormats) supportedFormats;
880 decltype(this->d->m_supportedLayouts) supportedChannels;
881 decltype(this->d->m_supportedSampleRates) supportedSampleRates;
882 decltype(this->d->m_preferredInputCaps) preferredInputCaps;
883 decltype(this->d->m_preferredOutputCaps) preferredOutputCaps;
884
885 for (auto &dataFlow: QVector<EDataFlow> {eCapture, eRender}) {
886 HRESULT hr;
887 IMMDevice *defaultDevice = nullptr;
888
889 if (SUCCEEDED(hr = this->d->m_deviceEnumerator->GetDefaultAudioEndpoint(dataFlow,
890 eMultimedia,
891 &defaultDevice))) {
892 LPWSTR deviceId;
893
894 if (SUCCEEDED(hr = defaultDevice->GetId(&deviceId))) {
895 if (dataFlow == eCapture)
896 defaultSource = QString::fromWCharArray(deviceId);
897 else
898 defaultSink = QString::fromWCharArray(deviceId);
899
900 CoTaskMemFree(deviceId);
901 }
902
903 defaultDevice->Release();
904 }
905
906 IMMDeviceCollection *endPoints = nullptr;
907
908 if (SUCCEEDED(hr = this->d->m_deviceEnumerator->EnumAudioEndpoints(dataFlow,
909 eMultimedia,
910 &endPoints))) {
911 UINT nDevices = 0;
912
913 if (SUCCEEDED(hr = endPoints->GetCount(&nDevices)))
914 for (UINT i = 0; i < nDevices; i++) {
915 IMMDevice *device = nullptr;
916
917 if (SUCCEEDED(endPoints->Item(i, &device))) {
918 LPWSTR deviceId;
919
920 if (SUCCEEDED(hr = device->GetId(&deviceId))) {
921 IPropertyStore *properties = nullptr;
922
923 if (SUCCEEDED(hr = device->OpenPropertyStore(STGM_READ, &properties))) {
924 PROPVARIANT friendlyName;
925 PropVariantInit(&friendlyName);
926
927 if (SUCCEEDED(hr = properties->GetValue(PKEY_Device_FriendlyName,
928 &friendlyName))) {
929 auto devId = QString::fromWCharArray(deviceId);
930
931 QList<AkAudioCaps::SampleFormat> _supportedFormats;
932 QList<AkAudioCaps::ChannelLayout> _supportedLayouts;
933 QList<int> _supportedSampleRates;
934 this->d->fillDeviceInfo(devId,
935 &_supportedFormats,
936 &_supportedLayouts,
937 &_supportedSampleRates);
938
939 if (_supportedFormats.isEmpty())
940 _supportedFormats =
941 this->d->m_supportedFormats.value(devId);
942
943 if (_supportedLayouts.isEmpty())
944 _supportedLayouts =
945 this->d->m_supportedLayouts.value(devId);
946
947 if (_supportedSampleRates.isEmpty())
948 _supportedSampleRates =
949 this->d->m_supportedSampleRates.value(devId);
950
951 if (!_supportedFormats.isEmpty()
952 && !_supportedLayouts.isEmpty()
953 && !_supportedSampleRates.isEmpty()) {
954 if (dataFlow == eCapture) {
955 inputs << devId;
956 preferredInputCaps[devId] =
957 this->d->preferredCaps(devId,
958 dataFlow);
959 } else {
960 outputs << devId;
961 preferredOutputCaps[devId] =
962 this->d->preferredCaps(devId,
963 dataFlow);
964 }
965
966 descriptionMap[devId] =
967 QString::fromWCharArray(friendlyName.pwszVal);
968 supportedFormats[devId] = _supportedFormats;
969 supportedChannels[devId] = _supportedLayouts;
970 supportedSampleRates[devId] = _supportedSampleRates;
971 }
972
973 PropVariantClear(&friendlyName);
974 }
975
976 properties->Release();
977 }
978
979 CoTaskMemFree(deviceId);
980 }
981
982 device->Release();
983 }
984 }
985
986 endPoints->Release();
987 }
988 }
989
990 if (this->d->m_supportedFormats != supportedFormats)
991 this->d->m_supportedFormats = supportedFormats;
992
993 if (this->d->m_supportedLayouts != supportedChannels)
994 this->d->m_supportedLayouts = supportedChannels;
995
996 if (this->d->m_supportedSampleRates != supportedSampleRates)
997 this->d->m_supportedSampleRates = supportedSampleRates;
998
999 if (this->d->m_descriptionMap != descriptionMap)
1000 this->d->m_descriptionMap = descriptionMap;
1001
1002 this->d->m_preferredInputCaps = preferredInputCaps;
1003 this->d->m_preferredOutputCaps = preferredOutputCaps;
1004
1005 if (this->d->m_sources != inputs) {
1006 this->d->m_sources = inputs;
1007 emit this->inputsChanged(inputs);
1008 }
1009
1010 if (this->d->m_sinks != outputs) {
1011 this->d->m_sinks = outputs;
1012 emit this->outputsChanged(outputs);
1013 }
1014
1015 if (defaultSource.isEmpty() && !inputs.isEmpty())
1016 defaultSource = inputs.first();
1017
1018 if (defaultSink.isEmpty() && !outputs.isEmpty())
1019 defaultSink = outputs.first();
1020
1021 if (this->d->m_defaultSource != defaultSource) {
1022 this->d->m_defaultSource = defaultSource;
1023 emit this->defaultInputChanged(defaultSource);
1024 }
1025
1026 if (this->d->m_defaultSink != defaultSink) {
1027 this->d->m_defaultSink = defaultSink;
1028 emit this->defaultOutputChanged(defaultSink);
1029 }
1030 }
1031
1032 #include "moc_audiodevwasapi.cpp"
1033