////////////////////////////////////////////////////////////////////////////////
///
/// Sampled sound tempo changer/time stretch algorithm. Changes the sound tempo
/// while maintaining the original pitch by using a time domain WSOLA-like method
/// with several performance-increasing tweaks.
///
/// Note : MMX optimized functions reside in a separate, platform-specific file,
/// e.g. 'mmx_win.cpp' or 'mmx_gcc.cpp'
///
/// Author        : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed  : $Date: 2006/02/05 16:44:06 $
// File revision : $Revision: 1.16 $
//
// $Id: TDStretch.h,v 1.16 2006/02/05 16:44:06 Olli Exp $
//
////////////////////////////////////////////////////////////////////////////////
//
// License :
//
//  SoundTouch audio processing library
//  Copyright (c) Olli Parviainen
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 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
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
////////////////////////////////////////////////////////////////////////////////

#ifndef TDStretch_H
#define TDStretch_H

#include "STTypes.h"
#include "RateTransposer.h"
#include "FIFOSamplePipe.h"

namespace soundtouch
{

// Default values for sound processing parameters:

/// Default length of a single processing sequence, in milliseconds. This determines to how
/// long sequences the original sound is chopped in the time-stretch algorithm.
///
/// The larger this value is, the lesser sequences are used in processing. In principle
/// a bigger value sounds better when slowing down tempo, but worse when increasing tempo
/// and vice versa.
///
/// Increasing this value reduces computational burden & vice versa.
static const uint32_t DEFAULT_SEQUENCE_MS = 63;

static const uint32_t DEFAULT_SEEKWINDOW_MS = 17;

static const uint32_t DEFAULT_OVERLAP_MS = 7;

/// Class that does the time-stretch (tempo change) effect for the processed
/// sound.
class TDStretch : public FIFOProcessor
{
protected:
	uint32_t channels;
	uint32_t sampleReq;
	float tempo;

	SAMPLETYPE *pMidBuffer;
	SAMPLETYPE *pRefMidBuffer;
	SAMPLETYPE *pRefMidBufferUnaligned;
	uint32_t overlapLength;
	uint32_t overlapDividerBits;
	uint32_t slopingDivider;
	uint32_t seekLength;
	uint32_t seekWindowLength;
	uint32_t maxOffset;
	float nominalSkip;
	float skipFract;
	FIFOSampleBuffer outputBuffer;
	FIFOSampleBuffer inputBuffer;
	bool bQuickseek;
	bool bMidBufferDirty;

	uint32_t sampleRate;
	uint32_t sequenceMs;
	uint32_t seekWindowMs;
	uint32_t overlapMs;

	void acceptNewOverlapLength(uint32_t newOverlapLength);

	virtual void clearCrossCorrState();
	void calculateOverlapLength(uint32_t overlapMs);

	virtual LONG_SAMPLETYPE calcCrossCorrStereo(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;
	virtual LONG_SAMPLETYPE calcCrossCorrMono(const SAMPLETYPE *mixingPos, const SAMPLETYPE *compare) const;

	virtual uint32_t seekBestOverlapPositionStereo(const SAMPLETYPE *refPos);
	virtual uint32_t seekBestOverlapPositionStereoQuick(const SAMPLETYPE *refPos);
	virtual uint32_t seekBestOverlapPositionMono(const SAMPLETYPE *refPos);
	virtual uint32_t seekBestOverlapPositionMonoQuick(const SAMPLETYPE *refPos);
	uint32_t seekBestOverlapPosition(const SAMPLETYPE *refPos);

	virtual void overlapStereo(SAMPLETYPE *output, const SAMPLETYPE *input) const;
	virtual void overlapMono(SAMPLETYPE *output, const SAMPLETYPE *input) const;

	void clearMidBuffer();
	void overlap(SAMPLETYPE *output, const SAMPLETYPE *input, uint32_t ovlPos) const;

	void precalcCorrReferenceMono();
	void precalcCorrReferenceStereo();

	void processNominalTempo();

	/// Changes the tempo of the given sound samples.
	/// Returns amount of samples returned in the "output" buffer.
	/// The maximum amount of samples that can be returned at a time is set by
	/// the 'set_returnBuffer_size' function.
	void processSamples();

public:
	TDStretch();
	virtual ~TDStretch();

	/// Operator 'new' is overloaded so that it automatically creates a suitable instance
	/// depending on if we've a MMX/SSE/etc-capable CPU available or not.
	void *operator new(size_t s);

	/// Use this function instead of "new" operator to create a new instance of this class.
	/// This function automatically chooses a correct feature set depending on if the CPU
	/// supports MMX/SSE/etc extensions.
	static TDStretch *newInstance();

	/// Returns the output buffer object
	FIFOSamplePipe *getOutput() { return &outputBuffer; };

	/// Returns the input buffer object
	FIFOSamplePipe *getInput() { return &inputBuffer; };

	/// Sets new target tempo. Normal tempo = 'SCALE', smaller values represent slower
	/// tempo, larger faster tempo.
	void setTempo(float newTempo);

	/// Returns nonzero if there aren't any samples available for outputting.
	virtual void clear();

	/// Clears the input buffer
	void clearInput();

	/// Sets the number of channels, 1 = mono, 2 = stereo
	void setChannels(uint32_t numChannels);

	/// Enables/disables the quick position seeking algorithm. Zero to disable,
	/// nonzero to enable
	void enableQuickSeek(bool enable);

	/// Returns nonzero if the quick seeking algorithm is enabled.
	bool isQuickSeekEnabled() const;

	/// Sets routine control parameters. These control are certain time constants
	/// defining how the sound is stretched to the desired duration.
	//
	/// 'sampleRate' = sample rate of the sound
	/// 'sequenceMS' = one processing sequence length in milliseconds
	/// 'seekwindowMS' = seeking window length for scanning the best overlapping
	///      position
	/// 'overlapMS' = overlapping length
	void setParameters(uint32_t sampleRate,                             ///< Samplerate of sound being processed (Hz)
                       uint32_t sequenceMS = DEFAULT_SEQUENCE_MS,       ///< Single processing sequence length (ms)
                       uint32_t seekwindowMS = DEFAULT_SEEKWINDOW_MS,   ///< Offset seeking window length (ms)
                       uint32_t overlapMS = DEFAULT_OVERLAP_MS          ///< Sequence overlapping length (ms)
                       );

	/// Get routine control parameters, see setParameters() function.
	/// Any of the parameters to this function can be NULL, in such case corresponding parameter
	/// value isn't returned.
	void getParameters(uint32_t *pSampleRate, uint32_t *pSequenceMs, uint32_t *pSeekWindowMs, uint32_t *pOverlapMs);

	/// Adds 'numsamples' pcs of samples from the 'samples' memory position into
	/// the input of the object.
	virtual void putSamples(
            const SAMPLETYPE *samples,  ///< Input sample data
            uint32_t numSamples                         ///< Number of samples in 'samples' so that one sample
                                                       ///< contains both channels if stereo
            );
};


// Implementation-specific class declarations:

//#ifdef ALLOW_MMX
//    /// Class that implements MMX optimized routines for 16bit integer samples type.
//    class TDStretchMMX : public TDStretch
//    {
//    protected:
//        long calcCrossCorrStereo(const short *mixingPos, const short *compare) const;
//        virtual void overlapStereo(short *output, const short *input) const;
//        virtual void clearCrossCorrState();
//    };
//#endif /// ALLOW_MMX
//
//
//#ifdef ALLOW_3DNOW
//    /// Class that implements 3DNow! optimized routines for floating point samples type.
//    class TDStretch3DNow : public TDStretch
//    {
//    protected:
//        double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
//    };
//#endif /// ALLOW_3DNOW

#ifdef __SSE__
/// Class that implements SSE optimized routines for floating point samples type.
class TDStretchSSE : public TDStretch
{
protected:
	double calcCrossCorrStereo(const float *mixingPos, const float *compare) const;
};
#endif /// ALLOW_SSE

}

#endif  /// TDStretch_H