/*
 * Delay.c
 * Delay filter the input and send it to the output.
 *
 * Author: Christopher Dobrian
 * Modified: Justin Myracks
 *
 * This program uses the PortAudio Portable Audio Library.
 * For more information see: http://www.portaudio.com
 *
 */

#include <stdio.h>
#include <math.h>
#include "portaudio.h" /* contains PortAudio functions */

#define NUM_SECONDS   (30) /* program will run for this number of seconds */
#define SAMPLE_RATE   (44100)
#define BUFFER_SIZE   (256) /* PortAudio callback will occur every 256 frames, 6 ms or so */
#define PIPE_SIZE (512) /* size of the delay line for the comb filters, 12 ms or so */

// this data structure will hold the most recently received audio data,
// and the number of samples in the past at which we will 'tap' into the recent past data,
// for both the left and right audio channels
// 

typedef struct {
	float leftpipe[PIPE_SIZE];
	float rightpipe[PIPE_SIZE];
	int left_delaysamps;
	int right_delaysamps;
} paAudioData;

//This routine will be called by the PortAudio engine when audio is needed. */

static int combCallback( void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, PaTimestamp outTime, void *userData )
{
	/* Cast data passed through stream to the format of the local structure. */
	paAudioData *data = (paAudioData*)userData; /* the data structure that is outside of this function */
	float *in = (float*)inputBuffer; /* point a local pointer to the input stream */
	float *out = (float*)outputBuffer; /* point a local pointer to the output stream */
	
	unsigned int i; /* a counter */
	int current_address; /* index (in the 'pipe') of the sample that represents the present moment */
	int left_delayedaddress; /* index of the sample that represents the desired moment in the past for the left delay line */
	int right_delayedaddress; /* index of the sample that represents the desired moment in the past for the right delay line */

	current_address = (int)outTime % PIPE_SIZE; /* use PA's current time number, modulo the pipe size, to get the current index in the pipe */
	left_delayedaddress = current_address - data->left_delaysamps; /* these two lines set the delay tap index for the left channel... */
	while ( left_delayedaddress < 0 ) left_delayedaddress += PIPE_SIZE; /* wrapping around if necessary, to be certain to stay within the array */
	right_delayedaddress = current_address - data->right_delaysamps; /* these two lines set the delay tap index for the right channel... */
	while ( right_delayedaddress < 0 ) right_delayedaddress += PIPE_SIZE; /* wrapping around if necessary, to be certain to stay within the array */
	
	/* now, for every sample... */
	for( i = 0 ; i < framesPerBuffer ; i++ )
	{	
		/* set the indices of the pipe */	
		current_address = (current_address+i)%PIPE_SIZE;
		left_delayedaddress = (left_delayedaddress+i)%PIPE_SIZE;
		right_delayedaddress = (right_delayedaddress+i)%PIPE_SIZE;
		
		/* put the current sample into the pipe, using data from the input stream (pointed to by *in) */
		data->leftpipe[current_address] = *in++;
		data->rightpipe[current_address] = *in++;
		
		/* add the current sample and a delayed sample,
		 * multiply the sum by 0.5 to keep them within range,
		 * and write the result to the output stream (pointed to by *out
		 */
		*out++ = ((data->leftpipe[current_address])+(data->leftpipe[left_delayedaddress]))*0.5; /* left */
		*out++ = ((data->rightpipe[current_address])+(data->rightpipe[right_delayedaddress]))*0.5; /* right */
	}

	return 0;
}

/*******************************************************************/

static paAudioData data;

int main(void);

int main(void)
{
	PortAudioStream *stream; /* a pointer to a PaAudioStream is needed by PortAudio */
	PaError err; /* we'll use this to contain error reports from PortAudio */
	
	unsigned int i; /* a counter */

	printf("PortAudio: Simple Delay.\n");

/* Initialize data for use by callback. */
	data.left_delaysamps = 0; /* Delay equals zero to remove resonating harmonics */
	data.right_delaysamps = 0; /* Delay equals zero to remove resonating harmonics*/
	/* initialize all array values to 0 so that "the past" is silent before we start */
	for ( i = 0 ; i < PIPE_SIZE ; i++ ) {
		data.leftpipe[i] = 0.0;
		data.rightpipe[i] = 0.0;
	}

/* Initialize library before making any other calls. */
	err = Pa_Initialize();
	if( err != paNoError ) goto error;

/* Open an audio I/O stream. */
	err = Pa_OpenDefaultStream(
				&stream,	/* the address of a PaAudioStream pointer */
				2,              /* two input channels */
				2,              /* stereo output */
				paFloat32,      /* 32 bit floating point output */
				SAMPLE_RATE,	/* the audio sampling rate */
				BUFFER_SIZE,            /* frames per buffer */
				0,              /* number of buffers, if zero then use default minimum */
				combCallback,	/* name of the callback function */
				&data );	/* address of a pointer to the 'data' structure containing the delay line info */
	if( err != paNoError ) goto error;

/* Start calling the callback function repeatedly (in background). */
	err = Pa_StartStream( stream );
	if( err != paNoError ) goto error;

/* Sleep for several seconds. This is just a quick and dirty way to let the program run and then stop itself. */
	Pa_Sleep(NUM_SECONDS*1000);

/* Stop calling the callback function. */
	err = Pa_StopStream( stream );
	if( err != paNoError ) goto error;

/* Release the audio stream. */
	err = Pa_CloseStream( stream );
	if( err != paNoError ) goto error;

/* Quit PortAudio, and quit the program. */
	Pa_Terminate();
	printf("Finished.\n");
	return err;

/* If there are any errors reported by PortAudio, we'll do this: */
error:
	Pa_Terminate();
	fprintf( stderr, "An error occured while using the portaudio stream.\n" ); 
	fprintf( stderr, "Error number: %d\n", err );
	fprintf( stderr, "Error message: %s\n", Pa_GetErrorText( err ) );
	return err;
}
