mirror of
https://github.com/HifiExperiments/overte.git
synced 2025-08-10 01:36:26 +02:00
move the audio noise gate to its own class
This commit is contained in:
parent
0429b338c0
commit
be184719b5
4 changed files with 217 additions and 157 deletions
|
@ -46,8 +46,6 @@
|
||||||
|
|
||||||
#include "Audio.h"
|
#include "Audio.h"
|
||||||
|
|
||||||
static const int NUMBER_OF_NOISE_SAMPLE_FRAMES = 300;
|
|
||||||
|
|
||||||
static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 100;
|
static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 100;
|
||||||
|
|
||||||
Audio::Audio() :
|
Audio::Audio() :
|
||||||
|
@ -68,21 +66,11 @@ Audio::Audio() :
|
||||||
_isStereoInput(false),
|
_isStereoInput(false),
|
||||||
_averagedLatency(0.0),
|
_averagedLatency(0.0),
|
||||||
_lastInputLoudness(0),
|
_lastInputLoudness(0),
|
||||||
_inputFrameCounter(0),
|
|
||||||
_quietestFrame(std::numeric_limits<float>::max()),
|
|
||||||
_loudestFrame(0.0f),
|
|
||||||
_timeSinceLastClip(-1.0),
|
|
||||||
_dcOffset(0),
|
|
||||||
_noiseGateMeasuredFloor(0),
|
|
||||||
_noiseGateSampleCounter(0),
|
|
||||||
_noiseGateOpen(false),
|
|
||||||
_noiseGateEnabled(true),
|
|
||||||
_audioSourceInjectEnabled(false),
|
|
||||||
_noiseGateFramesToClose(0),
|
|
||||||
_totalInputAudioSamples(0),
|
|
||||||
_muted(false),
|
_muted(false),
|
||||||
_shouldEchoLocally(false),
|
_shouldEchoLocally(false),
|
||||||
_shouldEchoToServer(false),
|
_shouldEchoToServer(false),
|
||||||
|
_isNoiseGateEnabled(true),
|
||||||
|
_audioSourceInjectEnabled(false),
|
||||||
_reverb(false),
|
_reverb(false),
|
||||||
_reverbOptions(&_scriptReverbOptions),
|
_reverbOptions(&_scriptReverbOptions),
|
||||||
_gverbLocal(NULL),
|
_gverbLocal(NULL),
|
||||||
|
@ -91,12 +79,11 @@ Audio::Audio() :
|
||||||
_toneSourceEnabled(true),
|
_toneSourceEnabled(true),
|
||||||
_outgoingAvatarAudioSequenceNumber(0),
|
_outgoingAvatarAudioSequenceNumber(0),
|
||||||
_audioOutputIODevice(_receivedAudioStream),
|
_audioOutputIODevice(_receivedAudioStream),
|
||||||
_stats(&_receivedAudioStream)
|
_stats(&_receivedAudioStream),
|
||||||
|
_inputGate()
|
||||||
{
|
{
|
||||||
// clear the array of locally injected samples
|
// clear the array of locally injected samples
|
||||||
memset(_localProceduralSamples, 0, AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL);
|
memset(_localProceduralSamples, 0, AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL);
|
||||||
// Create the noise sample array
|
|
||||||
_noiseSampleFrames = new float[NUMBER_OF_NOISE_SAMPLE_FRAMES];
|
|
||||||
|
|
||||||
connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples, this, &Audio::processReceivedSamples, Qt::DirectConnection);
|
connect(&_receivedAudioStream, &MixedProcessedAudioStream::processSamples, this, &Audio::processReceivedSamples, Qt::DirectConnection);
|
||||||
|
|
||||||
|
@ -586,6 +573,8 @@ void Audio::handleLocalEchoAndReverb(QByteArray& inputByteArray) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const float CLIPPING_THRESHOLD = 0.90f;
|
||||||
|
|
||||||
void Audio::handleAudioInput() {
|
void Audio::handleAudioInput() {
|
||||||
static char audioDataPacket[MAX_PACKET_SIZE];
|
static char audioDataPacket[MAX_PACKET_SIZE];
|
||||||
|
|
||||||
|
@ -655,131 +644,24 @@ void Audio::handleAudioInput() {
|
||||||
inputSamplesRequired, numNetworkSamples,
|
inputSamplesRequired, numNetworkSamples,
|
||||||
_inputFormat, _desiredInputFormat);
|
_inputFormat, _desiredInputFormat);
|
||||||
|
|
||||||
// only impose the noise gate and perform tone injection if we sending mono audio
|
// only impose the noise gate and perform tone injection if we are sending mono audio
|
||||||
if (!_isStereoInput) {
|
if (!_isStereoInput && _isNoiseGateEnabled) {
|
||||||
|
_inputGate.gateSamples(networkAudioSamples, numNetworkSamples);
|
||||||
//
|
_lastInputLoudness = _inputGate.getLastLoudness();
|
||||||
// Impose Noise Gate
|
_timeSinceLastClip = _inputGate.getTimeSinceLastClip();
|
||||||
//
|
|
||||||
// The Noise Gate is used to reject constant background noise by measuring the noise
|
|
||||||
// floor observed at the microphone and then opening the 'gate' to allow microphone
|
|
||||||
// signals to be transmitted when the microphone samples average level exceeds a multiple
|
|
||||||
// of the noise floor.
|
|
||||||
//
|
|
||||||
// NOISE_GATE_HEIGHT: How loud you have to speak relative to noise background to open the gate.
|
|
||||||
// Make this value lower for more sensitivity and less rejection of noise.
|
|
||||||
// NOISE_GATE_WIDTH: The number of samples in an audio frame for which the height must be exceeded
|
|
||||||
// to open the gate.
|
|
||||||
// NOISE_GATE_CLOSE_FRAME_DELAY: Once the noise is below the gate height for the frame, how many frames
|
|
||||||
// will we wait before closing the gate.
|
|
||||||
// NOISE_GATE_FRAMES_TO_AVERAGE: How many audio frames should we average together to compute noise floor.
|
|
||||||
// More means better rejection but also can reject continuous things like singing.
|
|
||||||
// NUMBER_OF_NOISE_SAMPLE_FRAMES: How often should we re-evaluate the noise floor?
|
|
||||||
|
|
||||||
|
|
||||||
float loudness = 0;
|
|
||||||
float thisSample = 0;
|
|
||||||
int samplesOverNoiseGate = 0;
|
|
||||||
|
|
||||||
const float NOISE_GATE_HEIGHT = 7.0f;
|
|
||||||
const int NOISE_GATE_WIDTH = 5;
|
|
||||||
const int NOISE_GATE_CLOSE_FRAME_DELAY = 5;
|
|
||||||
const int NOISE_GATE_FRAMES_TO_AVERAGE = 5;
|
|
||||||
const float DC_OFFSET_AVERAGING = 0.99f;
|
|
||||||
const float CLIPPING_THRESHOLD = 0.90f;
|
|
||||||
|
|
||||||
//
|
|
||||||
// Check clipping, adjust DC offset, and check if should open noise gate
|
|
||||||
//
|
|
||||||
float measuredDcOffset = 0.0f;
|
|
||||||
// Increment the time since the last clip
|
|
||||||
if (_timeSinceLastClip >= 0.0f) {
|
|
||||||
_timeSinceLastClip += (float) AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL
|
|
||||||
/ (float) AudioConstants::SAMPLE_RATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; i++) {
|
|
||||||
measuredDcOffset += networkAudioSamples[i];
|
|
||||||
networkAudioSamples[i] -= (int16_t) _dcOffset;
|
|
||||||
thisSample = fabsf(networkAudioSamples[i]);
|
|
||||||
if (thisSample >= ((float)AudioConstants::MAX_SAMPLE_VALUE * CLIPPING_THRESHOLD)) {
|
|
||||||
_timeSinceLastClip = 0.0f;
|
|
||||||
}
|
|
||||||
loudness += thisSample;
|
|
||||||
// Noise Reduction: Count peaks above the average loudness
|
|
||||||
if (_noiseGateEnabled && (thisSample > (_noiseGateMeasuredFloor * NOISE_GATE_HEIGHT))) {
|
|
||||||
samplesOverNoiseGate++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
measuredDcOffset /= AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL;
|
|
||||||
if (_dcOffset == 0.0f) {
|
|
||||||
// On first frame, copy over measured offset
|
|
||||||
_dcOffset = measuredDcOffset;
|
|
||||||
} else {
|
|
||||||
_dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastInputLoudness = fabs(loudness / AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
|
||||||
|
|
||||||
if (_quietestFrame > _lastInputLoudness) {
|
|
||||||
_quietestFrame = _lastInputLoudness;
|
|
||||||
}
|
|
||||||
if (_loudestFrame < _lastInputLoudness) {
|
|
||||||
_loudestFrame = _lastInputLoudness;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int FRAMES_FOR_NOISE_DETECTION = 400;
|
|
||||||
if (_inputFrameCounter++ > FRAMES_FOR_NOISE_DETECTION) {
|
|
||||||
_quietestFrame = std::numeric_limits<float>::max();
|
|
||||||
_loudestFrame = 0.0f;
|
|
||||||
_inputFrameCounter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If Noise Gate is enabled, check and turn the gate on and off
|
|
||||||
if (!_audioSourceInjectEnabled && _noiseGateEnabled) {
|
|
||||||
float averageOfAllSampleFrames = 0.0f;
|
|
||||||
_noiseSampleFrames[_noiseGateSampleCounter++] = _lastInputLoudness;
|
|
||||||
if (_noiseGateSampleCounter == NUMBER_OF_NOISE_SAMPLE_FRAMES) {
|
|
||||||
float smallestSample = FLT_MAX;
|
|
||||||
for (int i = 0; i <= NUMBER_OF_NOISE_SAMPLE_FRAMES - NOISE_GATE_FRAMES_TO_AVERAGE; i += NOISE_GATE_FRAMES_TO_AVERAGE) {
|
|
||||||
float thisAverage = 0.0f;
|
|
||||||
for (int j = i; j < i + NOISE_GATE_FRAMES_TO_AVERAGE; j++) {
|
|
||||||
thisAverage += _noiseSampleFrames[j];
|
|
||||||
averageOfAllSampleFrames += _noiseSampleFrames[j];
|
|
||||||
}
|
|
||||||
thisAverage /= NOISE_GATE_FRAMES_TO_AVERAGE;
|
|
||||||
|
|
||||||
if (thisAverage < smallestSample) {
|
|
||||||
smallestSample = thisAverage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
averageOfAllSampleFrames /= NUMBER_OF_NOISE_SAMPLE_FRAMES;
|
|
||||||
_noiseGateMeasuredFloor = smallestSample;
|
|
||||||
_noiseGateSampleCounter = 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
if (samplesOverNoiseGate > NOISE_GATE_WIDTH) {
|
|
||||||
_noiseGateOpen = true;
|
|
||||||
_noiseGateFramesToClose = NOISE_GATE_CLOSE_FRAME_DELAY;
|
|
||||||
} else {
|
|
||||||
if (--_noiseGateFramesToClose == 0) {
|
|
||||||
_noiseGateOpen = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!_noiseGateOpen) {
|
|
||||||
memset(networkAudioSamples, 0, AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL);
|
|
||||||
_lastInputLoudness = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
float loudness = 0.0f;
|
float loudness = 0.0f;
|
||||||
|
|
||||||
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_STEREO; i++) {
|
for (int i = 0; i < numNetworkSamples; i++) {
|
||||||
loudness += fabsf(networkAudioSamples[i]);
|
float thisSample = fabsf(networkAudioSamples[i]);
|
||||||
|
loudness += thisSample;
|
||||||
|
|
||||||
|
if (thisSample > (AudioConstants::MAX_SAMPLE_VALUE * AudioNoiseGate::CLIPPING_THRESHOLD)) {
|
||||||
|
_timeSinceLastClip = 0.0f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_lastInputLoudness = fabs(loudness / AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
_lastInputLoudness = fabs(loudness / numNetworkSamples);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// our input loudness is 0, since we're muted
|
// our input loudness is 0, since we're muted
|
||||||
|
@ -943,10 +825,6 @@ void Audio::toggleMute() {
|
||||||
muteToggled();
|
muteToggled();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Audio::toggleAudioNoiseReduction() {
|
|
||||||
_noiseGateEnabled = !_noiseGateEnabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Audio::setIsStereoInput(bool isStereoInput) {
|
void Audio::setIsStereoInput(bool isStereoInput) {
|
||||||
if (isStereoInput != _isStereoInput) {
|
if (isStereoInput != _isStereoInput) {
|
||||||
_isStereoInput = isStereoInput;
|
_isStereoInput = isStereoInput;
|
||||||
|
|
|
@ -32,6 +32,7 @@
|
||||||
|
|
||||||
#include "InterfaceConfig.h"
|
#include "InterfaceConfig.h"
|
||||||
#include "audio/AudioIOStats.h"
|
#include "audio/AudioIOStats.h"
|
||||||
|
#include "audio/AudioNoiseGate.h"
|
||||||
#include "AudioStreamStats.h"
|
#include "AudioStreamStats.h"
|
||||||
#include "Recorder.h"
|
#include "Recorder.h"
|
||||||
#include "RingBufferHistory.h"
|
#include "RingBufferHistory.h"
|
||||||
|
@ -82,12 +83,10 @@ public:
|
||||||
|
|
||||||
const MixedProcessedAudioStream& getReceivedAudioStream() const { return _receivedAudioStream; }
|
const MixedProcessedAudioStream& getReceivedAudioStream() const { return _receivedAudioStream; }
|
||||||
|
|
||||||
float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _noiseGateMeasuredFloor, 0.0f); }
|
float getLastInputLoudness() const { return glm::max(_lastInputLoudness - _inputGate.getMeasuredFloor(), 0.0f); }
|
||||||
float getTimeSinceLastClip() const { return _timeSinceLastClip; }
|
float getTimeSinceLastClip() const { return _timeSinceLastClip; }
|
||||||
float getAudioAverageInputLoudness() const { return _lastInputLoudness; }
|
float getAudioAverageInputLoudness() const { return _lastInputLoudness; }
|
||||||
|
|
||||||
void setNoiseGateEnabled(bool noiseGateEnabled) { _noiseGateEnabled = noiseGateEnabled; }
|
|
||||||
|
|
||||||
void setReceivedAudioStreamSettings(const InboundAudioStream::Settings& settings) { _receivedAudioStream.setSettings(settings); }
|
void setReceivedAudioStreamSettings(const InboundAudioStream::Settings& settings) { _receivedAudioStream.setSettings(settings); }
|
||||||
|
|
||||||
int getDesiredJitterBufferFrames() const { return _receivedAudioStream.getDesiredJitterBufferFrames(); }
|
int getDesiredJitterBufferFrames() const { return _receivedAudioStream.getDesiredJitterBufferFrames(); }
|
||||||
|
@ -116,11 +115,13 @@ public slots:
|
||||||
void reset();
|
void reset();
|
||||||
void audioMixerKilled();
|
void audioMixerKilled();
|
||||||
void toggleMute();
|
void toggleMute();
|
||||||
void toggleAudioNoiseReduction();
|
|
||||||
void toggleAudioSourceInject();
|
void toggleAudioSourceInject();
|
||||||
void selectAudioSourcePinkNoise();
|
void selectAudioSourcePinkNoise();
|
||||||
void selectAudioSourceSine440();
|
void selectAudioSourceSine440();
|
||||||
|
|
||||||
|
void toggleAudioNoiseReduction() { _isNoiseGateEnabled = !_isNoiseGateEnabled; }
|
||||||
|
|
||||||
void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; }
|
void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; }
|
||||||
void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; }
|
void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; }
|
||||||
|
|
||||||
|
@ -179,24 +180,14 @@ private:
|
||||||
QElapsedTimer _timeSinceLastReceived;
|
QElapsedTimer _timeSinceLastReceived;
|
||||||
float _averagedLatency;
|
float _averagedLatency;
|
||||||
float _lastInputLoudness;
|
float _lastInputLoudness;
|
||||||
int _inputFrameCounter;
|
|
||||||
float _quietestFrame;
|
|
||||||
float _loudestFrame;
|
|
||||||
float _timeSinceLastClip;
|
float _timeSinceLastClip;
|
||||||
float _dcOffset;
|
|
||||||
float _noiseGateMeasuredFloor;
|
|
||||||
float* _noiseSampleFrames;
|
|
||||||
int _noiseGateSampleCounter;
|
|
||||||
bool _noiseGateOpen;
|
|
||||||
bool _noiseGateEnabled;
|
|
||||||
bool _audioSourceInjectEnabled;
|
|
||||||
|
|
||||||
int _noiseGateFramesToClose;
|
|
||||||
int _totalInputAudioSamples;
|
int _totalInputAudioSamples;
|
||||||
|
|
||||||
bool _muted;
|
bool _muted;
|
||||||
bool _shouldEchoLocally;
|
bool _shouldEchoLocally;
|
||||||
bool _shouldEchoToServer;
|
bool _shouldEchoToServer;
|
||||||
|
bool _isNoiseGateEnabled;
|
||||||
|
bool _audioSourceInjectEnabled;
|
||||||
|
|
||||||
bool _reverb;
|
bool _reverb;
|
||||||
AudioEffectOptions _scriptReverbOptions;
|
AudioEffectOptions _scriptReverbOptions;
|
||||||
|
@ -244,6 +235,8 @@ private:
|
||||||
WeakRecorderPointer _recorder;
|
WeakRecorderPointer _recorder;
|
||||||
|
|
||||||
AudioIOStats _stats;
|
AudioIOStats _stats;
|
||||||
|
|
||||||
|
AudioNoiseGate _inputGate;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
145
interface/src/audio/AudioNoiseGate.cpp
Normal file
145
interface/src/audio/AudioNoiseGate.cpp
Normal file
|
@ -0,0 +1,145 @@
|
||||||
|
//
|
||||||
|
// AudioNoiseGate.cpp
|
||||||
|
// interface/src/audio
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2014-12-16.
|
||||||
|
// Copyright 2014 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 <float.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <AudioConstants.h>
|
||||||
|
|
||||||
|
#include "AudioNoiseGate.h"
|
||||||
|
|
||||||
|
const int NUMBER_OF_NOISE_SAMPLE_FRAMES = 300;
|
||||||
|
const float AudioNoiseGate::CLIPPING_THRESHOLD = 0.90f;
|
||||||
|
|
||||||
|
AudioNoiseGate::AudioNoiseGate() {
|
||||||
|
// Create the noise sample array
|
||||||
|
_sampleFrames = new float[NUMBER_OF_NOISE_SAMPLE_FRAMES];
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioNoiseGate::~AudioNoiseGate() {
|
||||||
|
delete[] _sampleFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioNoiseGate::gateSamples(int16_t* samples, int numSamples) {
|
||||||
|
//
|
||||||
|
// Impose Noise Gate
|
||||||
|
//
|
||||||
|
// The Noise Gate is used to reject constant background noise by measuring the noise
|
||||||
|
// floor observed at the microphone and then opening the 'gate' to allow microphone
|
||||||
|
// signals to be transmitted when the microphone samples average level exceeds a multiple
|
||||||
|
// of the noise floor.
|
||||||
|
//
|
||||||
|
// NOISE_GATE_HEIGHT: How loud you have to speak relative to noise background to open the gate.
|
||||||
|
// Make this value lower for more sensitivity and less rejection of noise.
|
||||||
|
// NOISE_GATE_WIDTH: The number of samples in an audio frame for which the height must be exceeded
|
||||||
|
// to open the gate.
|
||||||
|
// NOISE_GATE_CLOSE_FRAME_DELAY: Once the noise is below the gate height for the frame, how many frames
|
||||||
|
// will we wait before closing the gate.
|
||||||
|
// NOISE_GATE_FRAMES_TO_AVERAGE: How many audio frames should we average together to compute noise floor.
|
||||||
|
// More means better rejection but also can reject continuous things like singing.
|
||||||
|
// NUMBER_OF_NOISE_SAMPLE_FRAMES: How often should we re-evaluate the noise floor?
|
||||||
|
|
||||||
|
|
||||||
|
float loudness = 0;
|
||||||
|
float thisSample = 0;
|
||||||
|
int samplesOverNoiseGate = 0;
|
||||||
|
|
||||||
|
const float NOISE_GATE_HEIGHT = 7.0f;
|
||||||
|
const int NOISE_GATE_WIDTH = 5;
|
||||||
|
const int NOISE_GATE_CLOSE_FRAME_DELAY = 5;
|
||||||
|
const int NOISE_GATE_FRAMES_TO_AVERAGE = 5;
|
||||||
|
const float DC_OFFSET_AVERAGING = 0.99f;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check clipping, adjust DC offset, and check if should open noise gate
|
||||||
|
//
|
||||||
|
float measuredDcOffset = 0.0f;
|
||||||
|
// Increment the time since the last clip
|
||||||
|
if (_timeSinceLastClip >= 0.0f) {
|
||||||
|
_timeSinceLastClip += (float) AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL
|
||||||
|
/ (float) AudioConstants::SAMPLE_RATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL; i++) {
|
||||||
|
measuredDcOffset += samples[i];
|
||||||
|
samples[i] -= (int16_t) _dcOffset;
|
||||||
|
thisSample = fabsf(samples[i]);
|
||||||
|
|
||||||
|
if (thisSample >= ((float)AudioConstants::MAX_SAMPLE_VALUE * CLIPPING_THRESHOLD)) {
|
||||||
|
_timeSinceLastClip = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
loudness += thisSample;
|
||||||
|
// Noise Reduction: Count peaks above the average loudness
|
||||||
|
if (thisSample > (_measuredFloor * NOISE_GATE_HEIGHT)) {
|
||||||
|
samplesOverNoiseGate++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
measuredDcOffset /= AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL;
|
||||||
|
if (_dcOffset == 0.0f) {
|
||||||
|
// On first frame, copy over measured offset
|
||||||
|
_dcOffset = measuredDcOffset;
|
||||||
|
} else {
|
||||||
|
_dcOffset = DC_OFFSET_AVERAGING * _dcOffset + (1.0f - DC_OFFSET_AVERAGING) * measuredDcOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastLoudness = fabs(loudness / AudioConstants::NETWORK_FRAME_SAMPLES_PER_CHANNEL);
|
||||||
|
|
||||||
|
if (_quietestFrame > _lastLoudness) {
|
||||||
|
_quietestFrame = _lastLoudness;
|
||||||
|
}
|
||||||
|
if (_loudestFrame < _lastLoudness) {
|
||||||
|
_loudestFrame = _lastLoudness;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int FRAMES_FOR_NOISE_DETECTION = 400;
|
||||||
|
if (_inputFrameCounter++ > FRAMES_FOR_NOISE_DETECTION) {
|
||||||
|
_quietestFrame = std::numeric_limits<float>::max();
|
||||||
|
_loudestFrame = 0.0f;
|
||||||
|
_inputFrameCounter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If Noise Gate is enabled, check and turn the gate on and off
|
||||||
|
float averageOfAllSampleFrames = 0.0f;
|
||||||
|
_sampleFrames[_sampleCounter++] = _lastLoudness;
|
||||||
|
if (_sampleCounter == NUMBER_OF_NOISE_SAMPLE_FRAMES) {
|
||||||
|
float smallestSample = FLT_MAX;
|
||||||
|
for (int i = 0; i <= NUMBER_OF_NOISE_SAMPLE_FRAMES - NOISE_GATE_FRAMES_TO_AVERAGE; i += NOISE_GATE_FRAMES_TO_AVERAGE) {
|
||||||
|
float thisAverage = 0.0f;
|
||||||
|
for (int j = i; j < i + NOISE_GATE_FRAMES_TO_AVERAGE; j++) {
|
||||||
|
thisAverage += _sampleFrames[j];
|
||||||
|
averageOfAllSampleFrames += _sampleFrames[j];
|
||||||
|
}
|
||||||
|
thisAverage /= NOISE_GATE_FRAMES_TO_AVERAGE;
|
||||||
|
|
||||||
|
if (thisAverage < smallestSample) {
|
||||||
|
smallestSample = thisAverage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
averageOfAllSampleFrames /= NUMBER_OF_NOISE_SAMPLE_FRAMES;
|
||||||
|
_measuredFloor = smallestSample;
|
||||||
|
_sampleCounter = 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
if (samplesOverNoiseGate > NOISE_GATE_WIDTH) {
|
||||||
|
_isOpen = true;
|
||||||
|
_framesToClose = NOISE_GATE_CLOSE_FRAME_DELAY;
|
||||||
|
} else {
|
||||||
|
if (--_framesToClose == 0) {
|
||||||
|
_isOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!_isOpen) {
|
||||||
|
memset(samples, 0, AudioConstants::NETWORK_FRAME_BYTES_PER_CHANNEL);
|
||||||
|
_lastLoudness = 0;
|
||||||
|
}
|
||||||
|
}
|
44
interface/src/audio/AudioNoiseGate.h
Normal file
44
interface/src/audio/AudioNoiseGate.h
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
//
|
||||||
|
// AudioNoiseGate.h
|
||||||
|
// interface/src/audio
|
||||||
|
//
|
||||||
|
// Created by Stephen Birarda on 2014-12-16.
|
||||||
|
// Copyright 2014 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
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_AudioNoiseGate_h
|
||||||
|
#define hifi_AudioNoiseGate_h
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
class AudioNoiseGate {
|
||||||
|
public:
|
||||||
|
AudioNoiseGate();
|
||||||
|
~AudioNoiseGate();
|
||||||
|
|
||||||
|
void gateSamples(int16_t* samples, int numSamples);
|
||||||
|
|
||||||
|
float getTimeSinceLastClip() const { return _timeSinceLastClip; }
|
||||||
|
float getMeasuredFloor() const { return _measuredFloor; }
|
||||||
|
float getLastLoudness() const { return _lastLoudness; }
|
||||||
|
|
||||||
|
static const float CLIPPING_THRESHOLD;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int _inputFrameCounter;
|
||||||
|
float _lastLoudness;
|
||||||
|
float _quietestFrame;
|
||||||
|
float _loudestFrame;
|
||||||
|
float _timeSinceLastClip;
|
||||||
|
float _dcOffset;
|
||||||
|
float _measuredFloor;
|
||||||
|
float* _sampleFrames;
|
||||||
|
int _sampleCounter;
|
||||||
|
bool _isOpen;
|
||||||
|
int _framesToClose;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // hifi_AudioNoiseGate_h
|
Loading…
Reference in a new issue