diff options
Diffstat (limited to 'src/GainControl.cpp')
| -rw-r--r-- | src/GainControl.cpp | 369 | 
1 files changed, 369 insertions, 0 deletions
| diff --git a/src/GainControl.cpp b/src/GainControl.cpp new file mode 100644 index 0000000..75d811d --- /dev/null +++ b/src/GainControl.cpp @@ -0,0 +1,369 @@ +/* +   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 +   Her Majesty the Queen in Right of Canada (Communications Research +   Center Canada) + */ +/* +   This file is part of CRC-DADMOD. + +   CRC-DADMOD 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. + +   CRC-DADMOD 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 CRC-DADMOD.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include "GainControl.h" +#include "PcDebug.h" +#include "kiss_fftsimd.h" + +#include <stdio.h> +#include <stdexcept> + + +GainControl::GainControl(size_t framesize, GainMode mode, float factor) : +    ModCodec(ModFormat(framesize * sizeof(complexf)), ModFormat(framesize * sizeof(complexf))), +#ifdef __SSE__ +    d_frameSize(framesize * sizeof(complexf) / sizeof(__m128)), +#else // !__SSE__ +    d_frameSize(framesize), +#endif +    d_factor(factor) +{ +    PDEBUG("GainControl::GainControl(%zu, %u) @ %p\n", framesize, mode, this); + +    switch(mode) { +    case GAIN_FIX: +        PDEBUG("Gain mode: fix\n"); +        computeGain = computeGainFix; +        break; +    case GAIN_MAX: +        PDEBUG("Gain mode: max\n"); +        computeGain = computeGainMax; +        break; +    case GAIN_VAR: +        PDEBUG("Gain mode: var\n"); +        computeGain = computeGainVar; +        break; +    default: +        throw std::runtime_error( +                "GainControl::GainControl invalid computation gain mode!"); +    } +} + + +GainControl::~GainControl() +{ +    PDEBUG("GainControl::~GainControl() @ %p\n", this); + +} + + +int GainControl::process(Buffer* const dataIn, Buffer* dataOut) +{ +    PDEBUG("GainControl::process" +            "(dataIn: %p, dataOut: %p)\n", +            dataIn, dataOut); + +    dataOut->setLength(dataIn->getLength()); + +#ifdef __SSE__ +    const __m128* in = reinterpret_cast<const __m128*>(dataIn->getData()); +    __m128* out = reinterpret_cast<__m128*>(dataOut->getData()); +    size_t sizeIn = dataIn->getLength() / sizeof(__m128); +    size_t sizeOut = dataOut->getLength() / sizeof(__m128); +    __u128 gain128; + +    if ((sizeIn % d_frameSize) != 0) { +        PDEBUG("%zu != %zu\n", sizeIn, d_frameSize); +        throw std::runtime_error( +                "GainControl::process input size not valid!"); +    } + +    for (size_t i = 0; i < sizeIn; i += d_frameSize) { +        gain128.m = computeGain(in, d_frameSize); +        gain128.m = _mm_mul_ps(gain128.m, _mm_set1_ps(d_factor)); + +        PDEBUG("********** Gain: %10f **********\n", gain128.f[0]); + +        //////////////////////////////////////////////////////////////////////// +        // Applying gain to output data +        //////////////////////////////////////////////////////////////////////// +        for (size_t sample = 0; sample < d_frameSize; ++sample) { +            out[sample] = _mm_mul_ps(in[sample], gain128.m); +        } + +        in += d_frameSize; +        out += d_frameSize; +    } +#else // !__SSE__ +    const complexf* in = reinterpret_cast<const complexf*>(dataIn->getData()); +    complexf* out = reinterpret_cast<complexf*>(dataOut->getData()); +    size_t sizeIn = dataIn->getLength() / sizeof(complexf); +    size_t sizeOut = dataOut->getLength() / sizeof(complexf); +    float gain; + +    if ((sizeIn % d_frameSize) != 0) { +        PDEBUG("%zu != %zu\n", sizeIn, d_frameSize); +        throw std::runtime_error( +                "GainControl::process input size not valid!"); +    } + +    for (size_t i = 0; i < sizeIn; i += d_frameSize) { +        gain = myFactor * computeGain(in, d_frameSize); + +        PDEBUG("********** Gain: %10f **********\n", gain); + +        //////////////////////////////////////////////////////////////////////// +        // Applying gain to output data +        //////////////////////////////////////////////////////////////////////// +        for (size_t sample = 0; sample < d_frameSize; ++sample) { +            out[sample] = in[sample] * gain; +        } + +        in += d_frameSize; +        out += d_frameSize; +    } +#endif // __SSE__ +     +    return sizeOut; +} + + +#ifdef __SSE__ +__m128 GainControl::computeGainFix(const __m128* in, size_t sizeIn) +{ +    return _mm_set1_ps(512.0f); +} +#else // !__SSE__ +float GainControl::computeGainFix(const complexf* in, size_t sizeIn) +{ +    return 512.0f; +} +#endif // __SSE__ + + +#ifdef __SSE__ +__m128 GainControl::computeGainMax(const __m128* in, size_t sizeIn) +{ +    __u128 gain128; +    __u128 min128; +    __u128 max128; +    __u128 tmp128; +    static const __m128 factor128 = _mm_set1_ps(0x7fff); + +    //////////////////////////////////////////////////////////////////////// +    // Computing max, min and average +    //////////////////////////////////////////////////////////////////////// +    min128.m = _mm_set1_ps(__FLT_MAX__); +    max128.m = _mm_set1_ps(__FLT_MIN__); + +    for (size_t sample = 0; sample < sizeIn; ++sample) { +        min128.m = _mm_min_ps(in[sample], min128.m); +        max128.m = _mm_max_ps(in[sample], max128.m); +    } + +    // Merging min +    tmp128.m = _mm_shuffle_ps(min128.m, min128.m, _MM_SHUFFLE(0, 1, 2, 3)); +    min128.m = _mm_min_ps(min128.m, tmp128.m); +    tmp128.m = _mm_shuffle_ps(min128.m, min128.m, _MM_SHUFFLE(1, 0, 3, 2)); +    min128.m = _mm_min_ps(min128.m, tmp128.m); +    PDEBUG("********** Min:   %10f  **********\n", min128.f[0]); + +    // Merging max +    tmp128.m = _mm_shuffle_ps(max128.m, max128.m, _MM_SHUFFLE(0, 1, 2, 3)); +    max128.m = _mm_max_ps(max128.m, tmp128.m); +    tmp128.m = _mm_shuffle_ps(max128.m, max128.m, _MM_SHUFFLE(1, 0, 3, 2)); +    max128.m = _mm_max_ps(max128.m, tmp128.m); +    PDEBUG("********** Max:   %10f  **********\n", max128.f[0]); + +    //////////////////////////////////////////////////////////////////////////// +    // Computing gain +    //////////////////////////////////////////////////////////////////////////// +    // max = max(-min, max) +    max128.m = _mm_max_ps(_mm_mul_ps(min128.m, _mm_set1_ps(-1.0f)), max128.m); +    // Detect NULL +    if ((int)max128.f[0] != 0) { +        gain128.m = _mm_div_ps(factor128, max128.m); +    } else { +        gain128.m = _mm_set1_ps(1.0f); +    } + +    return gain128.m; +} +#else // !__SSE__ +float GainControl::computeGainMax(const complexf* in, size_t sizeIn) +{ +    float gain; +    float min; +    float max; +    static const float factor = 0x7fff; + +    //////////////////////////////////////////////////////////////////////// +    // Computing max, min and average +    //////////////////////////////////////////////////////////////////////// +    min = __FLT_MAX__; +    max = __FLT_MIN__; + +    for (size_t sample = 0; sample < sizeIn; ++sample) { +        if (in[sample].real() < min) { +            min = in[sample].real(); +        } +        if (in[sample].real() > max) { +            max = in[sample].real(); +        } +        if (in[sample].imag() < min) { +            min = in[sample].imag(); +        } +        if (in[sample].imag() > max) { +            max = in[sample].imag(); +        } +    } + +    PDEBUG("********** Min:  %10f **********\n", min); +    PDEBUG("********** Max:  %10f **********\n", max); + +    //////////////////////////////////////////////////////////////////////////// +    // Computing gain +    //////////////////////////////////////////////////////////////////////////// +    // gain = factor128 / max(-min, max) +    min = -min; +    if (min > max) { +        max = min; +    } + +    // Detect NULL +    if ((int)max != 0) { +        gain = factor / max; +    } else { +        gain = 1.0f; +    } + +    return gain; +} +#endif // __SSE__ + + +#ifdef __SSE__ +__m128 GainControl::computeGainVar(const __m128* in, size_t sizeIn) +{ +    __u128 gain128; +    __u128 mean128; +    __u128 var128; +    __u128 tmp128; +    static const __m128 factor128 = _mm_set1_ps(0x7fff); + +    mean128.m = _mm_setzero_ps(); +//    var128.m = _mm_setzero_ps(); + +    for (size_t sample = 0; sample < sizeIn; ++sample) { +        __m128 delta128 = _mm_sub_ps(in[sample], mean128.m); +        __m128 i128 = _mm_set1_ps(sample + 1); +        __m128 q128 = _mm_div_ps(delta128, i128); +        mean128.m = _mm_add_ps(mean128.m, q128); +//        var128.m = _mm_add_ps(var128.m, +//                _mm_mul_ps(delta128, _mm_sub_ps(in[sample], mean128.m))); +    } + +    // Merging average +    tmp128.m = _mm_shuffle_ps(mean128.m, mean128.m, _MM_SHUFFLE(1, 0, 3, 2)); +    mean128.m = _mm_add_ps(mean128.m, tmp128.m); +    mean128.m = _mm_mul_ps(mean128.m, _mm_set1_ps(0.5f)); +    PDEBUG("********** Mean:  %10f, %10f, %10f, %10f **********\n", +            mean128.f[0], mean128.f[1], mean128.f[2], mean128.f[3]); + +    //////////////////////////////////////////////////////////////////////// +    // Computing standard deviation +    //////////////////////////////////////////////////////////////////////// +    var128.m = _mm_setzero_ps(); +    for (size_t sample = 0; sample < sizeIn; ++sample) { +        __m128 diff128 = _mm_sub_ps(in[sample], mean128.m); +        __m128 delta128 = _mm_sub_ps(_mm_mul_ps(diff128, diff128), var128.m); +        __m128 i128 = _mm_set1_ps(sample + 1); +        __m128 q128 = _mm_div_ps(delta128, i128); +        var128.m = _mm_add_ps(var128.m, q128); +    } +    // Merging average +    tmp128.m = _mm_shuffle_ps(var128.m, var128.m, _MM_SHUFFLE(1, 0, 3, 2)); +    var128.m = _mm_add_ps(var128.m, tmp128.m); +    var128.m = _mm_mul_ps(var128.m, _mm_set1_ps(0.5f)); +    var128.m = _mm_sqrt_ps(var128.m); +    PDEBUG("********** Var:   %10f, %10f, %10f, %10f **********\n", +            var128.f[0], var128.f[1], var128.f[2], var128.f[3]); +    var128.m = _mm_mul_ps(var128.m, _mm_set1_ps(4.0f)); +    PDEBUG("********** 4*Var: %10f, %10f, %10f, %10f **********\n", +            var128.f[0], var128.f[1], var128.f[2], var128.f[3]); + +    //////////////////////////////////////////////////////////////////////////// +    // Computing gain +    //////////////////////////////////////////////////////////////////////////// +    // gain = factor128 / max(real, imag) +    // Detect NULL +    if ((int)var128.f[0] != 0) { +        gain128.m = _mm_div_ps(factor128, +                _mm_max_ps(var128.m, +                    _mm_shuffle_ps(var128.m, var128.m, _MM_SHUFFLE(2, 3, 0, 1)))); +    } else { +        gain128.m = _mm_set1_ps(1.0f); +    } + +    return gain128.m; +} +#else // !__SSE__ +float GainControl::computeGainVar(const complexf* in, size_t sizeIn) +{ +    float gain; +    complexf mean; +    complexf var; +    static const float factor = 0x7fff; + +    mean = complexf(0.0f, 0.0f); + +    for (size_t sample = 0; sample < sizeIn; ++sample) { +        complexf delta = in[sample] - mean; +        float i = sample + 1; +        complexf q = delta / i; +        mean = mean + q; +    } + +    PDEBUG("********** Mean:  %10f, %10f **********\n", mean.real(), mean.imag()); + +    //////////////////////////////////////////////////////////////////////// +    // Computing standard deviation +    //////////////////////////////////////////////////////////////////////// +    var = complexf(0.0f, 0.0f); +    for (size_t sample = 0; sample < sizeIn; ++sample) { +        complexf diff = in[sample] - mean; +        complexf delta = complexf(diff.real() * diff.real(), diff.imag() * diff.imag()) - var; +        float i = sample + 1; +        complexf q = delta / i; +        var = var + q; +    } +    PDEBUG("********** Var:   %10f, %10f **********\n", var.real(), var.imag()); +    var = var * 4.0f; +    PDEBUG("********** 4*Var: %10f, %10f **********\n", var.real(), var.imag()); + +    //////////////////////////////////////////////////////////////////////////// +    // Computing gain +    //////////////////////////////////////////////////////////////////////////// +    // gain = factor128 / max(real, imag) +    if (var.imag() > var.real()) { +        var.real() = var.imag(); +    } +    // Detect NULL +    if ((int)var.real() == 0) { +        gain = factor / var.real(); +    } else { +        gain = 1.0f; +    } + +    return gain; +} +#endif // __SSE__ | 
