Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#35 WIP Updated AM decoder to adjust demodulator gain and implemented…
… new Gain and DC removal control that attempts to normalize audio gain toward an objective value.
- Loading branch information
Dennis Sheirer
committed
May 2, 2023
1 parent
4147f75
commit 749613c
Showing
7 changed files
with
170 additions
and
141 deletions.
There are no files selected for viewing
140 changes: 140 additions & 0 deletions
140
src/main/java/io/github/dsheirer/dsp/gain/AudioGainAndDcFilter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
/* | ||
* ***************************************************************************** | ||
* Copyright (C) 2014-2023 Dennis Sheirer | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU General Public License | ||
* along with this program. If not, see <http://www.gnu.org/licenses/> | ||
* **************************************************************************** | ||
*/ | ||
|
||
package io.github.dsheirer.dsp.gain; | ||
|
||
import org.apache.commons.math3.util.FastMath; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
/** | ||
* Audio gain that normalizes audio amplitude against an objective amplitude value where the audio amplitude values | ||
* normally fall in the range of -1.0 to 1.0. | ||
* | ||
* Removes DC bias on a per audio sample buffer basis. | ||
* | ||
* This control is designed to work on a per-call basis by monitoring the maximum observed amplitude and adjusting the | ||
* gain to normalize the amplitude toward the objective amplitude value. This control is designed to be reset at the | ||
* beginning/end of each call segment so that the max observed amplitude is relevant to the current call segment. | ||
* | ||
* Min and max gains should be sized to constrain the applied value during periods of extended silence at the | ||
* beginning of an audio segment or where a single amplitude spike might erroneously affect gain across the segment. | ||
*/ | ||
public class AudioGainAndDcFilter | ||
{ | ||
private static final Logger mLog = LoggerFactory.getLogger(AudioGainAndDcFilter.class); | ||
private float mMinGain; | ||
private float mMaxGain; | ||
private float mCurrentGain; | ||
private float mObjectiveGain; | ||
private float mObjectiveAmplitude; | ||
private float mMaxObservedAmplitude; | ||
private static float MAX_AMPLITUDE = 0.95f; | ||
//Stabilizes gain after 2x buffers of 2048 samples | ||
private static final float GAIN_LOOP_BANDWIDTH = 0.0015f; | ||
|
||
/** | ||
* Constructs an instance | ||
* @param minGain to apply to the incoming sample stream. | ||
* @param maxGain to apply | ||
* @param objectiveAmplitude to achieve by adjusting gain between min and max. | ||
*/ | ||
public AudioGainAndDcFilter(float minGain, float maxGain, float objectiveAmplitude) | ||
{ | ||
mMinGain = minGain; | ||
mMaxGain = maxGain; | ||
mObjectiveAmplitude = objectiveAmplitude; | ||
reset(); | ||
} | ||
|
||
/** | ||
* Resets this gain control to prepare for the next audio call/segment. | ||
*/ | ||
public void reset() | ||
{ | ||
mMaxObservedAmplitude = 0.0f; | ||
mObjectiveGain = 1.0f; | ||
mCurrentGain = 1.0f; | ||
} | ||
|
||
/** | ||
* Process a buffer of audio samples and apply gain. | ||
* @param samples to adjust. | ||
* @return amplified audio samples | ||
*/ | ||
public float[] process(float[] samples) | ||
{ | ||
float currentAmplitude; | ||
float dcAccumulator = 0.0f; | ||
|
||
//Decay the max observed value by 10% each buffer so that an initial spike doesn't carry across all buffers | ||
mMaxObservedAmplitude *= 0.9f; | ||
|
||
for(float sample: samples) | ||
{ | ||
dcAccumulator += sample; | ||
currentAmplitude = FastMath.min(Math.abs(sample), 1.2f); | ||
|
||
if(currentAmplitude > mMaxObservedAmplitude) | ||
{ | ||
mMaxObservedAmplitude = currentAmplitude; | ||
} | ||
} | ||
|
||
mObjectiveGain = mObjectiveAmplitude / mMaxObservedAmplitude; | ||
|
||
if(mObjectiveGain > mMaxGain) | ||
{ | ||
mObjectiveGain = mMaxGain; | ||
} | ||
else if(mObjectiveGain < mMinGain) | ||
{ | ||
mObjectiveGain = mMinGain; | ||
} | ||
|
||
float dcOffset = dcAccumulator / samples.length; | ||
float gain = mCurrentGain; | ||
float objective = mObjectiveGain; | ||
float[] processed = new float[samples.length]; | ||
boolean objectiveAchieved = (gain == objective); | ||
float amplified; | ||
|
||
for(int x = 0; x < samples.length; x++) | ||
{ | ||
if(!objectiveAchieved) | ||
{ | ||
gain += ((objective - gain) * GAIN_LOOP_BANDWIDTH); | ||
if(Math.abs(objective - gain) < 0.00005f) | ||
{ | ||
gain = objective; | ||
objectiveAchieved = true; | ||
} | ||
} | ||
|
||
amplified = (samples[x] - dcOffset) * gain; | ||
amplified = FastMath.min(amplified, MAX_AMPLITUDE); | ||
amplified = FastMath.max(amplified, -MAX_AMPLITUDE); | ||
processed[x] = amplified; | ||
} | ||
|
||
mCurrentGain = gain; | ||
mObjectiveGain = objective; | ||
return processed; | ||
} | ||
} |
94 changes: 0 additions & 94 deletions
94
src/main/java/io/github/dsheirer/dsp/gain/ObjectiveGainControl.java
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.