///////////////////////////////////////
/// Audio 
/// compile with
/// gcc FPGA_audio_string_1.c -o str1 -lm -O3
/// *****
/// String PDE with "pluck" energy added as an initial condition
//  -- and with bowing provided by DDS with an envelope
/// *****
/// string fundamental frequency = Fs*sqrt(rho)/(2*(string_size-2))
/// The factor of 2 is there becuase we want to scale with the round-trip distance
/// of the wave on the string. 
/// Subtracting 2 for the zero end points.
/// Take the sqrt(rho) to make it ~speed of sound
/// For Fs=48kHz, rho=0.04, string_size=20 
/// string fundamental frequency = 250+/-5 Hz
/// see http://math.aalto.fi/~ksalo2/akusem/sem_FDM.pdf
/// eq 12 is Courant stability condition rho<1.0
///////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <math.h>
#include <sys/types.h>
#include <string.h>
// interprocess comm
#include <sys/ipc.h> 
#include <sys/shm.h> 
#include <sys/mman.h>
#include <time.h>
// network stuff
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 

// looks nicer
#define WAIT {}

//#include "address_map_arm_brl4.h"
// ===========================================
// virtual to real address pointers
// ===========================================
// the light weight physical buss base
#define HW_REGS_BASE          0xff200000
#define HW_REGS_SPAN          0x00005000 
// virtual pointer to base
void *h2p_lw_virtual_base;
// and bus addresses
// audio offset from bus base
#define AUDIO_BASE            0x00003040
volatile unsigned int * audio_base_ptr = NULL ;
volatile unsigned int * audio_fifo_data_ptr = NULL ; //4bytes
volatile unsigned int * audio_left_data_ptr = NULL ; //8bytes
volatile unsigned int * audio_right_data_ptr = NULL ; //12bytes

// string SRAM addresses
#define TRIGGER_ADDR    255
#define Uold_OFFSET     256
#define NOTE_TIME_ADDR  254
#define STR_LENGTH_ADDR 200
#define RHO_ADDR        201
#define ETA_ADDR        202

// full bus physical addresses
#define FPGA_SRAM_BASE      0xC0000000
#define FPGA_SRAM_SPAN      0x00001000
// virtual pointer to full bus base
void *h2p_virtual_base ;
// string buffer memory
volatile unsigned int * string_mem_ptr = NULL ;
// /dev/mem file descriptor for mmap
 int fd;
// ===========================================
// fixed pt macros suitable for 16-bit sound
// FPGA uses 2:25 fixed point
typedef signed int fix25 ;
#define float2fix25(a) ((fix25)((a)*33554432.0f)) // 2^25=33554432
#define fix2float25(a) (((float)(a))/33554432.0f) 
//#define multfix25(a,b) ((fix28)(((( signed long long)(a))*(( signed long long)(b)))>>25)) 
// shift fraction to 16-bit sound
#define fix2audio16(a) (a>>9)

// ============================================
// string parameters
float Fs = 48000 ;
// C scale
fix25 rho[8] ;
fix25 eta ;
int octave ;

// musical scale frequencies
//                 C4      D4      E4      F4      G4      A4      B4      C5
float notes[8] = { 261.63, 293.66, 329.63, 349.23, 392.00, 440.00, 493.88, 523.25 } ;
// octave scaling
float note_scale[6] = {0.0625, 0.125, 0.25, 0.50, 1.00, 2.00};
// string size paramenters and positions of bowing and output
int string_size_array[6] = {80, 80, 40, 40, 40, 20};
int string_out_position_array[6] = {20, 20, 10, 10, 10, 5};
int string_pluck_position_array[6] = {60, 60, 30, 30, 30, 15};
// the current values chosen from the above arrays
int string_size ;
int string_out_position ;
int string_pluck_position;
int copy_size ;
//
// the sample to read from the FPGA
fix25 string_output ;

// driving wave form
fix25 drive_wave ;
fix25 drive_table[256];
fix25 pluck_table[256];
float pluck_amp ;

// time that last note started
clock_t note_time ;


int main(void)
{
	// ===========================================
	// get FPGA addresses -> virtual addresses
	// ===========================================
    // Open /dev/mem
	if( ( fd = open( "/dev/mem", ( O_RDWR | O_SYNC ) ) ) == -1 ) 	{
		printf( "ERROR: could not open \"/dev/mem\"...\n" );
		return( 1 );
	}
    
    // get virtual addr that maps to physical light-weight bus
	h2p_lw_virtual_base = mmap( NULL, HW_REGS_SPAN, ( PROT_READ | PROT_WRITE ), MAP_SHARED, fd, HW_REGS_BASE );	
	if( h2p_lw_virtual_base == MAP_FAILED ) {
		printf( "ERROR: mmap1() failed...\n" );
		close( fd );
		return(1);
	}

	// audio addresses
	// base address is control register
	audio_base_ptr = (unsigned int *)(h2p_lw_virtual_base + AUDIO_BASE);
	audio_fifo_data_ptr  = audio_base_ptr  + 1 ; // word
	audio_left_data_ptr = audio_base_ptr  + 2 ; // words
	audio_right_data_ptr = audio_base_ptr  + 3 ; // words

	// get virtual addr that maps to physical full bus
	h2p_virtual_base = mmap( NULL, FPGA_SRAM_SPAN, ( PROT_READ | PROT_WRITE ), MAP_SHARED, fd, FPGA_SRAM_BASE);
	if( h2p_virtual_base == MAP_FAILED ) {
		printf( "ERROR: mmap1() failed...\n" );
		close( fd );
		return(1);
	}
	// get memory pointer
	string_mem_ptr =(unsigned int *)(h2p_virtual_base);
	
	printf("addr\n\r");
	
	// ===========================================
	// array index
	int i, j;
	// junk value
	int junk ;
	float eta_input ;
	// read the LINUX clock (microSec)
	// and set the time so that a note plays soon
	note_time = clock() - 1000000 ;
	//
	// choose freq range
	octave = 4;
	// pluck amplitude
	pluck_amp = 0.1 ;
	// eta
	eta_input = 0.9999 ;
	printf("octave(0-4), eta, pluck_amp\n\r");
	junk = scanf("%d %f %f", &octave, &eta_input, &pluck_amp);
	
	// frequencies range check
	if (octave<0) octave = 0;
	if (octave>5) octave = 5; 
	// get string length based on frequency
	string_size = string_size_array[octave];
    string_out_position = string_out_position_array[octave] + 1 ;
    string_pluck_position = string_pluck_position_array[octave] + 1 ;
	
	// damping conversion
	eta = float2fix25(eta_input) ;
	
	
	// compute the rhos for the scale
	for (i=0; i<8; i++){
		// rho = (2*(length-2)*f/Fs)**2
		rho[i] = float2fix25(pow(2.0*(string_size-2)*note_scale[octave]*notes[i]/Fs, 2.0));
		//rho[i] = float2fix25(1.0);
	}
	// set the first note to frequency index zero
	j = 0; 
	
	// compute the driving wavefrom table
	for (i=0; i<256; i++) { 
		//impulse + noise for bow drive
		//if (i<bow_width) drive_table[i] = float2fix25(bow_impulse) ;
		//else  drive_table[i] = float2fix25(bow_noise*(float)rand()/RAND_MAX) ;
		
		//noise for pluck drive
		pluck_table[i] = float2fix25(pluck_amp*(float)rand()/RAND_MAX) ;
		if (i>3) {
			pluck_table[i] = (pluck_table[i] + pluck_table[i-1] + pluck_table[i-2] + pluck_table[i-3])>>2 ;
		}
	}
		
	// send eta
	*(string_mem_ptr+ETA_ADDR) = eta;
	
	// send string length
	*(string_mem_ptr+STR_LENGTH_ADDR) = string_size ;
	
	// rho is sent in the loop below also
	*(string_mem_ptr+RHO_ADDR) = rho[0] ;
	
	/////////////////////
	//printf("start\n\r");
	/////////////////////
	
	while(1){	
		
		//printf("looping\n\r");
		// generate a drum simulation
		// load the FIFO until it is full
		////////////////////////////////////////////////
		//printf("%d", (*audio_fifo_data_ptr>>24)& 0xff);
		////////////////////////////////////////////////
		while (((*audio_fifo_data_ptr>>24)& 0xff) > 1) {
			// do string time sample on FPGA
			// by setting sample trigger address
			int trigger_value = 1 ;
			////////////////////
			//printf("in while");
			////////////////////
			*(string_mem_ptr+TRIGGER_ADDR) = trigger_value;
			//printf("%d", trigger_value);
			// wait for sample done (sample trigger zeroed by FPGA)
			while (*(string_mem_ptr+TRIGGER_ADDR)) WAIT ;
			/////////////
			//printf("*");
			/////////////
			
			// read samples back from FPGA SRAM
			string_output = *(string_mem_ptr + string_out_position) ;
			//string_output = pluck_table[i++ & 0xff] ;
			
			// send time sample to the audio FiFOs
			// 16 BIT SAMPLES
			*audio_left_data_ptr =  fix2audio16(string_output);
			*audio_right_data_ptr = fix2audio16(string_output);
			
		} // end while (((*audio	
		
		if (clock() - note_time > 1000000) {
			// cycle thru notes
			
			j++; 
			if(j>7) j=0;
			// send rho
			*(string_mem_ptr+RHO_ADDR) = rho[j] ;
			/////////////
			printf("note=%d\n\r", j);
			/////////////
			
			// read LINUX time for the next note
			note_time = clock();
			// zero the bow envelope generation
			
			// send a zero to the note time address
			// this effectively starts envelope generation for a bow action
			*(string_mem_ptr+NOTE_TIME_ADDR) = 0;
			
			// pluck the string with a harsh initial condition
			// and send the pluck amplitudes
			if (pluck_amp > 0) {
				for (i=1; i<string_size-2; i++){ 
					// the current position
					*(string_mem_ptr+i) = pluck_table[i] ;
					// the last position
					*(string_mem_ptr+i+Uold_OFFSET ) = pluck_table[i] ;
				}
			}
			
			// force zeros at end points.
			*(string_mem_ptr) = 0 ;
			*(string_mem_ptr+string_size-1) = 0 ;
			*(string_mem_ptr+string_size-2) = 0 ;			
		} // end if (clock() - note_time > 
	} // end while(1)
} // end main

