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