// ----------------------------------------------------------------------------
//
//  Copyright (C) 2010-2017 Fons Adriaensen <fons@linuxaudio.org>
//    
//  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/>.
//
// ----------------------------------------------------------------------------


#include <string.h>
#include <math.h>
#include "dyncom1.h"


Dyncom1::Dyncom1 (int fsamp, int nchan) :
    _fsamp (fsamp),
    _nchan (nchan),
    _ipg  (1.0f),
    _ipg1 (1.0f),
    _rat  (0.0f),
    _rat1 (0.0f),
    _pthr (0.05f),
    _tatt (0.01f),
    _trel (0.03f),
    _hold (false),
    _ambi (false)
{
    reset ();
}


Dyncom1::~Dyncom1 (void)
{
}


void Dyncom1::reset (void)
{
    _za1 = _zr1 = _zr2 = 0;
}


void Dyncom1::process (int nsamp, float *inp[], float *out[])
{
    int    i, j, n;
    float  watt, wrel;
    float  gmin, gmax;
    float  za1, zr1, zr2;;
    float  g, dg, r, dr;
    float  h, m, v, x, w;

    if (_newg)
    {
	gmax = -100.0f;
	gmin =  100.0f;
        _newg = false;
    }
    else
    {
	gmax = _gmax;
	gmin = _gmin;
    }

    dg = _ipg1 - _ipg;
    if (fabsf (dg) < 1e-5f)
    {
	g = _ipg1;
	dg = 0;
    }
    else
    {
	g = _ipg;
	dg /= nsamp;
    }
    
    dr = _rat1 - _rat;
    if (fabsf (dr) < 1e-5f)
    {
	r = _rat1;
	dr = 0;
    }
    else
    {
	r = _rat;
	dr /= nsamp;
    }
    watt = 0.5f / (_fsamp * _tatt);
    wrel = 3.5f / (_fsamp * _trel);
    za1 = _za1;
    zr1 = _zr1;
    zr2 = _zr2;
    h = _hold ? 2 * _pthr : 0;
    n = _ambi ? 1 : _nchan;
    m = 1.0f / n;
    for (j = 0; j < nsamp; j++)
    {
	g += dg;
	v = 0;
	for (i = 0; i < _nchan; i++)
	{
            x = g * inp [i][j];
	    if (i < n) v += x * x;
	}
	za1 += watt * (_pthr + v * m - za1);
	w = (za1 < h) ? 0 : wrel;
	if (zr1 < za1) zr1 = za1;
	else zr1 -= w * zr1;
	if (zr2 < za1) zr2 = za1;
	else zr2 += w * (zr1 - zr2);
	r += dr;
	v = -r * logf (20.0f * zr2);
	if (v > gmax) gmax = v;
	if (v < gmin) gmin = v;
	v = g * expf (v);
	for (i = 0; i < _nchan; i++)
	{
	    out [i][j] = v * inp [i][j];
	}
    }

    _za1 = za1;
    _zr1 = zr1;
    _zr2 = zr2;
    _ipg = g;
    _rat = r;
    _gmax = gmax;
    _gmin = gmin;
}
