/*

   Copyright (C) 2001 Matthias Kretz <kretz@kde.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 2 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.

 */

#include "artsmodules.h"
#include "stdsynthmodule.h"
#include "debug.h"

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

#define LN2 0.69314718

using namespace std;
namespace Arts {

class Synth_COMPRESSOR_impl : virtual public Synth_COMPRESSOR_skel,
							  virtual public StdSynthModule
{
protected:
	float _attack, _release, _threshold, _ratio, _output;
	float _attackfactor, _releasefactor;
	float _volume;

public:
	float attack() { return _attack; }
	float release() { return _release; }
	float threshold() { return _threshold; }
	float ratio() { return _ratio; }
	float output() { return _output; }

	Synth_COMPRESSOR_impl() : _threshold( 1 ), _ratio( 0.8 ), _output( 0 )
	{
		attack( 10 );
		release( 10 );
	}

	void streamInit()
	{
		_volume = 0;
	}

	void calculateBlock(unsigned long samples)
	{
		for( unsigned long i = 0; i < samples; i++ ) {
			float delta = fabs( invalue[i] ) - _volume;
			if( delta > 0.0 )
				_volume += _attackfactor * delta;
			else
				_volume += _releasefactor * delta;

			if( _volume > _threshold ) {
				outvalue[i] = ( ( _volume - _threshold ) * _ratio + _threshold ) / _volume * invalue[i] * _output;
			} else {
				outvalue[i] = invalue[i] * _output;
			}
		}
	}

	void attack( float newAttack )
	{ // in ms
		_attack = newAttack;
		if( _attack == 0 )
			_attackfactor = 1;
		else
			_attackfactor = LN2 / ( _attack / 1000 * samplingRateFloat );
		arts_debug( "Synth_COMPRESSOR_impl: _attackfactor = %g", _attackfactor );
		attack_changed( newAttack );
	}

	void release( float newRelease )
	{ // in ms
		_release = newRelease;
		if( _release == 0 )
			_releasefactor = 1;
		else
			_releasefactor = LN2 / ( _release / 1000 * samplingRateFloat );
		arts_debug( "Synth_COMPRESSOR_impl: _releasefactor = %g", _releasefactor );
		release_changed( newRelease );
	}

	void threshold( float newThreshold )
	{
		_threshold = newThreshold;
		threshold_changed( newThreshold );
	}

	void ratio( float newRatio )
	{
		_ratio = newRatio;
		ratio_changed( newRatio );
	}

	void output( float newOutput )
	{
		_output = newOutput;
		output_changed( newOutput );
	}
};

REGISTER_IMPLEMENTATION(Synth_COMPRESSOR_impl);
}
