/*

    Copyright (C) 2000 Stefan Westerfeld
                       stefan@space.twc.de
                  2001, 2003 Matthias Kretz
                             kretz@kde.org

    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 "artsflow.h"
#include "stdsynthmodule.h"
#include "debug.h"

#include <vector>
#include <iostream>
#include <math.h>

#undef DEBUG_MESSAGES

using namespace std;
using namespace Arts;

namespace Arts {

class AudioToByteStream_impl : public AudioToByteStream_skel,
                               public StdSynthModule
{
	long _samplingRate, _channels, _bits;
	long sampleSize;
	double step;
	bool interpolate;
	vector<float> leftbuffer;
	vector<float> rightbuffer;
	int range;
	double _pos;
protected:
	void updateSampleSize()
	{
		sampleSize = _channels * _bits / 8;
	}

public:
	AudioToByteStream_impl() :
		_pos(0)
	{
		samplingRate(44100);
		channels(2);
		bits(16);
	}

	long samplingRate() { return _samplingRate; }
	void samplingRate(long newRate) {
		double newStep = samplingRateFloat / (float)newRate;
		arts_return_if_fail(newStep > 0);
		_samplingRate = newRate;
		step = newStep;

		double delta = step - floor(step);
		interpolate = fabs(delta) > 0.001;
#ifdef DEBUG_MESSAGES
		arts_debug( "samplingRate(%i): step = %e, delta = %e, interpolate: %i", newRate, step, delta, interpolate );
#endif
	}

	long channels() { return _channels; }
	void channels(long newChannels) {
		arts_return_if_fail(newChannels == 1 || newChannels == 2);
		_channels = newChannels;
		updateSampleSize();
	}

	long bits() { return _bits; }
	void bits(long newBits) {
		arts_return_if_fail(newBits == 8 || newBits == 16);
		_bits = newBits;
		range = (newBits == 8 ) ? 128 : 32768;
		updateSampleSize();
	}

	void calculateBlock(unsigned long samples)
	{
		leftbuffer.resize( 1 + samples );
		rightbuffer.resize( 1 + samples );
		for( unsigned long i = 0; i < samples; ++i ) {
			leftbuffer[1 + i] = (left[i] > 1.0f) ? 1.0f : (left[i] < -1.0f) ? -1.0f : left[i];
			rightbuffer[1 + i] = (right[i] > 1.0f) ? 1.0f : (right[i] < -1.0f) ? -1.0f : right[i];
		}

		int samplestosend = (int)ceil(leftbuffer.size() / step);
		DataPacket<mcopbyte> *packet = outdata.allocPacket( samplestosend * sampleSize );
#ifdef DEBUG_MESSAGES
		arts_debug( "calculateBlock(%i): send %i samples", samples, samplestosend );
#endif

		int processed = 0;
		if( interpolate ) {
			double pos = 0;
			if( _channels == 2 ) {
				if( _bits == 16 ) {
					while( _pos < (double)leftbuffer.size() - 1 ) {
						double error = modf(_pos, &pos);
						int intpos = (int)pos;
#ifdef DEBUG_MESSAGES
						arts_debug( "pos=%i, error=%e", intpos, error );
#endif
						long leftchannel = long((leftbuffer[intpos] * (1.0 - error) + leftbuffer[intpos + 1] * error) * 32768) + 32768;
						long rightchannel = long((rightbuffer[intpos] * (1.0 - error) + rightbuffer[intpos + 1] * error) * 32768) + 32768;
						packet->contents[processed    ] = leftchannel;
						packet->contents[processed + 1] = (leftchannel >> 8) - 128;
						packet->contents[processed + 2] = rightchannel;
						packet->contents[processed + 3] = (rightchannel >> 8) - 128;
						_pos += step;
						processed += 4;
					}
				} else if( _bits == 8 ) {
					while( _pos < (double)leftbuffer.size() - 1 ) {
						double error = modf(_pos, &pos);
						int intpos = (int)pos;
						long leftchannel = long((leftbuffer[intpos] * (1.0 - error) + leftbuffer[intpos + 1] * error) * 128) + 128;
						long rightchannel = long((rightbuffer[intpos] * (1.0 - error) + rightbuffer[intpos + 1] * error) * 128) + 128;
						packet->contents[processed    ] = leftchannel;
						packet->contents[processed + 1] = rightchannel;
						_pos += step;
						processed += 2;
					}
				}
			} else {
				if( _bits == 16 ) {
					while( _pos < (double)leftbuffer.size() - 1 ) {
						double error = modf(_pos, &pos);
						int intpos = (int)pos;
						long leftchannel = long(((leftbuffer[intpos] + rightbuffer[intpos]) * (1.0 - error) + (leftbuffer[intpos + 1] + rightbuffer[intpos + 1]) * error) * 16384) + 32768;
						packet->contents[processed    ] = leftchannel;
						packet->contents[processed + 1] = (leftchannel >> 8) - 128;
						_pos += step;
						processed += 2;
					}
				} else if( _bits == 8 ) {
					while( _pos < (double)leftbuffer.size() - 1 ) {
						double error = modf(_pos, &pos);
						int intpos = (int)pos;
						long leftchannel = long(((leftbuffer[intpos] + rightbuffer[intpos]) * (1.0 - error) + (leftbuffer[intpos + 1] + rightbuffer[intpos + 1]) * error) * 64) + 128;
						packet->contents[processed] = leftchannel;
						_pos += step;
						++processed;
					}
				}
			}
		} else {
			if( _channels == 2 ) {
				if( _bits == 16 ) {
					while( _pos < (double)leftbuffer.size() - 1 ) {
						int intpos = (int)_pos;
						long leftchannel = long(leftbuffer[intpos] * 32768) + 32768;
						long rightchannel = long(rightbuffer[intpos] * 32768) + 32768;
						packet->contents[processed    ] = leftchannel;
						packet->contents[processed + 1] = (leftchannel >> 8) - 128;
						packet->contents[processed + 2] = rightchannel;
						packet->contents[processed + 3] = (rightchannel >> 8) - 128;
						_pos += step;
						processed += 4;
					}
				} else if( _bits == 8 ) {
					while( _pos < (double)leftbuffer.size() - 1 ) {
						int intpos = (int)_pos;
						long leftchannel = long(leftbuffer[intpos] * 128) + 128;
						long rightchannel = long(rightbuffer[intpos] * 128) + 128;
						packet->contents[processed    ] = leftchannel;
						packet->contents[processed + 1] = rightchannel;
						_pos += step;
						processed += 2;
					}
				}
			} else {
				if( _bits == 16 ) {
					while( _pos < (double)leftbuffer.size() - 1 ) {
						int intpos = (int)_pos;
						long leftchannel = long((leftbuffer[intpos] + rightbuffer[intpos]) * 16384) + 32768;
						packet->contents[processed    ] = leftchannel;
						packet->contents[processed + 1] = (leftchannel >> 8) - 128;
						_pos += step;
						processed += 2;
					}
				} else if( _bits == 8 ) {
					while( _pos < (double)leftbuffer.size() - 1 ) {
						int intpos = (int)_pos;
						long leftchannel = long((leftbuffer[intpos] + rightbuffer[intpos]) * 64) + 128;
						packet->contents[processed] = leftchannel;
						_pos += step;
						++processed;
					}
				}
			}
		}
		leftbuffer[0] = leftbuffer.back();
		rightbuffer[0] = rightbuffer.back();
		_pos = _pos - floor(_pos);
		packet->size = processed;
		packet->send();
#ifdef DEBUG_MESSAGES
		arts_debug( "calculateBlock(%i): %i samples sent, _pos = %e", samples, processed / sampleSize, _pos );
#endif
	}
};

REGISTER_IMPLEMENTATION(AudioToByteStream_impl);

}