///////////////////////////////////////
/// Audio 
/// compile with
/// gcc FPGA_audio_string_4.c -o str4 -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
///
/// this version setting up for more than one string
///////////////////////////////////////
#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 offset addresses from each string base address
#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
#define STRING1_BASE        0x00000000
#define STRING2_BASE        0x00000400
#define STRING3_BASE        0x00000800
#define STRING4_BASE        0x00000c00

// virtual pointer to full bus base
void *h2p_virtual_base ;
// string buffer memorys
volatile unsigned int * string1_mem_ptr = NULL ;
volatile unsigned int * string2_mem_ptr = NULL ;
volatile unsigned int * string3_mem_ptr = NULL ;
volatile unsigned int * string4_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};
//int string_pluck_position_array[6] = {70, 70, 35, 35, 35, 17};
// 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 ;

// pluck wave form
fix25 pluck_table[256];
float pluck_amp ;
float tringle_table[256];

// bowing variables
// rise/fall time of impulse in 48 kHz samples
int bow_rise=1000, bow_fall=1000, bow_sustain=1 ; 
// amplitude <1.0
float bow_amp, current_bow_amp ;
// amplitude change per sample
float bow_rise_inc, bow_fall_inc ;
// turning relative to string fundamental, impulse width
float bow_tune, bow_width ;
// bow force-- how mcuh the bow surpresses the wave 0 to 8
int bow_force ;
// DDS for bowing waveform
unsigned int bow_DDS_accum, bow_DDS_inc[8] ;
#define two32 4294967296
// bow waveform
float drive_table[256];
// bow driving wave sample
fix25 drive_wave ;

// time that last note started
clock_t note_time ;
// in sample cycles
int note_sample_count ;


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
	string1_mem_ptr =(unsigned int *)(h2p_virtual_base + STRING1_BASE);
	
	// ===========================================
	// array index
	int i, j;
	// junk value
	int junk ;
	float eta_input, pos_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 ;
	
	// freq  damp  pluck bow                                           
    // octave eta,  amp, amp, rise, sus, fall, tune,  wide
	printf("octave(0-4), eta, pluck_amp, bow_amp, rise, sus, fall, tune, width, position\n\r");
	junk = scanf("%d %f %f %f %d %d %d %f %f %f", &octave, &eta_input, &pluck_amp,
		&bow_amp, &bow_rise, &bow_sustain, &bow_fall, &bow_tune, &bow_width, &pos_input);
	
	// bow parameters range check
	if (bow_rise<1) bow_rise = 1;
	if (bow_fall<1) bow_fall = 1;
	if (bow_sustain<1) bow_sustain = 1;
	// set up increments for calculating bow envelope
	bow_rise_inc = bow_amp/(float)bow_rise ;
	bow_fall_inc = bow_amp/(float)bow_fall ;
	
	// 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 ;
	
	// move the peak of the pluck and/or position of bowing
    //string_pluck_position = string_pluck_position_array[octave] + 1 ;
	string_pluck_position = (int)((float)string_size * pos_input) ;
	if (string_pluck_position<2) string_pluck_position = 2;
	if (string_pluck_position>string_size-4) string_pluck_position = string_size-4;
	
	// 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));
		// bow DDS
		bow_DDS_inc[i] = (unsigned int)(note_scale[octave]*notes[i]*bow_tune/Fs*two32);
	}
	// set the first note to frequency index zero
	j = 0; 
	
	// compute the driving wavefrom tables
	for (i=0; i<256; i++) { 
		// triangle
		
		// Triangle pluck
		tringle_table[i] = (i<=string_pluck_position)? ((float)i/(float)string_pluck_position) :
				(1-(((float)i-string_pluck_position)/(float)(string_size-string_pluck_position))) ;
		pluck_table[i] = float2fix25(pluck_amp * tringle_table[i]) ;
		
		//bow drive impulse 
		if (i<bow_width) drive_table[i] = 1.0 ;
		else  drive_table[i] = 0 ;
		
		// Gaussian pluck
		//pluck_table[i] = float2fix25(triangle_n[i]*famp*exp(-(float)(i-string_pluck)*(i-string_pluck)/(fsd*fsd)));
		
		//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 + lowpassed version
		//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
	*(string1_mem_ptr+ETA_ADDR) = eta;
	
	// send string length
	*(string1_mem_ptr+STR_LENGTH_ADDR) = string_size ;
	
	// rho is sent in the loop below also
	*(string1_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 ;
			*(string1_mem_ptr+TRIGGER_ADDR) = trigger_value;
			
			// wait for sample done (sample trigger zeroed by FPGA)
			while (*(string1_mem_ptr+TRIGGER_ADDR)) WAIT ;
			
			// read samples back from FPGA SRAM
			string_output = *(string1_mem_ptr + string_out_position) ;
			
			// send time sample to the audio FiFOs
			// 16 BIT SAMPLES
			*audio_left_data_ptr =  fix2audio16(string_output);
			*audio_right_data_ptr = fix2audio16(string_output);
			
			// update bow drive evelope amd waveform
			// and add to string
			if (note_sample_count < (bow_rise + bow_fall + bow_sustain)){
				current_bow_amp = (note_sample_count <= bow_rise)? 
					current_bow_amp + bow_rise_inc : 
					(note_sample_count <= bow_rise + bow_sustain)? current_bow_amp:
						current_bow_amp - bow_fall_inc ;
				// update bow drive DDS sample	
				// driving function DDS at string fundamental
				bow_DDS_accum += bow_DDS_inc[j] ;
				
				// driving wave amplitude sine
				drive_wave = float2fix25(current_bow_amp * drive_table[bow_DDS_accum>>24]);	
				//drive_wave = (int) drive_table[bow_DDS_accum>>24];
				//if (drive_wave>0) printf("%d\n\r", drive_wave);
				// add the bow amplitude to the string model
				*(string1_mem_ptr + string_pluck_position) = 
					(*(string1_mem_ptr + string_pluck_position)) + (drive_wave) ;
			}
			else {
				drive_wave = 0 ;
			}	
			
			//if (note_sample_count==500){
			//	*(string_mem_ptr + string_pluck_position) = 
			//		*(string_mem_ptr + string_pluck_position) + float2fix25(0.25) ;
			//}
			
			// advance bow claculation time
			note_sample_count++ ;
		} // end while (((*audio	
		
		// --- new note ---
		// start a new note: send parameters, load initial conditions
		if (clock() - note_time > 1000000) {
			// cycle thru notes
			
			j++; 
			if(j>7) j=0;
			// send rho
			*(string1_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
			// this effectively starts envelope generation for a bow action
			note_sample_count = 0;
			current_bow_amp = 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
					*(string1_mem_ptr+i) = pluck_table[i] ;
					// the last position
					*(string1_mem_ptr+i+Uold_OFFSET ) = pluck_table[i] ;
				}
			//}
			
			// force zeros at end points.
			*(string1_mem_ptr) = 0 ;
			// fixed end
			*(string1_mem_ptr+string_size-1) = 0 ;
			*(string1_mem_ptr+string_size-2) = 0 ;
			// free end
			//*(string_mem_ptr+string_size-1) = *(string_mem_ptr+string_size-3) ;
			//*(string_mem_ptr+string_size-2) = *(string_mem_ptr+string_size-3) ;				
		} // end if (clock() - note_time > 
	} // end while(1)
} // end main

