/*
Copyright (C) 2007-2010 Christian Kothe

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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#ifndef FREESURROUND_DECODER_H
#define FREESURROUND_DECODER_H

/**
* Identifiers for the supported output channels (from front to back, left to right).
* The ordering here also determines the ordering of interleaved samples in the output signal.
*/
enum channel_id {
	ci_none					= 0,
	ci_front_left			= 1<<1,
	ci_front_center_left	= 1<<2,
	ci_front_center			= 1<<3,
	ci_front_center_right	= 1<<4,
	ci_front_right			= 1<<5,
	ci_side_front_left		= 1<<6,
	ci_side_front_right		= 1<<7,
	ci_side_center_left		= 1<<8,
	ci_side_center_right	= 1<<9,
	ci_side_back_left		= 1<<10,
	ci_side_back_right		= 1<<11,
	ci_back_left			= 1<<12,
	ci_back_center_left		= 1<<13,
	ci_back_center			= 1<<14,
	ci_back_center_right	= 1<<15,
	ci_back_right			= 1<<16,
	ci_lfe					= 1<<31
};

/**
* The supported output channel setups.
* A channel setup is defined by the set of channels that are present. Here is a graphic 
* of the cs_5point1 setup: http://en.wikipedia.org/wiki/File:5_1_channels_(surround_sound)_label.svg
*/
enum channel_setup {
	cs_stereo = ci_front_left | ci_front_right | ci_lfe,
	cs_3stereo = ci_front_left | ci_front_center | ci_front_right | ci_lfe,
//	cs_5stereo = ci_front_left | ci_front_center_left | ci_front_center | ci_front_center_right | ci_front_right | ci_lfe,
	cs_4point1 = ci_front_left | ci_front_right | ci_back_left | ci_back_right | ci_lfe,
	cs_5point1 = ci_front_left | ci_front_center | ci_front_right | ci_back_left | ci_back_right | ci_lfe,
	cs_6point1 = ci_front_left | ci_front_center | ci_front_right | ci_side_center_left | ci_side_center_right | ci_back_center | ci_lfe,
	cs_7point1 = ci_front_left | ci_front_center | ci_front_right | ci_side_center_left | ci_side_center_right | ci_back_left | ci_back_right | ci_lfe,
//	cs_7point1_panorama = ci_front_left | ci_front_center_left | ci_front_center | ci_front_center_right | ci_front_right | 
//						  ci_side_center_left | ci_side_center_right | ci_lfe,
//	cs_7point1_tricenter = ci_front_left | ci_front_center_left | ci_front_center | ci_front_center_right | ci_front_right |
//						   ci_back_left | ci_back_right | ci_lfe,
//	cs_8point1 = ci_front_left | ci_front_center | ci_front_right | ci_side_center_left | ci_side_center_right | 
//				 ci_back_left | ci_back_center | ci_back_right | ci_lfe,
//	cs_9point1_densepanorama = ci_front_left | ci_front_center_left | ci_front_center | ci_front_center_right | ci_front_right | 
//							   ci_side_front_left | ci_side_front_right | ci_side_center_left | ci_side_center_right | ci_lfe,
//	cs_9point1_wrap = ci_front_left | ci_front_center_left | ci_front_center | ci_front_center_right | ci_front_right | 
//					  ci_side_center_left | ci_side_center_right | ci_back_left | ci_back_right | ci_lfe,
//	cs_11point1_densewrap = ci_front_left | ci_front_center_left | ci_front_center | ci_front_center_right | ci_front_right | 
//					       ci_side_front_left | ci_side_front_right | ci_side_center_left | ci_side_center_right | 
//						   ci_side_back_left | ci_side_back_right | ci_lfe,
//	cs_13point1_totalwrap = ci_front_left | ci_front_center_left | ci_front_center | ci_front_center_right | ci_front_right | 
//					       ci_side_front_left | ci_side_front_right | ci_side_center_left | ci_side_center_right | 
//						   ci_side_back_left | ci_side_back_right | ci_back_left | ci_back_right | ci_lfe,
//	cs_16point1 = ci_front_left | ci_front_center_left | ci_front_center | ci_front_center_right | ci_front_right | 
//			      ci_side_front_left | ci_side_front_right | ci_side_center_left | ci_side_center_right | ci_side_back_left | 
//				  ci_side_back_right | ci_back_left | ci_back_center_left | ci_back_center | ci_back_center_right | ci_back_right | ci_lfe,
//	cs_legacy = 0 // same channels as cs_5point1 but different upmixing transform; does not support the focus control
};


/**
* The FreeSurround decoder.
*/
class freesurround_decoder {
public:

	/**
	* Create an instance of the decoder.
	* @param setup The output channel setup -- determines the number of output channels 
	*			   and their place in the sound field.
	* @param blocksize Granularity at which data is processed by the decode() function.
	*				   Must be a power of two and should correspond to ca. 10ms worth of single-channel 
	*				   samples (default is 4096 for 44.1Khz data). Do not make it shorter or longer 
	*				   than 5ms to 20ms since the granularity at which locations are decoded 
	*				   changes with this.
	*/
	freesurround_decoder(channel_setup setup=cs_5point1, unsigned blocksize=4096);
	~freesurround_decoder();

	/**
	* Decode a chunk of stereo sound. The output is delayed by half of the blocksize.
	* This function is the only one needed for straightforward decoding.
	* @param input Contains exactly blocksize (multiplexed) stereo samples, i.e. 2*blocksize numbers.
	* @return A pointer to an internal buffer of exactly blocksize (multiplexed) multichannel samples. 
	*		  The actual number of values depends on the number of output channels in the chosen 
	*		  channel setup.
	*/
	float *decode(float *input);
	
	/** 
	* Flush the internal buffer. 
	*/
	void flush();


	// --- soundfield transformations
	// These functions allow to set up geometric transformations of the sound field after it has been decoded.
	// The sound field is best pictured as a 2-dimensional square with the listener in its 
	// center which can be shifted or stretched in various ways before it is sent to the 
	// speakers. The order in which these transformations are applied is as listed below.
	
	/**
	* Allows to wrap the soundfield around the listener in a circular manner.
	* Determines the angle of the frontal sound stage relative to the listener, in degrees.
	* A setting of 90° corresponds to standard surround decoding, 180° stretches the front stage from 
	* ear to ear, 270° wraps it around most of the head. The side and rear content of the sound 
	* field is compressed accordingly behind the listerer. (default: 90, range: [0°..360°])
	*/
	void circular_wrap(float v);

	/**
	* Allows to shift the soundfield forward or backward.
	* Value range: [-1.0..+1.0]. 0 is no offset, positive values move the sound 
	* forward, negative values move it backwards. (default: 0)
	*/
	void shift(float v);

	/**
	* Allows to scale the soundfield backwards.
	* Value range: [0.0..+5.0] -- 0 is all compressed to the front, 1 is no change, 5 is scaled 5x backwards (default: 1)
	*/
	void depth(float v);

	/**
	* Allows to control the localization (i.e., focality) of sources.
	* Value range: [-1.0..+1.0] -- 0 means unchanged, positive means more localized, negative means more ambient (default: 0)
	*/
	void focus(float v);


	// --- rendering parameters
	// These parameters control how the sound field is mapped onto speakers.

	/**
	* Set the presence of the front center channel(s).
	* Value range: [0.0..1.0] -- fully present at 1.0, fully replaced by left/right at 0.0 (default: 1).
	* The default of 1.0 results in spec-conformant decoding ("movie mode") while a value of 0.7 is 
	* better suited for music reproduction (which is usually mixed without a center channel).
	*/
	void center_image(float v);

	/**
	* Set the front stereo separation.
	* Value range: [0.0..inf] -- 1.0 is default, 0.0 is mono.
	*/
	void front_separation(float v);

	/**
	* Set the rear stereo separation.
	* Value range: [0.0..inf] -- 1.0 is default, 0.0 is mono.
	*/
	void rear_separation(float v);


	// --- bass redirection (to LFE)

	/** 
	* Enable/disable LFE channel (default: false = disabled)
	*/
	void bass_redirection(bool v);

	/**
	* Set the lower end of the transition band, in Hz/Nyquist (default: 40/22050).
	*/
	void low_cutoff(float v);

	/**
	* Set the upper end of the transition band, in Hz/Nyquist (default: 90/22050).
	*/
	void high_cutoff(float v);


	// --- info

	/**
	* Number of samples currently held in the buffer.
	*/
	unsigned buffered();

	/** 
	* Number of channels in the given setup.
	*/
	static unsigned num_channels(channel_setup s);

	/**
	* Channel id of the i'th channel in the given setup.
	*/
	static channel_id channel_at(channel_setup s, unsigned i);

private:
	class decoder_impl *impl; // private implementation
};

#endif

