From b46219241ce428b82e0252081b00af69969308be Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zmp@umich.edu>
Date: Mon, 26 Jun 2017 22:44:31 -0400
Subject: [PATCH 01/10] add peakValueListChanged scaffolding

---
 libraries/audio-client/src/AudioClient.cpp     | 12 +++++++++---
 libraries/audio-client/src/AudioClient.h       | 10 +++++++++-
 libraries/audio-client/src/AudioPeakValues.cpp | 16 ++++++++++++++++
 3 files changed, 34 insertions(+), 4 deletions(-)
 create mode 100644 libraries/audio-client/src/AudioPeakValues.cpp

diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp
index fc54a04a5e..7a1d8edccd 100644
--- a/libraries/audio-client/src/AudioClient.cpp
+++ b/libraries/audio-client/src/AudioClient.cpp
@@ -224,13 +224,18 @@ AudioClient::AudioClient() :
     // start a thread to detect any device changes
     _checkDevicesTimer = new QTimer(this);
     connect(_checkDevicesTimer, &QTimer::timeout, [this] {
-        QtConcurrent::run(QThreadPool::globalInstance(), [this] {
-            checkDevices();
-        });
+        QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkDevices(); });
     });
     const unsigned long DEVICE_CHECK_INTERVAL_MSECS = 2 * 1000;
     _checkDevicesTimer->start(DEVICE_CHECK_INTERVAL_MSECS);
 
+    // start a thread to detect peak value changes
+    _checkPeakValuesTimer = new QTimer(this);
+    connect(_checkPeakValuesTimer, &QTimer::timeout, [this] {
+        QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkPeakValues(); });
+    });
+    const unsigned long PEAK_VALUES_CHECK_INTERVAL_SECS = 50;
+
 
     configureReverb();
 
@@ -262,6 +267,7 @@ void AudioClient::cleanupBeforeQuit() {
     stop();
 
     _checkDevicesTimer->stop();
+    _checkPeakValuesTimer->stop();
 }
 
 void AudioClient::handleMismatchAudioFormat(SharedNodePointer node, const QString& currentCodec, const QString& recievedCodec) {
diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h
index 54ce3aa6c2..79e82526c6 100644
--- a/libraries/audio-client/src/AudioClient.h
+++ b/libraries/audio-client/src/AudioClient.h
@@ -148,6 +148,8 @@ public:
     QAudioDeviceInfo getActiveAudioDevice(QAudio::Mode mode) const;
     QList<QAudioDeviceInfo> getAudioDevices(QAudio::Mode mode) const;
 
+    void enablePeakValues(bool enable) { _enablePeakValues = enable; }
+
     static const float CALLBACK_ACCELERATOR_RATIO;
 
     bool getNamedAudioDeviceForModeExists(QAudio::Mode mode, const QString& deviceName);
@@ -220,6 +222,7 @@ signals:
 
     void deviceChanged(QAudio::Mode mode, const QAudioDeviceInfo& device);
     void devicesChanged(QAudio::Mode mode, const QList<QAudioDeviceInfo>& devices);
+    void peakValueListChanged(const QList<float> peakValueList);
 
     void receivedFirstPacket();
     void disconnected();
@@ -238,9 +241,12 @@ private:
     friend class CheckDevicesThread;
     friend class LocalInjectorsThread;
 
+    // background tasks
+    void checkDevices();
+    void checkPeakValues();
+
     void outputFormatChanged();
     void handleAudioInput(QByteArray& audioBuffer);
-    void checkDevices();
     void prepareLocalAudioInjectors(std::unique_ptr<Lock> localAudioLock = nullptr);
     bool mixLocalAudioInjectors(float* mixBuffer);
     float azimuthForSource(const glm::vec3& relativePosition);
@@ -293,6 +299,7 @@ private:
     std::atomic<bool> _localInjectorsAvailable { false };
     MixedProcessedAudioStream _receivedAudioStream;
     bool _isStereoInput;
+    std::atomic<bool> _enablePeakValues { false };
 
     quint64 _outputStarveDetectionStartTimeMsec;
     int _outputStarveDetectionCount;
@@ -391,6 +398,7 @@ private:
     RateCounter<> _audioInbound;
 
     QTimer* _checkDevicesTimer { nullptr };
+    QTimer* _checkPeakValuesTimer { nullptr };
 };
 
 
diff --git a/libraries/audio-client/src/AudioPeakValues.cpp b/libraries/audio-client/src/AudioPeakValues.cpp
new file mode 100644
index 0000000000..021f23b907
--- /dev/null
+++ b/libraries/audio-client/src/AudioPeakValues.cpp
@@ -0,0 +1,16 @@
+//
+//  AudioPeakValues.cpp
+//  interface/src
+//
+//  Created by Zach Pomerantz on 6/26/2017
+//  Copyright 2013 High Fidelity, Inc.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include "AudioClient.h"
+
+void AudioClient::checkPeakValues() {
+    // TODO
+}

From 3f57e5eb4d9cc145915948b2c91860c994cac344 Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zmp@umich.edu>
Date: Tue, 27 Jun 2017 14:04:52 -0400
Subject: [PATCH 02/10] 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<int> staticJitterBufferFrames("staticJitterBufferFrames",
 // protect the Qt internal device list
 using Mutex = std::mutex;
 using Lock = std::unique_lock<Mutex>;
-static Mutex _deviceMutex;
+Mutex _deviceMutex;
 
 // thread-safe
 QList<QAudioDeviceInfo> 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<QAudioDeviceInfo> 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 <windows.h>
+#include <mmdeviceapi.h>
+#include <endpointvolume.h>
+#include <audioclient.h>
+
+#include <QString>
+
+#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<std::wstring, std::shared_ptr<IAudioClient>> activeClients;
+
+template <class T>
+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<std::mutex> lock(_deviceMutex);
+    HRESULT result;
+
+    // initialize the payload
+    QList<float> peakValueList;
+    for (int i = 0; i < _inputDevices.size(); ++i) {
+        peakValueList.push_back(0.0f);
+    }
+
+    std::shared_ptr<IMMDeviceEnumerator> enumerator;
+    {
+        IMMDeviceEnumerator* pEnumerator;
+        result = CoCreateInstance(
+            __uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER,
+            __uuidof(IMMDeviceEnumerator), (void**)&pEnumerator);
+        RETURN_ON_FAIL(result);
+        enumerator = std::shared_ptr<IMMDeviceEnumerator>(pEnumerator, &release<IMMDeviceEnumerator>);
+    }
+
+    std::shared_ptr<IMMDeviceCollection> endpoints;
+    {
+        IMMDeviceCollection* pEndpoints;
+        result = enumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE, &pEndpoints);
+        RETURN_ON_FAIL(result);
+        endpoints = std::shared_ptr<IMMDeviceCollection>(pEndpoints, &release<IMMDeviceCollection>);
+    }
+
+    UINT count;
+    {
+        result = endpoints->GetCount(&count);
+        RETURN_ON_FAIL(result);
+    }
+
+    IMMDevice* pDevice;
+    std::shared_ptr<IMMDevice> device;
+    IAudioMeterInformation* pMeterInfo;
+    std::shared_ptr<IAudioMeterInformation> meterInfo;
+    IAudioClient* pAudioClient;
+    std::shared_ptr<IAudioClient> 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<IMMDevice>(pDevice, &release<IMMDevice>);
+
+        // 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<IAudioMeterInformation>(pMeterInfo, &release<IAudioMeterInformation>);
+
+        //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<IAudioClient>(pAudioClient, &release<IAudioClient>);
+
+                //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

From 5353437f5915b9c88ab276a81dfdd95276a8df8d Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zmp@umich.edu>
Date: Tue, 27 Jun 2017 17:37:13 -0400
Subject: [PATCH 03/10] add peakValues to scripting API

---
 interface/src/scripting/AudioDevices.cpp | 82 ++++++++++++++++++------
 interface/src/scripting/AudioDevices.h   | 63 +++++++++++++++---
 2 files changed, 118 insertions(+), 27 deletions(-)

diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp
index b0ea8226e8..1c31d7aa5a 100644
--- a/interface/src/scripting/AudioDevices.cpp
+++ b/interface/src/scripting/AudioDevices.cpp
@@ -34,28 +34,44 @@ Setting::Handle<QString>& getSetting(bool contextIsHMD, QAudio::Mode mode) {
     }
 }
 
-QHash<int, QByteArray> AudioDeviceList::_roles {
-    { Qt::DisplayRole, "display" },
-    { Qt::CheckStateRole, "selected" }
+enum AudioDeviceRole {
+    DisplayRole = Qt::DisplayRole,
+    CheckStateRole = Qt::CheckStateRole,
+    PeakRole = Qt::UserRole
 };
+
+QHash<int, QByteArray> AudioDeviceList::_roles {
+    { DisplayRole, "display" },
+    { CheckStateRole, "selected" },
+    { PeakRole, "peak" }
+};
+
 Qt::ItemFlags AudioDeviceList::_flags { Qt::ItemIsSelectable | Qt::ItemIsEnabled };
 
 QVariant AudioDeviceList::data(const QModelIndex& index, int role) const {
-    if (!index.isValid() || index.row() >= _devices.size()) {
+    if (!index.isValid() || index.row() >= rowCount()) {
         return QVariant();
     }
 
-    if (role == Qt::DisplayRole) {
-        return _devices.at(index.row()).display;
-    } else if (role == Qt::CheckStateRole) {
-        return _devices.at(index.row()).selected;
+    if (role == DisplayRole) {
+        return _devices.at(index.row())->display;
+    } else if (role == CheckStateRole) {
+        return _devices.at(index.row())->selected;
     } else {
         return QVariant();
     }
 }
 
+QVariant AudioInputDeviceList::data(const QModelIndex& index, int role) const {
+    if (index.isValid() && index.row() < rowCount() && role == PeakRole) {
+        return std::static_pointer_cast<AudioInputDevice>(_devices.at(index.row()))->peak;
+    } else {
+        return AudioDeviceList::data(index, role);
+    }
+}
+
 bool AudioDeviceList::setData(const QModelIndex& index, const QVariant& value, int role) {
-    if (!index.isValid() || index.row() >= _devices.size() || role != Qt::CheckStateRole) {
+	if (!index.isValid() || index.row() >= rowCount() || role != CheckStateRole) {
         return false;
     }
 
@@ -73,19 +89,19 @@ bool AudioDeviceList::setDevice(int row, bool fromUser) {
     auto& device = _devices[row];
 
     // skip if already selected
-    if (!device.selected) {
+    if (!device->selected) {
         auto client = DependencyManager::get<AudioClient>();
         QMetaObject::invokeMethod(client.data(), "switchAudioDevice", Qt::BlockingQueuedConnection,
             Q_RETURN_ARG(bool, success),
             Q_ARG(QAudio::Mode, _mode),
-            Q_ARG(const QAudioDeviceInfo&, device.info));
+            Q_ARG(const QAudioDeviceInfo&, device->info));
 
         if (success) {
-            device.selected = true;
+            device->selected = true;
             if (fromUser) {
-                emit deviceSelected(device.info, _selectedDevice);
+                emit deviceSelected(device->info, _selectedDevice);
             }
-            emit deviceChanged(device.info);
+            emit deviceChanged(device->info);
         }
     }
 
@@ -100,7 +116,7 @@ void AudioDeviceList::resetDevice(bool contextIsHMD, const QString& device) {
     if (!device.isNull()) {
         auto i = 0;
         for (; i < rowCount(); ++i) {
-            if (device == _devices[i].info.deviceName()) {
+            if (device == _devices[i]->info.deviceName()) {
                 break;
             }
         }
@@ -138,8 +154,8 @@ void AudioDeviceList::onDeviceChanged(const QAudioDeviceInfo& device) {
     _selectedDevice = device;
     QModelIndex index;
 
-    for (auto i = 0; i < _devices.size(); ++i) {
-        AudioDevice& device = _devices[i];
+    for (auto i = 0; i < rowCount(); ++i) {
+        AudioDevice& device = *_devices[i];
 
         if (device.selected && device.info != _selectedDevice) {
             device.selected = false;
@@ -166,17 +182,47 @@ void AudioDeviceList::onDevicesChanged(const QList<QAudioDeviceInfo>& devices) {
             .remove("Device")
             .replace(" )", ")");
         device.selected = (device.info == _selectedDevice);
-        _devices.push_back(device);
+        _devices.push_back(newDevice(device));
     }
 
     endResetModel();
 }
 
+bool AudioInputDeviceList::peakValuesAvailable() {
+    std::call_once(_peakFlag, [&] {
+        _peakValuesAvailable = DependencyManager::get<AudioClient>()->peakValuesAvailable();
+    });
+    return _peakValuesAvailable;
+}
+
+void AudioInputDeviceList::setPeakValuesEnabled(bool enable) {
+    if (peakValuesAvailable() && (enable != _peakValuesEnabled)) {
+        DependencyManager::get<AudioClient>()->enablePeakValues(enable);
+        _peakValuesEnabled = enable;
+        emit peakValuesEnabledChanged(_peakValuesEnabled);
+    }
+}
+
+void AudioInputDeviceList::onPeakValueListChanged(const QList<float>& peakValueList) {
+    assert(_mode == QAudio::AudioInput);
+
+    if (peakValueList.length() != rowCount()) {
+        qWarning() << "AudioDeviceList" << __FUNCTION__ << "length mismatch";
+    }
+
+    for (auto i = 0; i < rowCount(); ++i) {
+        std::static_pointer_cast<AudioInputDevice>(_devices[i])->peak = peakValueList[i];
+    }
+
+    emit dataChanged(createIndex(0, 0), createIndex(rowCount() - 1, 0), { PeakRole });
+}
+
 AudioDevices::AudioDevices(bool& contextIsHMD) : _contextIsHMD(contextIsHMD) {
     auto client = DependencyManager::get<AudioClient>();
 
     connect(client.data(), &AudioClient::deviceChanged, this, &AudioDevices::onDeviceChanged, Qt::QueuedConnection);
     connect(client.data(), &AudioClient::devicesChanged, this, &AudioDevices::onDevicesChanged, Qt::QueuedConnection);
+    connect(client.data(), &AudioClient::peakValueListChanged, &_inputs, &AudioInputDeviceList::onPeakValueListChanged, Qt::QueuedConnection);
 
     // connections are made after client is initialized, so we must also fetch the devices
     _inputs.onDeviceChanged(client->getActiveAudioDevice(QAudio::AudioInput));
diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h
index cd47ab4191..bdf03820f4 100644
--- a/interface/src/scripting/AudioDevices.h
+++ b/interface/src/scripting/AudioDevices.h
@@ -12,6 +12,9 @@
 #ifndef hifi_scripting_AudioDevices_h
 #define hifi_scripting_AudioDevices_h
 
+#include <memory>
+#include <mutex>
+
 #include <QObject>
 #include <QAbstractListModel>
 #include <QAudioDeviceInfo>
@@ -29,7 +32,11 @@ class AudioDeviceList : public QAbstractListModel {
     Q_OBJECT
 
 public:
-    AudioDeviceList(QAudio::Mode mode) : _mode(mode) {}
+    AudioDeviceList(QAudio::Mode mode = QAudio::AudioOutput) : _mode(mode) {}
+    ~AudioDeviceList() = default;
+
+    virtual std::shared_ptr<AudioDevice> newDevice(const AudioDevice& device)
+        { return std::make_shared<AudioDevice>(device); }
 
     int rowCount(const QModelIndex& parent = QModelIndex()) const override { Q_UNUSED(parent); return _devices.size(); }
     QHash<int, QByteArray> roleNames() const override { return _roles; }
@@ -47,11 +54,11 @@ signals:
                         const QAudioDeviceInfo& previousDevice = QAudioDeviceInfo());
     void deviceChanged(const QAudioDeviceInfo& device);
 
-private slots:
+protected slots:
     void onDeviceChanged(const QAudioDeviceInfo& device);
     void onDevicesChanged(const QList<QAudioDeviceInfo>& devices);
 
-private:
+protected:
     friend class AudioDevices;
 
     bool setDevice(int index, bool fromUser);
@@ -59,16 +66,54 @@ private:
     static QHash<int, QByteArray> _roles;
     static Qt::ItemFlags _flags;
 
-    QAudio::Mode _mode;
+    QAudio::Mode _mode { QAudio::AudioOutput };
     QAudioDeviceInfo _selectedDevice;
-    QList<AudioDevice> _devices;
+    QList<std::shared_ptr<AudioDevice>> _devices;
+};
+
+class AudioInputDevice : public AudioDevice {
+public:
+    AudioInputDevice(const AudioDevice& device) : AudioDevice(device) {}
+    float peak { 0.0f };
+};
+
+class AudioInputDeviceList : public AudioDeviceList {
+    Q_OBJECT
+    Q_PROPERTY(bool peakValuesAvailable READ peakValuesAvailable)
+    Q_PROPERTY(bool peakValuesEnabled READ peakValuesEnabled WRITE setPeakValuesEnabled NOTIFY peakValuesEnabledChanged)
+
+public:
+    AudioInputDeviceList() : AudioDeviceList(QAudio::AudioInput) {}
+    virtual ~AudioInputDeviceList() = default;
+
+    virtual std::shared_ptr<AudioDevice> newDevice(const AudioDevice& device)
+        { return std::make_shared<AudioInputDevice>(device); }
+
+    QVariant data(const QModelIndex& index, int role) const override;
+
+signals:
+    void peakValuesEnabledChanged(bool enabled);
+
+protected slots:
+    void onPeakValueListChanged(const QList<float>& peakValueList);
+
+protected:
+    friend class AudioDevices;
+
+    bool peakValuesAvailable();
+    std::once_flag _peakFlag;
+    bool _peakValuesAvailable;
+
+    bool peakValuesEnabled() const { return _peakValuesEnabled; }
+    void setPeakValuesEnabled(bool enable);
+    bool _peakValuesEnabled { false };
 };
 
 class Audio;
 
 class AudioDevices : public QObject {
     Q_OBJECT
-    Q_PROPERTY(AudioDeviceList* input READ getInputList NOTIFY nop)
+    Q_PROPERTY(AudioInputDeviceList* input READ getInputList NOTIFY nop)
     Q_PROPERTY(AudioDeviceList* output READ getOutputList NOTIFY nop)
 
 public:
@@ -86,11 +131,11 @@ private slots:
 private:
     friend class Audio;
 
-    AudioDeviceList* getInputList() { return &_inputs; }
+    AudioInputDeviceList* getInputList() { return &_inputs; }
     AudioDeviceList* getOutputList() { return &_outputs; }
 
-    AudioDeviceList _inputs { QAudio::AudioInput };
-    AudioDeviceList _outputs { QAudio::AudioOutput };
+    AudioInputDeviceList _inputs;
+    AudioDeviceList _outputs;
 
     bool& _contextIsHMD;
 };

From 9b72ab96ff5a019d20b7a030a48c20af1bf84023 Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zmp@umich.edu>
Date: Tue, 27 Jun 2017 17:37:24 -0400
Subject: [PATCH 04/10] add peak values to qml

---
 interface/resources/qml/hifi/audio/Audio.qml  | 26 ++++++++++++++++---
 .../audio/{InputLevel.qml => InputPeak.qml}   |  6 ++---
 2 files changed, 25 insertions(+), 7 deletions(-)
 rename interface/resources/qml/hifi/audio/{InputLevel.qml => InputPeak.qml} (95%)

diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml
index c13bd3281a..9ebde8b61f 100644
--- a/interface/resources/qml/hifi/audio/Audio.qml
+++ b/interface/resources/qml/hifi/audio/Audio.qml
@@ -36,6 +36,23 @@ Rectangle {
         return root.parent.objectName == "loader";
     }
 
+    property bool showPeaks: true;
+    function enablePeakValues() {
+        Audio.devices.input.peakValuesEnabled = true;
+        Audio.devices.input.peakValuesEnabledChanged.connect(function(enabled) {
+            if (!enabled && root.showPeaks) {
+                Audio.devices.input.peakValuesEnabled = true;
+            }
+        });
+    }
+    function disablePeakValues() {
+        root.showPeaks = false;
+        Audio.devices.input.peakValuesEnabled = false;
+    }
+
+    Component.onCompleted: enablePeakValues();
+    Component.onDestruction: disablePeakValues();
+
     Column {
         y: 16; // padding does not work
         spacing: 16;
@@ -122,7 +139,7 @@ Rectangle {
                     width: parent.width;
 
                     AudioControls.CheckBox {
-                        Layout.maximumWidth: parent.width - level.width - 40;
+                        Layout.maximumWidth: parent.width - inputPeak.width - 40;
                         text: display;
                         wrap: false;
                         checked: selected;
@@ -131,11 +148,12 @@ Rectangle {
                             checked = Qt.binding(function() { return selected; }); // restore binding
                         }
                     }
-                    InputLevel {
-                        id: level;
+                    InputPeak {
+                        id: inputPeak;
+                        visible: Audio.devices.input.peakValuesAvailable;
+                        peak: model.peak;
                         Layout.alignment: Qt.AlignRight;
                         Layout.rightMargin: 30;
-                        visible: selected;
                     }
                 }
             }
diff --git a/interface/resources/qml/hifi/audio/InputLevel.qml b/interface/resources/qml/hifi/audio/InputPeak.qml
similarity index 95%
rename from interface/resources/qml/hifi/audio/InputLevel.qml
rename to interface/resources/qml/hifi/audio/InputPeak.qml
index 58ce3a1ec7..4ff49091b1 100644
--- a/interface/resources/qml/hifi/audio/InputLevel.qml
+++ b/interface/resources/qml/hifi/audio/InputPeak.qml
@@ -1,5 +1,5 @@
 //
-//  InputLevel.qml
+//  InputPeak.qml
 //  qml/hifi/audio
 //
 //  Created by Zach Pomerantz on 6/20/2017
@@ -15,7 +15,7 @@ import QtQuick.Layouts 1.3
 import QtGraphicalEffects 1.0
 
 Rectangle {
-    readonly property var level: Audio.inputLevel;
+    property var peak;
 
     width: 70;
     height: 8;
@@ -65,7 +65,7 @@ Rectangle {
 
         Rectangle { // mask
             id: mask;
-            width: parent.width * level;
+            width: parent.width * peak;
             radius: 5;
             anchors {
                 bottom: parent.bottom;

From ed49f80322377e7d5e40f4229889eed13345504f Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zmp@umich.edu>
Date: Thu, 29 Jun 2017 17:32:52 -0400
Subject: [PATCH 05/10] fix peak check secs->msecs

---
 libraries/audio-client/src/AudioClient.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp
index 7f872443be..88f34d60f1 100644
--- a/libraries/audio-client/src/AudioClient.cpp
+++ b/libraries/audio-client/src/AudioClient.cpp
@@ -234,8 +234,8 @@ AudioClient::AudioClient() :
     connect(_checkPeakValuesTimer, &QTimer::timeout, [this] {
         QtConcurrent::run(QThreadPool::globalInstance(), [this] { checkPeakValues(); });
     });
-    const unsigned long PEAK_VALUES_CHECK_INTERVAL_SECS = 50;
-    _checkPeakValuesTimer->start(PEAK_VALUES_CHECK_INTERVAL_SECS);
+    const unsigned long PEAK_VALUES_CHECK_INTERVAL_MSECS = 50;
+    _checkPeakValuesTimer->start(PEAK_VALUES_CHECK_INTERVAL_MSECS);
 
     configureReverb();
 

From 631e5a7b0433c100b457428a6e53bc0cf9ac575a Mon Sep 17 00:00:00 2001
From: Zach Pomerantz <zmp@umich.edu>
Date: Thu, 29 Jun 2017 17:34:01 -0400
Subject: [PATCH 06/10] add override to AudioInputDeviceList::newDevice

---
 interface/src/scripting/AudioDevices.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/interface/src/scripting/AudioDevices.h b/interface/src/scripting/AudioDevices.h
index bdf03820f4..60d547f58f 100644
--- a/interface/src/scripting/AudioDevices.h
+++ b/interface/src/scripting/AudioDevices.h
@@ -86,7 +86,7 @@ public:
     AudioInputDeviceList() : AudioDeviceList(QAudio::AudioInput) {}
     virtual ~AudioInputDeviceList() = default;
 
-    virtual std::shared_ptr<AudioDevice> newDevice(const AudioDevice& device)
+    virtual std::shared_ptr<AudioDevice> newDevice(const AudioDevice& device) override
         { return std::make_shared<AudioInputDevice>(device); }
 
     QVariant data(const QModelIndex& index, int role) const override;

From 0da19af6f32c274f42a0fc08b18a6bb2cb220280 Mon Sep 17 00:00:00 2001
From: Ken Cooke <ken@highfidelity.io>
Date: Tue, 11 Jul 2017 10:06:38 -0700
Subject: [PATCH 07/10] Stop all capture devices when Audio.qml is not visible.
 Otherwise, Windows will continue to capture audio from all attached devices
 (possibly causing mic audio glitches) until Interface shutdown.

---
 interface/resources/qml/hifi/audio/Audio.qml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/interface/resources/qml/hifi/audio/Audio.qml b/interface/resources/qml/hifi/audio/Audio.qml
index aea0f0501d..4e0b4cebe2 100644
--- a/interface/resources/qml/hifi/audio/Audio.qml
+++ b/interface/resources/qml/hifi/audio/Audio.qml
@@ -52,6 +52,7 @@ Rectangle {
 
     Component.onCompleted: enablePeakValues();
     Component.onDestruction: disablePeakValues();
+    onVisibleChanged: visible ? enablePeakValues() : disablePeakValues();
 
     Column {
         y: 16; // padding does not work

From 50deee1d38fd9f66d94bd8b3731f29342e43b411 Mon Sep 17 00:00:00 2001
From: Ken Cooke <ken@highfidelity.io>
Date: Wed, 12 Jul 2017 12:29:25 -0700
Subject: [PATCH 08/10] Replace tabs with spaces

---
 interface/src/scripting/AudioDevices.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp
index 574ec00ad6..c3958f2ad9 100644
--- a/interface/src/scripting/AudioDevices.cpp
+++ b/interface/src/scripting/AudioDevices.cpp
@@ -73,7 +73,7 @@ QVariant AudioInputDeviceList::data(const QModelIndex& index, int role) const {
 }
 
 bool AudioDeviceList::setData(const QModelIndex& index, const QVariant& value, int role) {
-	if (!index.isValid() || index.row() >= rowCount() || role != CheckStateRole) {
+    if (!index.isValid() || index.row() >= rowCount() || role != CheckStateRole) {
         return false;
     }
 

From cf6c1ae4a54391d4d096c5e76805534fbfc60f31 Mon Sep 17 00:00:00 2001
From: Ken Cooke <ken@highfidelity.io>
Date: Wed, 12 Jul 2017 12:30:02 -0700
Subject: [PATCH 09/10] Replace tabs with spaces

---
 assignment-client/src/audio/AudioMixerSlave.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/assignment-client/src/audio/AudioMixerSlave.cpp b/assignment-client/src/audio/AudioMixerSlave.cpp
index ed63bbc298..a131e266d2 100644
--- a/assignment-client/src/audio/AudioMixerSlave.cpp
+++ b/assignment-client/src/audio/AudioMixerSlave.cpp
@@ -558,7 +558,7 @@ float computeAzimuth(const AvatarAudioStream& listeningNodeStream, const Positio
         
         // produce an oriented angle about the y-axis
         glm::vec3 direction = rotatedSourcePosition * (1.0f / fastSqrtf(rotatedSourcePositionLength2));
-		float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
+        float angle = fastAcosf(glm::clamp(-direction.z, -1.0f, 1.0f)); // UNIT_NEG_Z is "forward"
         return (direction.x < 0.0f) ? -angle : angle;
 
     } else {   

From 1fc050fdef894751f9888ef97d1c30a4810ab729 Mon Sep 17 00:00:00 2001
From: Ken Cooke <ken@highfidelity.io>
Date: Mon, 7 Aug 2017 08:11:43 -0700
Subject: [PATCH 10/10] Fix _devices access via shared_ptr

---
 interface/src/scripting/AudioDevices.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/interface/src/scripting/AudioDevices.cpp b/interface/src/scripting/AudioDevices.cpp
index c06f921a7f..e5cc43f9df 100644
--- a/interface/src/scripting/AudioDevices.cpp
+++ b/interface/src/scripting/AudioDevices.cpp
@@ -74,11 +74,11 @@ QVariant AudioDeviceList::data(const QModelIndex& index, int role) const {
     }
 
     if (role == DisplayRole) {
-        return _devices.at(index.row()).display;
+        return _devices.at(index.row())->display;
     } else if (role == CheckStateRole) {
-        return _devices.at(index.row()).selected;
+        return _devices.at(index.row())->selected;
     } else if (role == InfoRole) {
-        return QVariant::fromValue<QAudioDeviceInfo>(_devices.at(index.row()).info);
+        return QVariant::fromValue<QAudioDeviceInfo>(_devices.at(index.row())->info);
     } else {
         return QVariant();
     }
@@ -90,7 +90,7 @@ QVariant AudioInputDeviceList::data(const QModelIndex& index, int role) const {
     }
 
     if (role == PeakRole) {
-        return std::static_pointer_cast<AudioInputDevice>(_devices.at(index.row())).peak;
+        return std::static_pointer_cast<AudioInputDevice>(_devices.at(index.row()))->peak;
     } else {
         return AudioDeviceList::data(index, role);
     }