// 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   	96000 // 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][2][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;


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;
}

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];
    float temp;     
        
  
/* zero out left channel 
   for(i=0;i < BUFFER_LENGTH;i++){ 
		*pL = 0.0;
        pL++;
   }  */

 
/* zero out right channel 
   for(i=0;i < BUFFER_LENGTH;i++){ 
		*pR = 0.0;
        pR++;
   }    */


/* reverb on right channel   
   for(i=0;i < BUFFER_LENGTH-4;i++){ 
		*pR = *pR + (0.9 * pR[2]) + (0.45 * pR[4]);
        pR++;
   }              */
 
  
/* addition and subtraction 
   for(i=0;i < BUFFER_LENGTH;i++){ 
      temp = *pL;        
      *pL = temp + *pR; // left = L+R
      *pR = temp - *pR; // right = L-R 
      pL++;
      pR++;
    }   */
             
                   
/* add a sinusoid    
   for(i=0;i < BUFFER_LENGTH;i++){ 
      *pL = *pL + 1024*sinf(0.5*i);
      pL++;
    }    
*/                   
                   
/* AM modulation 
    for(i=0;i < BUFFER_LENGTH;i++){
      *pR = *pL * *pR; // right = L*R 
      *pL = *pL + *pR; // left = L*(1+R) 
      pL++;
      pR++;
    } 
*/
    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;

    // pack output data without bounding 
//  CodecData.channel[ LEFT] = buffer[dump_index][LEFT][sample_count];
//  CodecData.channel[RIGHT] = buffer[dump_index][RIGHT][sample_count];

    // 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
}
