gaincontrol.cpp 4.01 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
/*
 *  Copyright (C) 2004-2012 Savoir-Faire Linux Inc.
 *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
 *
 *  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, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
 */

31 32
#include <cmath>
#include <climits>
33 34
#include <fstream>

35
#include "sfl_types.h"
36
#include "logger.h"
37 38
#include "gaincontrol.h"

39 40
#define SFL_GAIN_ATTACK_TIME 10
#define SFL_GAIN_RELEASE_TIME 300
41 42

#define SFL_GAIN_LIMITER_RATIO 0.1
43
#define SFL_GAIN_LIMITER_THRESHOLD 0.8
44

45 46
#define SFL_GAIN_LOGe10  2.30258509299404568402

47 48 49 50 51 52 53 54
GainControl::GainControl(double sr, double target) : averager_(sr, SFL_GAIN_ATTACK_TIME, SFL_GAIN_RELEASE_TIME)
    , limiter_(SFL_GAIN_LIMITER_RATIO, SFL_GAIN_LIMITER_THRESHOLD)
    , targetLeveldB_(target)
    , targetLevelLinear_(exp(targetLeveldB_ * 0.05 * SFL_GAIN_LOGe10))
    , currentGain_(1.0)
    , previousGain_(0.0)
    , maxIncreaseStep_(exp(0.11513 * 12. * 160 / 8000)) // Computed on 12 frames (240 ms)
    , maxDecreaseStep_(exp(-0.11513 * 40. * 160 / 8000))// Computed on 40 frames (800 ms)
55
{
56
    DEBUG("Target gain %f dB (%f linear)", targetLeveldB_, targetLevelLinear_);
57
}
58

59
void GainControl::process(SFLDataFormat *buf, int samples)
60
{
61
    double rms, rmsAvgLevel, in, out, diffRms, maxRms;
62

63
    maxRms = 0.0;
64 65

    for (int i = 0; i < samples; i++) {
66
        // linear conversion
67 68
        in = (double)buf[i] / (double)SHRT_MAX;

69
        out = currentGain_ * in;
70

71 72
        rms = out * out;
        rmsAvgLevel = sqrt(averager_.getAverage(rms));
73

74
        if (rmsAvgLevel > maxRms)
75
            maxRms = rmsAvgLevel;
76

77
        out = limiter_.limit(out);
78 79

        buf[i] = (short)(out * (double)SHRT_MAX);
80
    }
81

82
    diffRms = maxRms - targetLevelLinear_;
83

84 85 86 87 88 89
    if ((diffRms > 0.0) && (maxRms > 0.1))
        currentGain_ *= maxDecreaseStep_;
    else if ((diffRms <= 0.0) && (maxRms > 0.1))
        currentGain_ *= maxIncreaseStep_;
    else if (maxRms <= 0.1)
        currentGain_ = 1.0;
90

91
    currentGain_ = 0.5 * (currentGain_ + previousGain_);
92

93
    previousGain_ = currentGain_;
94 95
}

96
GainControl::DetectionAverage::DetectionAverage(double sr, double ta, double tr) :
97
    g_a_(0.0), teta_a_(ta), g_r_(0.0), teta_r_(tr), samplingRate_(sr), previous_y_(0.0)
98
{
99 100
    g_a_ = exp(-1.0 / (samplingRate_ * (teta_a_ / 1000.0)));
    g_r_ = exp(-1.0 / (samplingRate_ * (teta_r_ / 1000.0)));
101 102 103 104
}

double GainControl::DetectionAverage::getAverage(double in)
{
105 106 107 108 109
    if (in > previous_y_)
        previous_y_ = ((1.0 - g_a_) * in) + (g_a_ * previous_y_);
    else
        previous_y_ = ((1.0 - g_r_) * in) + (g_r_ * previous_y_);
    return previous_y_;
110 111
}

112 113
GainControl::Limiter::Limiter(double r, double thresh) : ratio_(r), threshold_(thresh)
{}
114

115
double GainControl::Limiter::limit(double in) const
116
{
117 118
    double out = (in > threshold_ ? (ratio_ * (in - threshold_)) + threshold_ :
           in < -threshold_ ? (ratio_ * (in + threshold_)) - threshold_ : in);
119 120 121

    return out;
}