// // Audio.cpp // interface/src/scripting // // Created by Zach Pomerantz on 28/5/2017. // Copyright 2017 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 "Audio.h" #include #include "Application.h" #include "AudioClient.h" #include "AudioHelpers.h" #include "ui/AvatarInputs.h" using namespace scripting; QString Audio::AUDIO { "Audio" }; QString Audio::DESKTOP { "Desktop" }; QString Audio::HMD { "VR" }; Setting::Handle enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true }; Setting::Handle enableWarnWhenMutedSetting { QStringList { Audio::AUDIO, "WarnWhenMuted" }, true }; Setting::Handle mutedSetting { QStringList{ Audio::AUDIO, "MuteMicrophone" }, false }; float Audio::loudnessToLevel(float loudness) { float level = loudness * (1/32768.0f); // level in [0, 1] level = 6.02059991f * fastLog2f(level); // convert to dBFS level = (level + 48.0f) * (1/42.0f); // map [-48, -6] dBFS to [0, 1] return glm::clamp(level, 0.0f, 1.0f); } Audio::Audio() : _devices(_contextIsHMD) { auto client = DependencyManager::get().data(); connect(client, &AudioClient::muteToggled, this, &Audio::setMuted); connect(client, &AudioClient::noiseReductionChanged, this, &Audio::enableNoiseReduction); connect(client, &AudioClient::warnWhenMutedChanged, this, &Audio::enableWarnWhenMuted); connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged); connect(client, &AudioClient::inputVolumeChanged, this, &Audio::setInputVolume); connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged); enableNoiseReduction(enableNoiseReductionSetting.get()); enableWarnWhenMuted(enableWarnWhenMutedSetting.get()); onContextChanged(); setMuted(mutedSetting.get()); } bool Audio::startRecording(const QString& filepath) { return resultWithWriteLock([&] { return DependencyManager::get()->startRecording(filepath); }); } bool Audio::getRecording() { return resultWithReadLock([&] { return DependencyManager::get()->getRecording(); }); } void Audio::stopRecording() { withWriteLock([&] { DependencyManager::get()->stopRecording(); }); } bool Audio::isMuted() const { return resultWithReadLock([&] { return _isMuted; }); } void Audio::setMuted(bool isMuted) { bool changed = false; withWriteLock([&] { if (_isMuted != isMuted) { _isMuted = isMuted; mutedSetting.set(_isMuted); auto client = DependencyManager::get().data(); QMetaObject::invokeMethod(client, "setMuted", Q_ARG(bool, isMuted), Q_ARG(bool, false)); changed = true; } }); if (changed) { emit mutedChanged(isMuted); } } bool Audio::noiseReductionEnabled() const { return resultWithReadLock([&] { return _enableNoiseReduction; }); } void Audio::enableNoiseReduction(bool enable) { bool changed = false; withWriteLock([&] { if (_enableNoiseReduction != enable) { _enableNoiseReduction = enable; auto client = DependencyManager::get().data(); QMetaObject::invokeMethod(client, "setNoiseReduction", Q_ARG(bool, enable), Q_ARG(bool, false)); enableNoiseReductionSetting.set(enable); changed = true; } }); if (changed) { emit noiseReductionChanged(enable); } } bool Audio::warnWhenMutedEnabled() const { return resultWithReadLock([&] { return _enableWarnWhenMuted; }); } void Audio::enableWarnWhenMuted(bool enable) { bool changed = false; withWriteLock([&] { if (_enableWarnWhenMuted != enable) { _enableWarnWhenMuted = enable; auto client = DependencyManager::get().data(); QMetaObject::invokeMethod(client, "setWarnWhenMuted", Q_ARG(bool, enable), Q_ARG(bool, false)); enableWarnWhenMutedSetting.set(enable); changed = true; } }); if (changed) { emit warnWhenMutedChanged(enable); } } float Audio::getInputVolume() const { return resultWithReadLock([&] { return _inputVolume; }); } void Audio::setInputVolume(float volume) { // getInputVolume will not reflect changes synchronously, so clamp beforehand volume = glm::clamp(volume, 0.0f, 1.0f); bool changed = false; withWriteLock([&] { if (_inputVolume != volume) { _inputVolume = volume; auto client = DependencyManager::get().data(); QMetaObject::invokeMethod(client, "setInputVolume", Q_ARG(float, volume), Q_ARG(bool, false)); changed = true; } }); if (changed) { emit inputVolumeChanged(volume); } } float Audio::getInputLevel() const { return resultWithReadLock([&] { return _inputLevel; }); } bool Audio::isClipping() const { return resultWithReadLock([&] { return _isClipping; }); } void Audio::onInputLoudnessChanged(float loudness, bool isClipping) { float level = loudnessToLevel(loudness); bool levelChanged = false; bool isClippingChanged = false; withWriteLock([&] { if (_inputLevel != level) { _inputLevel = level; levelChanged = true; } if (_isClipping != isClipping) { _isClipping = isClipping; isClippingChanged = true; } }); if (levelChanged) { emit inputLevelChanged(level); } if (isClippingChanged) { emit clippingChanged(isClipping); } } QString Audio::getContext() const { return resultWithReadLock([&] { return _contextIsHMD ? Audio::HMD : Audio::DESKTOP; }); } void Audio::onContextChanged() { bool changed = false; bool isHMD = qApp->isHMDMode(); withWriteLock([&] { if (_contextIsHMD != isHMD) { _contextIsHMD = isHMD; changed = true; } }); if (changed) { emit contextChanged(isHMD ? Audio::HMD : Audio::DESKTOP); } } void Audio::setReverb(bool enable) { withWriteLock([&] { DependencyManager::get()->setReverb(enable); }); } void Audio::setReverbOptions(const AudioEffectOptions* options) { withWriteLock([&] { DependencyManager::get()->setReverbOptions(options); }); } void Audio::setInputDevice(const QAudioDeviceInfo& device, bool isHMD) { withWriteLock([&] { _devices.chooseInputDevice(device, isHMD); }); } void Audio::setOutputDevice(const QAudioDeviceInfo& device, bool isHMD) { withWriteLock([&] { _devices.chooseOutputDevice(device, isHMD); }); }