From 3f57e5eb4d9cc145915948b2c91860c994cac344 Mon Sep 17 00:00:00 2001 From: Zach Pomerantz Date: Tue, 27 Jun 2017 14:04:52 -0400 Subject: [PATCH] implement peakValueListChanged --- libraries/audio-client/src/AudioClient.cpp | 10 +- libraries/audio-client/src/AudioClient.h | 1 + .../audio-client/src/AudioPeakValues.cpp | 164 +++++++++++++++++- 3 files changed, 169 insertions(+), 6 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 7a1d8edccd..7f872443be 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -77,7 +77,7 @@ Setting::Handle staticJitterBufferFrames("staticJitterBufferFrames", // protect the Qt internal device list using Mutex = std::mutex; using Lock = std::unique_lock; -static Mutex _deviceMutex; +Mutex _deviceMutex; // thread-safe QList getAvailableDevices(QAudio::Mode mode) { @@ -235,7 +235,7 @@ AudioClient::AudioClient() : QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkPeakValues(); }); }); const unsigned long PEAK_VALUES_CHECK_INTERVAL_SECS = 50; - + _checkPeakValuesTimer->start(PEAK_VALUES_CHECK_INTERVAL_SECS); configureReverb(); @@ -308,8 +308,6 @@ QString getWinDeviceName(IMMDevice* pEndpoint) { QString deviceName; IPropertyStore* pPropertyStore; pEndpoint->OpenPropertyStore(STGM_READ, &pPropertyStore); - pEndpoint->Release(); - pEndpoint = nullptr; PROPVARIANT pv; PropVariantInit(&pv); HRESULT hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv); @@ -338,6 +336,8 @@ QString AudioClient::getWinDeviceName(wchar_t* guid) { deviceName = QString("NONE"); } else { deviceName = ::getWinDeviceName(pEndpoint); + pEndpoint->Release(); + pEndpoint = nullptr; } pMMDeviceEnumerator->Release(); pMMDeviceEnumerator = nullptr; @@ -421,6 +421,8 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { deviceName = QString("NONE"); } else { deviceName = getWinDeviceName(pEndpoint); + pEndpoint->Release(); + pEndpoint = nullptr; } pMMDeviceEnumerator->Release(); pMMDeviceEnumerator = NULL; diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 79e82526c6..63b1398d09 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -149,6 +149,7 @@ public: QList getAudioDevices(QAudio::Mode mode) const; void enablePeakValues(bool enable) { _enablePeakValues = enable; } + bool peakValuesAvailable() const; static const float CALLBACK_ACCELERATOR_RATIO; diff --git a/libraries/audio-client/src/AudioPeakValues.cpp b/libraries/audio-client/src/AudioPeakValues.cpp index 021f23b907..3df469b830 100644 --- a/libraries/audio-client/src/AudioPeakValues.cpp +++ b/libraries/audio-client/src/AudioPeakValues.cpp @@ -11,6 +11,166 @@ #include "AudioClient.h" -void AudioClient::checkPeakValues() { - // TODO +#ifdef Q_OS_WIN + +#include +#include +#include +#include + +#include + +#define RETURN_ON_FAIL(result) if (FAILED(result)) { return; } +#define CONTINUE_ON_FAIL(result) if (FAILED(result)) { continue; } + +extern QString getWinDeviceName(IMMDevice* pEndpoint); +extern std::mutex _deviceMutex; + +std::map> activeClients; + +template +void release(T* t) { + t->Release(); } + +template <> +void release(IAudioClient* audioClient) { + audioClient->Stop(); + audioClient->Release(); +} + +void AudioClient::checkPeakValues() { + // prepare the windows environment + CoInitialize(NULL); + + // if disabled, clean up active clients + if (!_enablePeakValues) { + activeClients.clear(); + return; + } + + // lock the devices so the _inputDevices list is static + std::unique_lock lock(_deviceMutex); + HRESULT result; + + // initialize the payload + QList peakValueList; + for (int i = 0; i < _inputDevices.size(); ++i) { + peakValueList.push_back(0.0f); + } + + std::shared_ptr enumerator; + { + IMMDeviceEnumerator* pEnumerator; + result = CoCreateInstance( + __uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, + __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator); + RETURN_ON_FAIL(result); + enumerator = std::shared_ptr(pEnumerator, &release); + } + + std::shared_ptr endpoints; + { + IMMDeviceCollection* pEndpoints; + result = enumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &pEndpoints); + RETURN_ON_FAIL(result); + endpoints = std::shared_ptr(pEndpoints, &release); + } + + UINT count; + { + result = endpoints->GetCount(&count); + RETURN_ON_FAIL(result); + } + + IMMDevice* pDevice; + std::shared_ptr device; + IAudioMeterInformation* pMeterInfo; + std::shared_ptr meterInfo; + IAudioClient* pAudioClient; + std::shared_ptr audioClient; + DWORD hardwareSupport; + LPWSTR pDeviceId = NULL; + LPWAVEFORMATEX format; + float peakValue; + QString deviceName; + int deviceIndex; + for (UINT i = 0; i < count; ++i) { + result = endpoints->Item(i, &pDevice); + CONTINUE_ON_FAIL(result); + device = std::shared_ptr(pDevice, &release); + + // if the device isn't listed through Qt, skip it + deviceName = ::getWinDeviceName(pDevice); + deviceIndex = 0; + for (; deviceIndex < _inputDevices.size(); ++deviceIndex) { + if (deviceName == _inputDevices[deviceIndex].deviceName()) { + break; + } + } + if (deviceIndex >= _inputDevices.size()) { + continue; + } + + //continue; + + result = device->Activate(__uuidof(IAudioMeterInformation), CLSCTX_ALL, NULL, (void**)&pMeterInfo); + CONTINUE_ON_FAIL(result); + meterInfo = std::shared_ptr(pMeterInfo, &release); + + //continue; + + hardwareSupport; + result = meterInfo->QueryHardwareSupport(&hardwareSupport); + CONTINUE_ON_FAIL(result); + + //continue; + + // if the device has no hardware support (USB)... + if (!(hardwareSupport & ENDPOINT_HARDWARE_SUPPORT_METER)) { + result = device->GetId(&pDeviceId); + CONTINUE_ON_FAIL(result); + std::wstring deviceId(pDeviceId); + CoTaskMemFree(pDeviceId); + + //continue; + + // ...and no active client... + if (activeClients.find(deviceId) == activeClients.end()) { + result = device->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient); + CONTINUE_ON_FAIL(result); + audioClient = std::shared_ptr(pAudioClient, &release); + + //continue; + + // ...activate a client + audioClient->GetMixFormat(&format); + audioClient->Initialize(AUDCLNT_SHAREMODE_SHARED, 0, 0, 0, format, NULL); + audioClient->Start(); + + //continue; + + activeClients[deviceId] = audioClient; + } + } + + // get the peak value and put it in the payload + meterInfo->GetPeakValue(&peakValue); + peakValueList[deviceIndex] = peakValue; + } + + emit peakValueListChanged(peakValueList); +} + +bool AudioClient::peakValuesAvailable() const { + return true; +} + +#else +void AudioClient::checkPeakValues() { +} + +bool AudioClient::peakValuesAvailable() const { + return false; +} +#endif \ No newline at end of file