// Welch, Wright, & Morrow, 
// Real-time Digital Signal Processing, 2005

///////////////////////////////////////////////////////////////////////
// Filename: ISRs.c
//
// Synopsis: Interrupt service routines for McBSP transmit and receive
//
///////////////////////////////////////////////////////////////////////

#include "..\..\..\Common_Code\DSK_Config.h" 
#include "math.h"
#include "frames.h"  
  
// Data is received from the PCM3006 codec as 2 16-bit words (left/right) 
// packed into one 32-bit word.  The union allows the data to be accessed 
// as a single entity when transferring to and from the serial port, but 
// still be able to manipulate the left and right channels independently.
#define LEFT  0
#define RIGHT 1
volatile union {
	unsigned int UINT;
	short Channel[2];
	} CodecData;

// frame buffer declarations
#define BUFFER_LENGTH   	1024  // buffer length in samples 
#define NUM_CHANNELS    	2     // supports stereo audio 
#define NUM_BUFFERS     	3     // don't change 
#define INITIAL_FILL_INDEX	0     // start filling this buffer
#define INITIAL_DUMP_INDEX	1     // start dumping this buffer

#pragma DATA_SECTION (buffer, "CE0"); // allocate buffers in SDRAM 
volatile float buffer[NUM_BUFFERS][NUM_CHANNELS][BUFFER_LENGTH];
// there are 3 buffers in use at all times, one being filled by McBSP1_Rx_ISR,
// one being operated on, and one being emptied by McBSP1_Tx_ISR
// fill_index   --> buffer being filled by the ADC
// dump_index --> buffer being written to the DAC
// ready_index --> buffer ready for processing
short buffer_ready = 0, over_run = 0, ready_index = 2;

// fft defines
#include "fft.h"
#define N BUFFER_LENGTH
COMPLEX x[N], W[N];
float y[N];


void ZeroBuffers() 
////////////////////////////////////////////////////////////////////////
// Purpose:   Sets all buffer locations to 0.0 
//
// Input:     None
//
// Returns:   Nothing
//
// Calls:     Nothing
//
// Notes:     None
///////////////////////////////////////////////////////////////////////
{
    int i = BUFFER_LENGTH * NUM_BUFFERS * NUM_CHANNELS;
    volatile float *p = buffer[0][0];

    while(i--)
        *p++ = 0.0;
        
    init_W(N, W); // initialize fft twiddle factors
}

void ProcessBuffer()
///////////////////////////////////////////////////////////////////////
// Purpose:   Processes the data in buffer[ready_index] and stores
//  		  the results back into the buffer 
//
// Input:     None
//
// Returns:   Nothing
//
// Calls:     Nothing
//
// Notes:     None
///////////////////////////////////////////////////////////////////////
{   
	int i;
    volatile float *pL = buffer[ready_index][LEFT];
//    volatile float *pR = buffer[ready_index][RIGHT];
    static int frame_count = 0;    
        

/* copy left channel to complex array */ 
   for(i=0;i < BUFFER_LENGTH;i++){ 
		x[i].real = *pL++;
		x[i].imag = 0;
   }  
   
	/* calculate the FFT of x[N] */
	fft_c(N, x, W);

/* get magnitude of complex array */ 
   for(i=0;i < BUFFER_LENGTH;i++){ 
		y[i] = sqrtf(x[i].real*x[i].real + x[i].imag*x[i].imag);
   }  

	if(frame_count++ > 0) {
		frame_count = 0; // put probe point here
	}
    buffer_ready = 0;
}

///////////////////////////////////////////////////////////////////////
// Purpose:   Access function for buffer ready flag 
//
// Input:     None
//
// Returns:   Non-zero when a buffer is ready for processing
//
// Calls:     Nothing
//
// Notes:     None
///////////////////////////////////////////////////////////////////////
int IsBufferReady()
{
    return buffer_ready;
}

///////////////////////////////////////////////////////////////////////
// Purpose:   Access function for buffer overrun flag 
//
// Input:     None
//
// Returns:   Non-zero if a buffer overrun has occurred
//
// Calls:     Nothing
//
// Notes:     None
///////////////////////////////////////////////////////////////////////
int IsOverRun()
{
    return over_run;
}
    
    
interrupt void McBSP_Rx_ISR()
///////////////////////////////////////////////////////////////////////
// Purpose:   McBSP receive interrupt service routine.  Codec data is
//            stored in the global variable CodecData. 
//
// Input:     None
//
// Returns:   Nothing
//
// Calls:     Nothing
//
// Notes:     None
///////////////////////////////////////////////////////////////////////
{                              
    static short fill_index = INITIAL_FILL_INDEX; // index of buffer to fill
    static int sample_count = 0; // current sample count in buffer
	volatile McBSP *port;

	if(CodecType == TLC320AD535)
		port = McBSP0_Base;			// McBSP0 used with TLC320AD535
	else
		port = McBSP1_Base;			// McBSP1 used with codec daughtercards
    CodecData.UINT = port->drr; 	// get input data from serial port
	
    // store in buffer
    buffer[fill_index][LEFT][sample_count] = CodecData.Channel[LEFT];
    buffer[fill_index][RIGHT][sample_count] = CodecData.Channel[RIGHT];
    
    /* update sample count and swap buffers when filled */
    if(++sample_count >= BUFFER_LENGTH) {
        sample_count = 0;
        ready_index = fill_index;
        if(++fill_index >= NUM_BUFFERS)
            fill_index = 0;
        if(buffer_ready == 1) /* set a flag if buffer isn't processed in time */
            over_run = 1;
        buffer_ready = 1;
    }
}


interrupt void McBSP_Tx_ISR()
///////////////////////////////////////////////////////////////////////
// Purpose:   McBSP transmit interrupt service routine.  Codec data 
//            stored in the global variable CodecData is sent to the
//            codec. 
//
// Input:     None
//
// Returns:   Nothing
//
// Calls:     Nothing
//
// Notes:     None
///////////////////////////////////////////////////////////////////////
{
    static short dump_index = INITIAL_DUMP_INDEX; // index of buffer to dump
    static int sample_count = 0; // current sample count in buffer
	volatile McBSP *port;
	
    // bound output data before packing
	// use saturation of SPINT to limit to 16-bits
	CodecData.Channel[ LEFT] = _spint(buffer[dump_index][ LEFT][sample_count] * 65536) >> 16;
	CodecData.Channel[RIGHT] = _spint(buffer[dump_index][RIGHT][sample_count] * 65536) >> 16;

   // update sample count and swap buffers when filled 
    if(++sample_count >= BUFFER_LENGTH) {
        sample_count = 0;
        if(++dump_index >= NUM_BUFFERS)
            dump_index = 0;
    }
									// now, send the data to the codec
	if(CodecType == TLC320AD535) {
		port = McBSP0_Base;			// McBSP0 used with TLC320AD535
		CodecData.UINT &= 0xfffffffe;// mask off LSB to prevent codec reprogramming
		}
	else {
		port = McBSP1_Base;			// McBSP1 used with codec daughtercards
	}
	
	port->dxr = CodecData.UINT; 	// send output data to serial port
}
