/* SigLib Frequency Domain Pitch Shifting Example by interpolation, using a sine wave
This algorithm benefits from using overlapping windows, in general,
the greater the overlap, the better the performance. ie. the lower the distortion.
*/

#include <stdio.h>
#include <siglib.h>
#include "GraphFunctions.h"

#define	DEBUG			1

#define	LEN				256L
#define	FFT_SIZE		((SLArrayIndex_t)64)
#define	INTERP_SIZE		((SLArrayIndex_t)64)
#define	LOG_FFT_SIZE	6L
#define	FFT_LEN			256L
#define	LOG_FFT_LEN		((SLArrayIndex_t)8)

#define	INPUT_LEN		(LEN+INTERP_SIZE)

#define	RATIO_UP		5L
#define	RATIO_DOWN		3L

SLData_t	pRealInput[INPUT_LEN], pImagInput[LEN], pRealOutput[LEN], pImagOutput[LEN];
SLData_t	pResults[LEN];
SLData_t	RealTime[FFT_SIZE], ImagTime[FFT_SIZE];
SLData_t	RealNew[FFT_SIZE], ImagNew[FFT_SIZE];
SLData_t	SinePhase;

SLData_t	*pFFTCoeffs;

void main(void)
{
	GraphObject *h2DGraph;							/* Declare graph object */

	SLArrayIndex_t	i, j;

													/* Allocate enough space for largest FFT */
	pFFTCoeffs = SUF_FftCoefficientAllocate (FFT_LEN);

	h2DGraph =										/* Initialize graph */
		Create2DGraph ("Frequency Domain Interpolation",	/* Graph title */
					   "Time / Frequency",			/* X-Axis label */
					   "Magnitude",					/* Y-Axis label */
					   SV_AUTO_SCALE,				/* Scaling mode */
					   SV_SIGNED,					/* Sign mode */
					   SV_GRAPH_LINE,				/* Graph type */
					   "localhost");				/* Graph server */
	if (h2DGraph == NULL)							/* Graph creation failed - e.g is server running ? */
	{
		printf ("\nGraph creation failure. Please check that the server is running\n");
		exit (1);
	}


													/* Initialise FFT */
	SIF_Fft (pFFTCoeffs,							/* Pointer to FFT coefficients */
			 SIGLIB_NULL_ARRAY_INDEX_PTR,			/* Pointer to bit reverse address table - NOT USED */
			 FFT_SIZE);								/* FFT Size */

	SinePhase = SIGLIB_ZERO;

			/* If shifting up, generate a low frequency,
				if shifting down generate a high frequency
				note : i and j are dummy variables to stop compiler warnings */
	if ((i = RATIO_UP) > (j = RATIO_DOWN))
	{
		SDA_SignalGenerate (pRealInput,				/* Pointer to destination array */
							SIGLIB_SINE_WAVE,		/* Signal type - Sine wave */
							((SLData_t)0.9),		/* Signal peak level */
							SIGLIB_FILL,			/* Fill (overwrite) or add to existing array contents */
							((SLData_t)0.038),		/* Signal frequency */
							SIGLIB_ZERO,			/* D.C. Offset */
							SIGLIB_ZERO,			/* Unused */
							SIGLIB_ZERO,			/* Signal end value - Unused */
							&SinePhase,				/* Signal phase - maintained across array boundaries */
							SIGLIB_NULL_DATA_PTR,	/* Unused */
							LEN);					/* Output array length */
	}
	else
	{
		SDA_SignalGenerate (pRealInput,				/* Pointer to destination array */
							SIGLIB_SINE_WAVE,		/* Signal type - Sine wave */
							((SLData_t)0.9),		/* Signal peak level */
							SIGLIB_FILL,			/* Fill (overwrite) or add to existing array contents */
							((SLData_t)0.138),		/* Signal frequency */
							SIGLIB_ZERO,			/* D.C. Offset */
							SIGLIB_ZERO,			/* Unused */
							SIGLIB_ZERO,			/* Signal end value - Unused */
							&SinePhase,				/* Signal phase - maintained across array boundaries */
							SIGLIB_NULL_DATA_PTR,	/* Unused */
							LEN);					/* Output array length */
	}

	for (i = 0; i < LEN; i += (FFT_SIZE / 2))
	{
													/* Copy input data */
		SDA_Copy (pRealInput+i,						/* Pointer to source array */
				  RealTime,							/* Pointer to destination array */
				  FFT_SIZE);						/* Array length */

													/* Perform real FFT */
		SDA_Rfft (RealTime,							/* Pointer to real array */
				  ImagTime,							/* Pointer to imaginary array */
				  pFFTCoeffs,						/* Pointer to FFT coefficients */
				  SIGLIB_NULL_ARRAY_INDEX_PTR,		/* Pointer to bit reverse address table - NOT USED */
				  FFT_SIZE,							/* FFT size */
				  LOG_FFT_SIZE);					/* log2 FFT size */

							/* Perform frequency domain interpolation */
		SDA_FdInterpolate (RealTime,				/* Pointer to real source array */
						   ImagTime,				/* Pointer to imaginary source array */
						   RealNew,					/* Pointer to real destination array */
						   ImagNew,					/* Pointer to imaginary destination array */
						   RATIO_UP,				/* Ratio up */
						   RATIO_DOWN,				/* Ratio down */
						   INTERP_SIZE);

#if DEBUG
		Display2DGraph (h2DGraph,					/* Graph handle */
					    "FFT'd RealTime",			/* Title of the dataset */
					    RealTime,					/* Array of Double dataset */
					    FFT_SIZE,					/* Number of data points */
						SV_GRAPH_LINE,				/* Graph type */
					    SV_MAGENTA,					/* Colour */
						SV_HIDE_MARKERS,			/* Marker enable / disable */
						SV_GRAPH_NEW);				/* New graph */
		printf ("\nFFT'd RealTime\nPlease hit <Carriage Return> to continue . . ."); getchar ();

		Display2DGraph (h2DGraph,					/* Graph handle */
					    "FFT'd ImagTime",			/* Title of the dataset */
					    ImagTime,					/* Array of Double dataset */
					    FFT_SIZE,					/* Number of data points */
						SV_GRAPH_LINE,				/* Graph type */
					    SV_BLUE,					/* Colour */
						SV_HIDE_MARKERS,			/* Marker enable / disable */
						SV_GRAPH_ADD);				/* New graph */
		printf ("\nFFT'd ImagTime\nPlease hit <Carriage Return> to continue . . ."); getchar ();
#endif


#if DEBUG
		Display2DGraph (h2DGraph,						/* Graph handle */
					    "FFT'd RealNew",				/* Title of the dataset */
					    RealNew,						/* Array of Double dataset */
					    FFT_SIZE,						/* Number of data points */
						SV_GRAPH_LINE,					/* Graph type */
					    SV_MAGENTA,						/* Colour */
						SV_HIDE_MARKERS,				/* Marker enable / disable */
						SV_GRAPH_NEW);					/* New graph */
		printf ("\nFFT'd RealNew\nPlease hit <Carriage Return> to continue . . ."); getchar ();

		Display2DGraph (h2DGraph,						/* Graph handle */
					    "FFT'd ImagNew",				/* Title of the dataset */
					    ImagNew,						/* Array of Double dataset */
					    FFT_SIZE,						/* Number of data points */
						SV_GRAPH_LINE,					/* Graph type */
					    SV_BLUE,						/* Colour */
						SV_HIDE_MARKERS,				/* Marker enable / disable */
						SV_GRAPH_ADD);					/* New graph */
		printf ("\nFFT'd ImagNew\nPlease hit <Carriage Return> to continue . . ."); getchar ();
#endif

													/* Perform complex inverse FFT */
		SDA_Cifft (RealNew,							/* Pointer to real array */
				  ImagNew,							/* Pointer to imaginary array */
				  pFFTCoeffs,						/* Pointer to FFT coefficients */
				  SIGLIB_NULL_ARRAY_INDEX_PTR,		/* Pointer to bit reverse address table - NOT USED */
				  FFT_SIZE,							/* FFT size */
				  LOG_FFT_SIZE);					/* log2 FFT size */

		for (j = 0; j < FFT_SIZE / 2; j++)
		{
			pRealOutput[i+j] = RealNew[j] / FFT_SIZE;
			pImagOutput[i+j] = ImagNew[j] / FFT_SIZE;
		}
	}

													/* Initialise FFT */
	SIF_Fft (pFFTCoeffs,							/* Pointer to FFT coefficients */
			 SIGLIB_NULL_ARRAY_INDEX_PTR,			/* Pointer to bit reverse address table - NOT USED */
			 FFT_LEN);								/* FFT Size */

	Display2DGraph (h2DGraph,						/* Graph handle */
				    "Real Input",					/* Title of the dataset */
				    pRealInput,						/* Array of Double dataset */
				    LEN,							/* Number of data points */
					SV_GRAPH_LINE,					/* Graph type */
				    SV_MAGENTA,						/* Colour */
					SV_HIDE_MARKERS,				/* Marker enable / disable */
					SV_GRAPH_NEW);					/* New graph */
	printf ("\nReal Input\nPlease hit <Carriage Return> to continue . . ."); getchar ();

	Display2DGraph (h2DGraph,						/* Graph handle */
				    "Real Output",					/* Title of the dataset */
				    pRealOutput,					/* Array of Double dataset */
				    LEN,							/* Number of data points */
					SV_GRAPH_LINE,					/* Graph type */
				    SV_MAGENTA,						/* Colour */
					SV_HIDE_MARKERS,				/* Marker enable / disable */
					SV_GRAPH_NEW);					/* New graph */
	printf ("\nReal Output\nPlease hit <Carriage Return> to continue . . ."); getchar ();

													/* Perform real FFT */
	SDA_Rfft (pRealInput,							/* Pointer to real array */
			  pImagInput,							/* Pointer to imaginary array */
			  pFFTCoeffs,							/* Pointer to FFT coefficients */
			  SIGLIB_NULL_ARRAY_INDEX_PTR,			/* Pointer to bit reverse address table - NOT USED */
			  FFT_LEN,								/* FFT size */
			  LOG_FFT_LEN);							/* log2 FFT size */

													/* Perform real FFT */
	SDA_Rfft (pRealOutput,							/* Pointer to real array */
			  pImagOutput,							/* Pointer to imaginary array */
			  pFFTCoeffs,							/* Pointer to FFT coefficients */
			  SIGLIB_NULL_ARRAY_INDEX_PTR,			/* Pointer to bit reverse address table - NOT USED */
			  FFT_LEN,								/* FFT size */
			  LOG_FFT_LEN);							/* log2 FFT size */

													/* Calculate real power from complex */
	SDA_LogMagnitude (pRealInput,					/* Pointer to real source array */
					  pImagInput,					/* Pointer to imaginary source array */
					  pResults,						/* Pointer to log magnitude destination array */
					  FFT_LEN);						/* Array length */

	Display2DGraph (h2DGraph,						/* Graph handle */
				    "Input spectrum",				/* Title of the dataset */
				    pResults,						/* Array of Double dataset */
				    LEN,							/* Number of data points */
					SV_GRAPH_LINE,					/* Graph type */
				    SV_MAGENTA,						/* Colour */
					SV_HIDE_MARKERS,				/* Marker enable / disable */
					SV_GRAPH_NEW);					/* New graph */
	printf ("\nInput spectrum\nPlease hit <Carriage Return> to continue . . ."); getchar ();

													/* Scale arrays for display */
	SDA_ComplexScalarDivide (pRealOutput,			/* Pointer to real source array */
							 pImagOutput,			/* Pointer to imaginary source array */
							 ((SLData_t)FFT_LEN),	/* Scalar divisor */
							 pRealOutput,			/* Pointer to real destination array */
							 pImagOutput,			/* Pointer to imaginary destination array */
							 FFT_LEN);				/* Array lengths */

													/* Calculate real power from complex */
	SDA_Magnitude (pRealOutput,						/* Pointer to real source array */
				   pImagOutput,						/* Pointer to imaginary source array */
				   pResults,						/* Pointer to log magnitude destination array */
				   FFT_LEN);						/* Array length */

	Display2DGraph (h2DGraph,						/* Graph handle */
				    "Output spectrum",				/* Title of the dataset */
				    pResults,						/* Array of Double dataset */
				    LEN,							/* Number of data points */
					SV_GRAPH_LINE,					/* Graph type */
				    SV_MAGENTA,						/* Colour */
					SV_HIDE_MARKERS,				/* Marker enable / disable */
					SV_GRAPH_NEW);					/* New graph */
	printf ("\nOutput spectrum\n");


	SUF_MemoryFree (pFFTCoeffs);					/* Free memory */
}


