Merge pull request #1007 from digisomni/feature/manual-noise-reduction-gate

Add manual mode to noise reduction + add noise reduction threshold
This commit is contained in:
Kalila 2021-03-03 21:18:59 -05:00 committed by GitHub
commit 672bceee1c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 408 additions and 50 deletions

View file

@ -15,6 +15,7 @@
import QtQuick 2.10 import QtQuick 2.10
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import QtQuick.Layouts 1.3 import QtQuick.Layouts 1.3
import QtGraphicalEffects 1.0
import stylesUit 1.0 import stylesUit 1.0
import controlsUit 1.0 as HifiControlsUit import controlsUit 1.0 as HifiControlsUit
@ -104,6 +105,11 @@ Rectangle {
AudioScriptingInterface.setSystemInjectorGain(sliderValue); AudioScriptingInterface.setSystemInjectorGain(sliderValue);
} }
} }
function updateNoiseReductionThresholdFromQML(sliderValue) {
if (AudioScriptingInterface.getNoiseReductionThreshold() !== sliderValue) {
AudioScriptingInterface.setNoiseReductionThreshold(sliderValue);
}
}
Component.onCompleted: { Component.onCompleted: {
enablePeakValues(); enablePeakValues();
@ -164,7 +170,7 @@ Rectangle {
x: 2 * margins.paddings; x: 2 * margins.paddings;
width: parent.width; width: parent.width;
// switch heights + 2 * top margins // switch heights + 2 * top margins
height: (root.switchHeight) * 6 + 48; height: (bar.currentIndex === 0) ? (root.switchHeight) * 2 + 48 : (root.switchHeight) * 4 + 48;
anchors.top: firstSeparator.bottom; anchors.top: firstSeparator.bottom;
anchors.topMargin: 10; anchors.topMargin: 10;
@ -175,6 +181,7 @@ Rectangle {
height: parent.height; height: parent.height;
anchors.top: parent.top anchors.top: parent.top
anchors.left: parent.left; anchors.left: parent.left;
HifiControlsUit.Switch { HifiControlsUit.Switch {
id: muteMic; id: muteMic;
height: root.switchHeight; height: root.switchHeight;
@ -193,45 +200,11 @@ Rectangle {
} }
} }
HifiControlsUit.Switch {
id: noiseReductionSwitch;
height: root.switchHeight;
switchWidth: root.switchWidth;
anchors.top: muteMic.bottom;
anchors.topMargin: 24
anchors.left: parent.left
labelTextOn: "Noise Reduction";
labelTextSize: 16;
backgroundOnColor: "#E3E3E3";
checked: AudioScriptingInterface.noiseReduction;
onCheckedChanged: {
AudioScriptingInterface.noiseReduction = checked;
checked = Qt.binding(function() { return AudioScriptingInterface.noiseReduction; }); // restore binding
}
}
HifiControlsUit.Switch {
id: acousticEchoCancellationSwitch;
height: root.switchHeight;
switchWidth: root.switchWidth;
anchors.top: noiseReductionSwitch.bottom
anchors.topMargin: 24
anchors.left: parent.left
labelTextOn: "Echo Cancellation";
labelTextSize: 16;
backgroundOnColor: "#E3E3E3";
checked: AudioScriptingInterface.acousticEchoCancellation;
onCheckedChanged: {
AudioScriptingInterface.acousticEchoCancellation = checked;
checked = Qt.binding(function() { return AudioScriptingInterface.acousticEchoCancellation; });
}
}
HifiControlsUit.Switch { HifiControlsUit.Switch {
id: pttSwitch id: pttSwitch
height: root.switchHeight; height: root.switchHeight;
switchWidth: root.switchWidth; switchWidth: root.switchWidth;
anchors.top: acousticEchoCancellationSwitch.bottom; anchors.top: muteMic.bottom;
anchors.topMargin: 24 anchors.topMargin: 24
anchors.left: parent.left anchors.left: parent.left
labelTextOn: (bar.currentIndex === 0) ? qsTr("Push To Talk (T)") : qsTr("Push To Talk"); labelTextOn: (bar.currentIndex === 0) ? qsTr("Push To Talk (T)") : qsTr("Push To Talk");
@ -254,6 +227,7 @@ Rectangle {
height: parent.height; height: parent.height;
anchors.top: parent.top anchors.top: parent.top
anchors.left: switchContainer.right; anchors.left: switchContainer.right;
HifiControlsUit.Switch { HifiControlsUit.Switch {
id: warnMutedSwitch id: warnMutedSwitch
height: root.switchHeight; height: root.switchHeight;
@ -271,7 +245,6 @@ Rectangle {
} }
} }
HifiControlsUit.Switch { HifiControlsUit.Switch {
id: audioLevelSwitch id: audioLevelSwitch
height: root.switchHeight; height: root.switchHeight;
@ -519,13 +492,264 @@ Rectangle {
anchors.top: systemInjectorGainContainer.bottom; anchors.top: systemInjectorGainContainer.bottom;
anchors.topMargin: 10; anchors.topMargin: 10;
} }
Item {
id: noiseReductionHeader
x: margins.paddings;
width: parent.width - margins.paddings * 2;
height: 36;
anchors.top: secondSeparator.bottom;
anchors.topMargin: 10;
HiFiGlyphs {
width: margins.sizeCheckBox;
text: hifi.glyphs.mic;
color: hifi.colors.white;
anchors.left: parent.left;
anchors.leftMargin: -size / 4; // The glyph has empty space at left about 25%
anchors.verticalCenter: parent.verticalCenter;
size: 30;
}
RalewayRegular {
anchors.verticalCenter: parent.verticalCenter;
width: margins.sizeText + margins.sizeLevel;
anchors.left: parent.left;
anchors.leftMargin: margins.sizeCheckBox;
size: 22;
color: hifi.colors.white;
text: qsTr("Noise Reduction");
}
}
Item {
id: noiseReductionSwitches;
x: 2 * margins.paddings;
width: parent.width;
// switch heights + 2 * top margins
height: (root.switchHeight) * 5 + 48;
anchors.top: noiseReductionHeader.bottom;
anchors.topMargin: 0;
Item {
id: noiseReductionSwitchContainer;
x: margins.paddings;
width: parent.width / 2;
height: parent.height;
anchors.top: parent.top;
anchors.left: parent.left;
HifiControlsUit.Switch {
id: acousticEchoCancellationSwitch;
height: root.switchHeight;
switchWidth: root.switchWidth;
anchors.top: noiseReductionSwitchContainer.top
anchors.topMargin: 24
anchors.left: parent.left
labelTextOn: "Echo Cancellation";
labelTextSize: 16;
backgroundOnColor: "#E3E3E3";
checked: AudioScriptingInterface.acousticEchoCancellation;
onCheckedChanged: {
AudioScriptingInterface.acousticEchoCancellation = checked;
checked = Qt.binding(function () { return AudioScriptingInterface.acousticEchoCancellation; });
}
}
HifiControlsUit.Switch {
id: noiseReductionSwitch;
height: root.switchHeight;
switchWidth: root.switchWidth;
anchors.top: acousticEchoCancellationSwitch.bottom;
anchors.topMargin: 24
anchors.left: parent.left
labelTextOn: "Noise Reduction";
labelTextSize: 16;
backgroundOnColor: "#E3E3E3";
checked: AudioScriptingInterface.noiseReduction;
onCheckedChanged: {
AudioScriptingInterface.noiseReduction = checked;
checked = Qt.binding(function () { return AudioScriptingInterface.noiseReduction; }); // restore binding
}
}
HifiControlsUit.Switch {
id: noiseReductionAutomaticSwitch;
height: root.switchHeight;
switchWidth: root.switchWidth;
anchors.top: noiseReductionSwitch.bottom;
anchors.topMargin: 24;
anchors.left: parent.left;
labelTextOn: "Manual Noise Reduction";
labelTextSize: 16;
backgroundOnColor: "#E3E3E3";
checked: !AudioScriptingInterface.noiseReductionAutomatic;
visible: AudioScriptingInterface.noiseReduction;
onCheckedChanged: {
AudioScriptingInterface.noiseReductionAutomatic = !checked;
checked = Qt.binding(function () { return !AudioScriptingInterface.noiseReductionAutomatic; }); // restore binding
}
}
}
}
Item {
id: noiseReductionThresholdContainer
x: margins.paddings;
anchors.top: noiseReductionSwitches.bottom;
anchors.topMargin: 16;
width: parent.width - margins.paddings * 2;
height: avatarGainSliderTextMetrics.height + 10;
visible: AudioScriptingInterface.noiseReduction && !AudioScriptingInterface.noiseReductionAutomatic;
HifiControlsUit.Slider {
id: noiseReductionThresholdSlider
anchors.right: parent.right
height: noiseReductionThresholdSliderTextMetrics.height
width: 200
minimumValue: 0.0
maximumValue: 1.0
stepSize: 0.05
value: AudioScriptingInterface.getNoiseReductionThreshold()
onValueChanged: {
updateNoiseReductionThresholdFromQML(value);
}
onPressedChanged: {
if (!pressed) {
updateNoiseReductionThresholdFromQML(value);
}
}
MouseArea {
anchors.fill: parent
onWheel: {
// Do nothing.
}
onDoubleClicked: {
noiseReductionThresholdSlider.value = 0.0;
}
onPressed: {
// Pass through to Slider
mouse.accepted = false;
}
onReleased: {
// the above mouse.accepted seems to make this
// never get called, nonetheless...
mouse.accepted = false;
}
}
}
TextMetrics {
id: noiseReductionThresholdSliderTextMetrics
text: noiseReductionThresholdSliderText.text
font: noiseReductionThresholdSliderText.font
}
RalewayRegular {
// The slider for my card is special, it controls the master gain
id: noiseReductionThresholdSliderText;
text: "Audio input threshold";
size: 16;
anchors.left: parent.left;
color: hifi.colors.white;
horizontalAlignment: Text.AlignLeft;
verticalAlignment: Text.AlignTop;
}
Item {
id: noisePeak
anchors.right: parent.right
anchors.rightMargin: 5;
anchors.top: noiseReductionThresholdSlider.bottom;
width: noiseReductionThresholdSlider.width - 10;
height: 8;
Text {
id: status;
anchors {
horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter;
}
visible: AudioScriptingInterface.muted;
color: "#E2334D";
text: "MUTED";
font.pointSize: 10;
}
Item {
id: noiseBar;
property bool gated: false;
property var level: AudioScriptingInterface.inputLevel;
width: parent.width;
height: parent.height;
anchors { fill: parent }
visible: !status.visible;
Component.onCompleted: {
AudioScriptingInterface.noiseGateOpened.connect(function () { noiseBar.gated = false; });
AudioScriptingInterface.noiseGateClosed.connect(function () { noiseBar.gated = true; });
AudioScriptingInterface.inputLevelChanged.connect(function () { noiseBar.level = AudioScriptingInterface.inputLevel; });
}
Rectangle { // base
radius: 4;
anchors { fill: parent }
color: colors.gutter;
}
Rectangle { // noiseMask
id: noiseMask;
width: parent.width * noiseBar.level;
radius: 5;
anchors {
bottom: parent.bottom;
bottomMargin: 0;
top: parent.top;
topMargin: 0;
left: parent.left;
leftMargin: 0;
}
}
LinearGradient {
anchors { fill: noiseMask }
source: noiseMask
start: Qt.point(0, 0);
end: Qt.point(noiseBar.width, 0);
gradient: Gradient {
GradientStop {
position: 0;
color: noiseBar.gated ? "#E2334D" : "#39A38F";
}
GradientStop {
position: 1;
color: noiseBar.gated ? "#E2334D" : "#39A38F";
}
}
}
}
}
}
Separator {
id: thirdSeparator;
anchors.top: noiseReductionThresholdContainer.bottom;
anchors.topMargin: 14;
}
Item { Item {
id: inputDeviceHeader id: inputDeviceHeader
x: margins.paddings; x: margins.paddings;
width: parent.width - margins.paddings*2; width: parent.width - margins.paddings*2;
height: 36; height: 36;
anchors.top: secondSeparator.bottom; anchors.top: thirdSeparator.bottom;
anchors.topMargin: 10; anchors.topMargin: 10;
HiFiGlyphs { HiFiGlyphs {
@ -597,19 +821,19 @@ Rectangle {
} }
} }
AudioControls.InputPeak { AudioControls.InputPeak {
id: inputLevel id: inputLevel
anchors.right: parent.right anchors.right: parent.right
peak: model.peak; peak: model.peak;
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: ((bar.currentIndex === 1 && isVR) || visible: ((bar.currentIndex === 1 && isVR) ||
(bar.currentIndex === 0 && !isVR)) && (bar.currentIndex === 0 && !isVR)) &&
AudioScriptingInterface.devices.input.peakValuesAvailable; AudioScriptingInterface.devices.input.peakValuesAvailable;
} }
} }
} }
Separator { Separator {
id: thirdSeparator; id: fourthSeparator;
anchors.top: inputView.bottom; anchors.top: inputView.bottom;
anchors.topMargin: 10; anchors.topMargin: 10;
} }
@ -617,7 +841,7 @@ Rectangle {
Item { Item {
id: outputDeviceHeader; id: outputDeviceHeader;
anchors.topMargin: 10; anchors.topMargin: 10;
anchors.top: thirdSeparator.bottom; anchors.top: fourthSeparator.bottom;
x: margins.paddings; x: margins.paddings;
width: parent.width - margins.paddings*2 width: parent.width - margins.paddings*2
height: 36 height: 36
@ -684,4 +908,4 @@ Rectangle {
} }
} }
} }
} }

View file

@ -25,6 +25,8 @@ QString Audio::DESKTOP { "Desktop" };
QString Audio::HMD { "VR" }; QString Audio::HMD { "VR" };
Setting::Handle<bool> enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true }; Setting::Handle<bool> enableNoiseReductionSetting { QStringList { Audio::AUDIO, "NoiseReduction" }, true };
Setting::Handle<bool> enableNoiseReductionAutomaticSetting { QStringList { Audio::AUDIO, "NoiseReductionAutomatic" }, true };
Setting::Handle<float> setNoiseReductionThresholdSetting { QStringList { Audio::AUDIO, "NoiseReductionThreshold" }, 0.1f };
Setting::Handle<bool> enableWarnWhenMutedSetting { QStringList { Audio::AUDIO, "WarnWhenMuted" }, true }; Setting::Handle<bool> enableWarnWhenMutedSetting { QStringList { Audio::AUDIO, "WarnWhenMuted" }, true };
Setting::Handle<bool> enableAcousticEchoCancellationSetting { QStringList { Audio::AUDIO, "AcousticEchoCancellation" }, true }; Setting::Handle<bool> enableAcousticEchoCancellationSetting { QStringList { Audio::AUDIO, "AcousticEchoCancellation" }, true };
@ -40,6 +42,8 @@ Audio::Audio() : _devices(_contextIsHMD) {
auto client = DependencyManager::get<AudioClient>().data(); auto client = DependencyManager::get<AudioClient>().data();
connect(client, &AudioClient::muteToggled, this, &Audio::setMuted); connect(client, &AudioClient::muteToggled, this, &Audio::setMuted);
connect(client, &AudioClient::noiseReductionChanged, this, &Audio::enableNoiseReduction); connect(client, &AudioClient::noiseReductionChanged, this, &Audio::enableNoiseReduction);
connect(client, &AudioClient::noiseReductionAutomaticChanged, this, &Audio::enableNoiseReductionAutomatic);
connect(client, &AudioClient::noiseReductionThresholdChanged, this, &Audio::setNoiseReductionThreshold);
connect(client, &AudioClient::warnWhenMutedChanged, this, &Audio::enableWarnWhenMuted); connect(client, &AudioClient::warnWhenMutedChanged, this, &Audio::enableWarnWhenMuted);
connect(client, &AudioClient::acousticEchoCancellationChanged, this, &Audio::enableAcousticEchoCancellation); connect(client, &AudioClient::acousticEchoCancellationChanged, this, &Audio::enableAcousticEchoCancellation);
connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged); connect(client, &AudioClient::inputLoudnessChanged, this, &Audio::onInputLoudnessChanged);
@ -47,6 +51,8 @@ Audio::Audio() : _devices(_contextIsHMD) {
connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged); connect(this, &Audio::contextChanged, &_devices, &AudioDevices::onContextChanged);
connect(this, &Audio::pushingToTalkChanged, this, &Audio::handlePushedToTalk); connect(this, &Audio::pushingToTalkChanged, this, &Audio::handlePushedToTalk);
enableNoiseReduction(enableNoiseReductionSetting.get()); enableNoiseReduction(enableNoiseReductionSetting.get());
enableNoiseReductionAutomatic(enableNoiseReductionAutomaticSetting.get());
setNoiseReductionThreshold(setNoiseReductionThresholdSetting.get());
enableWarnWhenMuted(enableWarnWhenMutedSetting.get()); enableWarnWhenMuted(enableWarnWhenMutedSetting.get());
enableAcousticEchoCancellation(enableAcousticEchoCancellationSetting.get()); enableAcousticEchoCancellation(enableAcousticEchoCancellationSetting.get());
onContextChanged(); onContextChanged();
@ -286,6 +292,50 @@ void Audio::enableNoiseReduction(bool enable) {
} }
} }
bool Audio::noiseReductionAutomatic() const {
return resultWithReadLock<bool>([&] {
return _noiseReductionAutomatic;
});
}
void Audio::enableNoiseReductionAutomatic(bool enable) {
bool changed = false;
withWriteLock([&] {
if (_noiseReductionAutomatic != enable) {
_noiseReductionAutomatic = enable;
auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "setNoiseReductionAutomatic", Q_ARG(bool, enable), Q_ARG(bool, false));
enableNoiseReductionAutomaticSetting.set(enable);
changed = true;
}
});
if (changed) {
emit noiseReductionAutomaticChanged(enable);
}
}
float Audio::getNoiseReductionThreshold() {
return resultWithReadLock<float>([&] {
return _noiseReductionThreshold;
});
}
void Audio::setNoiseReductionThreshold(float threshold) {
bool changed = false;
withWriteLock([&] {
if (_noiseReductionThreshold != threshold) {
_noiseReductionThreshold = threshold;
auto client = DependencyManager::get<AudioClient>().data();
QMetaObject::invokeMethod(client, "setNoiseReductionThreshold", Q_ARG(float, threshold), Q_ARG(bool, false));
setNoiseReductionThresholdSetting.set(threshold);
changed = true;
}
});
if (changed) {
emit noiseReductionThresholdChanged(threshold);
}
}
bool Audio::warnWhenMutedEnabled() const { bool Audio::warnWhenMutedEnabled() const {
return resultWithReadLock<bool>([&] { return resultWithReadLock<bool>([&] {
return _enableWarnWhenMuted; return _enableWarnWhenMuted;

View file

@ -50,6 +50,11 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
* @property {boolean} noiseReduction - <code>true</code> if noise reduction is enabled, otherwise <code>false</code>. When * @property {boolean} noiseReduction - <code>true</code> if noise reduction is enabled, otherwise <code>false</code>. When
* enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just * enabled, the input audio signal is blocked (fully attenuated) when it falls below an adaptive threshold set just
* above the noise floor. * above the noise floor.
* @property {boolean} noiseReductionAutomatic - <code>true</code> if audio input noise reduction automatic mode is enabled,
* <code>false</code> if in manual mode. Manual mode allows you to use <code>Audio.noiseReductionThreshold</code>
* to set a manual sensitivity for the noise gate.
* @property {number} noiseReductionThreshold - Sets the noise gate threshold before your mic audio is transmitted.
* (Applies only if <code>Audio.noiseReductionAutomatic</code> is <code>false</code>.)
* @property {number} inputVolume - Adjusts the volume of the input audio, range <code>0.0</code> &ndash; <code>1.0</code>. * @property {number} inputVolume - Adjusts the volume of the input audio, range <code>0.0</code> &ndash; <code>1.0</code>.
* If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some * If set to a value, the resulting value depends on the input device: for example, the volume can't be changed on some
* devices, and others might only support values of <code>0.0</code> and <code>1.0</code>. * devices, and others might only support values of <code>0.0</code> and <code>1.0</code>.
@ -92,6 +97,8 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged) Q_PROPERTY(bool muted READ isMuted WRITE setMuted NOTIFY mutedChanged)
Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged) Q_PROPERTY(bool noiseReduction READ noiseReductionEnabled WRITE enableNoiseReduction NOTIFY noiseReductionChanged)
Q_PROPERTY(bool noiseReductionAutomatic READ noiseReductionAutomatic WRITE enableNoiseReductionAutomatic NOTIFY noiseReductionAutomaticChanged)
Q_PROPERTY(float noiseReductionThreshold READ getNoiseReductionThreshold WRITE setNoiseReductionThreshold NOTIFY noiseReductionThresholdChanged)
Q_PROPERTY(bool warnWhenMuted READ warnWhenMutedEnabled WRITE enableWarnWhenMuted NOTIFY warnWhenMutedChanged) Q_PROPERTY(bool warnWhenMuted READ warnWhenMutedEnabled WRITE enableWarnWhenMuted NOTIFY warnWhenMutedChanged)
Q_PROPERTY(bool acousticEchoCancellation Q_PROPERTY(bool acousticEchoCancellation
READ acousticEchoCancellationEnabled WRITE enableAcousticEchoCancellation NOTIFY acousticEchoCancellationChanged) READ acousticEchoCancellationEnabled WRITE enableAcousticEchoCancellation NOTIFY acousticEchoCancellationChanged)
@ -124,6 +131,7 @@ public:
bool isMuted() const; bool isMuted() const;
bool noiseReductionEnabled() const; bool noiseReductionEnabled() const;
bool noiseReductionAutomatic() const;
bool warnWhenMutedEnabled() const; bool warnWhenMutedEnabled() const;
bool acousticEchoCancellationEnabled() const; bool acousticEchoCancellationEnabled() const;
float getInputVolume() const; float getInputVolume() const;
@ -270,6 +278,20 @@ public:
* @returns {number} The injector gain (dB) in the client. * @returns {number} The injector gain (dB) in the client.
*/ */
Q_INVOKABLE float getSystemInjectorGain(); Q_INVOKABLE float getSystemInjectorGain();
/**jsdoc
* Sets the noise gate threshold before your mic audio is transmitted. (Applies only if <code>Audio.noiseReductionAutomatic</code> is <code>false</code>.)
* @function Audio.setNoiseReductionThreshold
* @param {number} threshold - The level that your input must surpass to be transmitted. <code>0.0</code> (open the gate completely) &ndash; <code>1.0</code>
*/
Q_INVOKABLE void setNoiseReductionThreshold(float threshold);
/**jsdoc
* Gets the noise reduction threshold.
* @function Audio.getNoiseReductionThreshold
* @returns {number} The noise reduction threshold. <code>0.0</code> &ndash; <code>1.0</code>
*/
Q_INVOKABLE float getNoiseReductionThreshold();
/**jsdoc /**jsdoc
* Starts making an audio recording of the audio being played in-world (i.e., not local-only audio) to a file in WAV format. * Starts making an audio recording of the audio being played in-world (i.e., not local-only audio) to a file in WAV format.
@ -398,6 +420,23 @@ signals:
* @returns {Signal} * @returns {Signal}
*/ */
void noiseReductionChanged(bool isEnabled); void noiseReductionChanged(bool isEnabled);
/**jsdoc
* Triggered when the audio input noise reduction mode is changed.
* @function Audio.noiseReductionAutomaticChanged
* @param {boolean} isEnabled - <code>true</code> if audio input noise reduction automatic mode is enabled, <code>false</code> if in manual mode.
* @returns {Signal}
*/
void noiseReductionAutomaticChanged(bool isEnabled);
/**jsdoc
* Triggered when the audio input noise reduction threshold is changed.
* @function Audio.noiseReductionThresholdChanged
* @param {number} threshold - The threshold for the audio input noise reduction, range <code>0.0</code> (open the gate completely) &ndash; <code>1.0</code>
* (close the gate completely).
* @returns {Signal}
*/
void noiseReductionThresholdChanged(float threshold);
/**jsdoc /**jsdoc
* Triggered when "warn when muted" is enabled or disabled. * Triggered when "warn when muted" is enabled or disabled.
@ -512,6 +551,7 @@ public slots:
private slots: private slots:
void setMuted(bool muted); void setMuted(bool muted);
void enableNoiseReduction(bool enable); void enableNoiseReduction(bool enable);
void enableNoiseReductionAutomatic(bool enable);
void enableWarnWhenMuted(bool enable); void enableWarnWhenMuted(bool enable);
void enableAcousticEchoCancellation(bool enable); void enableAcousticEchoCancellation(bool enable);
void setInputVolume(float volume); void setInputVolume(float volume);
@ -533,8 +573,10 @@ private:
float _localInjectorGain { 0.0f }; // in dB float _localInjectorGain { 0.0f }; // in dB
float _systemInjectorGain { 0.0f }; // in dB float _systemInjectorGain { 0.0f }; // in dB
float _pttOutputGainDesktop { 0.0f }; // in dB float _pttOutputGainDesktop { 0.0f }; // in dB
float _noiseReductionThreshold { 0.1f }; // Match default value of AudioClient::_noiseReductionThreshold.
bool _isClipping { false }; bool _isClipping { false };
bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled. bool _enableNoiseReduction { true }; // Match default value of AudioClient::_isNoiseGateEnabled.
bool _noiseReductionAutomatic { true }; // Match default value of AudioClient::_noiseReductionAutomatic.
bool _enableWarnWhenMuted { true }; bool _enableWarnWhenMuted { true };
bool _enableAcousticEchoCancellation { true }; // AudioClient::_isAECEnabled bool _enableAcousticEchoCancellation { true }; // AudioClient::_isAECEnabled
bool _contextIsHMD { false }; bool _contextIsHMD { false };

View file

@ -1266,7 +1266,7 @@ void AudioClient::processWebrtcNearEnd(int16_t* samples, int numFrames, int numC
void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
// If there is server echo, reverb will be applied to the recieved audio stream so no need to have it here. // If there is server echo, reverb will be applied to the recieved audio stream so no need to have it here.
bool hasReverb = _reverb || _receivedAudioStream.hasReverb(); bool hasReverb = _reverb || _receivedAudioStream.hasReverb();
if ((_muted && !_shouldEchoLocally) || !_audioOutput || (!_shouldEchoLocally && !hasReverb)) { if ((_muted && !_shouldEchoLocally) || !_audioOutput || (!_shouldEchoLocally && !hasReverb) || !_audioGateOpen) {
return; return;
} }
@ -1342,6 +1342,13 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
} }
} }
float AudioClient::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);
}
void AudioClient::handleAudioInput(QByteArray& audioBuffer) { void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
if (!_audioPaused) { if (!_audioPaused) {
@ -1352,9 +1359,14 @@ void AudioClient::handleAudioInput(QByteArray& audioBuffer) {
int numSamples = audioBuffer.size() / AudioConstants::SAMPLE_SIZE; int numSamples = audioBuffer.size() / AudioConstants::SAMPLE_SIZE;
int numFrames = numSamples / (_isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO); int numFrames = numSamples / (_isStereoInput ? AudioConstants::STEREO : AudioConstants::MONO);
if (_isNoiseGateEnabled) { if (_isNoiseGateEnabled && _isNoiseReductionAutomatic) {
// The audio gate includes DC removal // The audio gate includes DC removal
audioGateOpen = _audioGate->render(samples, samples, numFrames); audioGateOpen = _audioGate->render(samples, samples, numFrames);
} else if (_isNoiseGateEnabled && !_isNoiseReductionAutomatic &&
loudnessToLevel(_lastSmoothedRawInputLoudness) >= _noiseReductionThreshold) {
audioGateOpen = _audioGate->removeDC(samples, samples, numFrames);
} else if (_isNoiseGateEnabled && !_isNoiseReductionAutomatic) {
audioGateOpen = false;
} else { } else {
audioGateOpen = _audioGate->removeDC(samples, samples, numFrames); audioGateOpen = _audioGate->removeDC(samples, samples, numFrames);
} }
@ -1750,6 +1762,24 @@ void AudioClient::setNoiseReduction(bool enable, bool emitSignal) {
} }
} }
void AudioClient::setNoiseReductionAutomatic(bool enable, bool emitSignal) {
if (_isNoiseReductionAutomatic != enable) {
_isNoiseReductionAutomatic = enable;
if (emitSignal) {
emit noiseReductionAutomaticChanged(_isNoiseReductionAutomatic);
}
}
}
void AudioClient::setNoiseReductionThreshold(float threshold, bool emitSignal) {
if (_noiseReductionThreshold != threshold) {
_noiseReductionThreshold = threshold;
if (emitSignal) {
emit noiseReductionThresholdChanged(_noiseReductionThreshold);
}
}
}
void AudioClient::setWarnWhenMuted(bool enable, bool emitSignal) { void AudioClient::setWarnWhenMuted(bool enable, bool emitSignal) {
if (_warnWhenMuted != enable) { if (_warnWhenMuted != enable) {
_warnWhenMuted = enable; _warnWhenMuted = enable;

View file

@ -217,6 +217,12 @@ public slots:
void setNoiseReduction(bool isNoiseGateEnabled, bool emitSignal = true); void setNoiseReduction(bool isNoiseGateEnabled, bool emitSignal = true);
bool isNoiseReductionEnabled() const { return _isNoiseGateEnabled; } bool isNoiseReductionEnabled() const { return _isNoiseGateEnabled; }
void setNoiseReductionAutomatic(bool isNoiseGateAutomatic, bool emitSignal = true);
bool isNoiseReductionAutomatic() const { return _isNoiseReductionAutomatic; }
void setNoiseReductionThreshold(float noiseReductionThreshold, bool emitSignal = true);
float noiseReductionThreshold() const { return _noiseReductionThreshold; }
void setWarnWhenMuted(bool isNoiseGateEnabled, bool emitSignal = true); void setWarnWhenMuted(bool isNoiseGateEnabled, bool emitSignal = true);
bool isWarnWhenMutedEnabled() const { return _warnWhenMuted; } bool isWarnWhenMutedEnabled() const { return _warnWhenMuted; }
@ -265,6 +271,8 @@ signals:
void inputVolumeChanged(float volume); void inputVolumeChanged(float volume);
void muteToggled(bool muted); void muteToggled(bool muted);
void noiseReductionChanged(bool noiseReductionEnabled); void noiseReductionChanged(bool noiseReductionEnabled);
void noiseReductionAutomaticChanged(bool noiseReductionAutomatic);
void noiseReductionThresholdChanged(bool noiseReductionThreshold);
void warnWhenMutedChanged(bool warnWhenMutedEnabled); void warnWhenMutedChanged(bool warnWhenMutedEnabled);
void acousticEchoCancellationChanged(bool acousticEchoCancellationEnabled); void acousticEchoCancellationChanged(bool acousticEchoCancellationEnabled);
void mutedByMixer(); void mutedByMixer();
@ -310,6 +318,8 @@ private:
friend class CheckDevicesThread; friend class CheckDevicesThread;
friend class LocalInjectorsThread; friend class LocalInjectorsThread;
float loudnessToLevel(float loudness);
// background tasks // background tasks
void checkDevices(); void checkDevices();
void checkPeakValues(); void checkPeakValues();
@ -397,6 +407,8 @@ private:
bool _shouldEchoLocally{ false }; bool _shouldEchoLocally{ false };
bool _shouldEchoToServer{ false }; bool _shouldEchoToServer{ false };
bool _isNoiseGateEnabled{ true }; bool _isNoiseGateEnabled{ true };
bool _isNoiseReductionAutomatic{ true };
float _noiseReductionThreshold{ 0.1f };
bool _warnWhenMuted; bool _warnWhenMuted;
bool _isAECEnabled{ true }; bool _isAECEnabled{ true };