    /*

    Copyright (C) 2000 Stefan Westerfeld
                       stefan@space.twc.de

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Library General Public
    License as published by the Free Software Foundation; either
    version 2 of the License, or (at your option) any later version.
  
    This library 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
    Library General Public License for more details.

    You should have received a copy of the GNU Library General Public License
    along with this library; see the file COPYING.LIB.  If not, write to
    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
    Boston, MA 02110-1301, USA.

    */

#include <math.h>
#include "artsflow.h"
#include "stdsynthmodule.h"
#include "flowsystem.h"
#include <debug.h>

#include <cstring>

using namespace Arts;

namespace Arts {

class StereoVolumeControl_impl : virtual public StereoVolumeControl_skel,
								 virtual public StdSynthModule
{
	float _scaleFactor;
	float _currentVolumeLeft;
	float _currentVolumeRight;
	bool _virtualized;
	bool _calcVolume;
public:
	StereoVolumeControl_impl() :_scaleFactor(1.0), _currentVolumeLeft(0.0),
	                            _currentVolumeRight(0.0), _virtualized( false ),
	                            _calcVolume( false )
	{
		virtualize();
	}

	/*attribute float scaleFactor;*/
	void scaleFactor(float newFactor) {
		if(_scaleFactor != newFactor)
		{
			_scaleFactor = newFactor;
			scaleFactor_changed(newFactor);
			if( _scaleFactor == 1.0 )
			{
				if( !_calcVolume && !_virtualized )
					virtualize();
			}
			else if( _virtualized )
				devirtualize();
		}
	}
	float scaleFactor() { return _scaleFactor; }

	/*
	  readonly attribute float currentVolumeLeft;
	  readonly attribute float currentVolumeRight;
	*/
	float currentVolumeLeft()
	{
		if( _virtualized && !_calcVolume )
			needVolumeCalculation();

		return _currentVolumeLeft;
	}

	float currentVolumeRight()
	{
		if( _virtualized && !_calcVolume )
			needVolumeCalculation();

		return _currentVolumeRight;
	}

	void needVolumeCalculation()
	{
		arts_debug( "calculate Volume in StereoVolumeControl" );
		_calcVolume = true;
		devirtualize();
	}

	void virtualize()
	{
		arts_debug( "virtualize StereoVolumeControl" );
		_virtualized = true;
		_node()->virtualize( "inleft", _node(), "outleft" );
		_node()->virtualize( "inright", _node(), "outright" );
		_currentVolumeRight = _currentVolumeLeft = 0.0;
	}
	void devirtualize()
	{
		arts_debug( "devirtualize StereoVolumeControl" );
		_virtualized = false;
		_node()->devirtualize( "inleft", _node(), "outleft" );
		_node()->devirtualize( "inright", _node(), "outright" );
	}
	/*
	  in audio stream inleft, inright;
	  out audio stream outleft, outright;
	*/
	void calculateBlock(unsigned long samples)
	{
		if( _scaleFactor == 1.0 )
		{
			if( _calcVolume )
			{
				/*
				 * make sure that we reach zero sometimes - otherwise there are
				 * performance issues with calculations close to zero
				 */
				if(fabs(_currentVolumeLeft) < 0.001)	_currentVolumeLeft = 0.0;
				if(fabs(_currentVolumeRight) < 0.001)	_currentVolumeRight = 0.0;

				for( unsigned long i = 0; i < samples; i += 10 )
				{
					/* measure volume */
					float delta;

					// left
					delta = fabs( outleft[ i ] ) - _currentVolumeLeft;
					if( delta > 0.0 )
						_currentVolumeLeft += 0.1 * delta;
					else
						_currentVolumeLeft += 0.003 * delta;

					// right
					delta = fabs( outright[ i ] ) - _currentVolumeRight;
					if( delta > 0.0 )
						_currentVolumeRight += 0.1 * delta;
					else
						_currentVolumeRight += 0.003 * delta;
				}
				memcpy( outleft, inleft, samples * sizeof( float ) );
				memcpy( outright, inright, samples * sizeof( float ) );
			}
			return;
		}

		unsigned long i;

		/*
		 * make sure that we reach zero sometimes - otherwise there are
		 * performance issues with calculations close to zero
		 */
		if(fabs(_currentVolumeLeft) < 0.001)	_currentVolumeLeft = 0.0;
		if(fabs(_currentVolumeRight) < 0.001)	_currentVolumeRight = 0.0;

		for(i=0;i<samples;i++)
		{
			/* scale */
			outleft[i] = inleft[i] * _scaleFactor;
			outright[i] = inright[i] * _scaleFactor;

			/* measure volume */
			float delta;

			// left
			delta = fabs(outleft[i])-_currentVolumeLeft;
			if(delta > 0.0)
				_currentVolumeLeft += 0.01 * delta;
			else
				_currentVolumeLeft += 0.0003 * delta;

			// right
			delta = fabs(outright[i])-_currentVolumeRight;
			if(delta > 0.0)
				_currentVolumeRight += 0.01 * delta;
			else
				_currentVolumeRight += 0.0003 * delta;
		}
	}

	AutoSuspendState autoSuspend() {
		return (_currentVolumeLeft+_currentVolumeRight<0.001)? 
			asSuspend : asNoSuspend;
	}

};

REGISTER_IMPLEMENTATION(StereoVolumeControl_impl);

}

// vim: sw=4 ts=4 noet
