#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/time.h>
#include <time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/soundcard.h>
#include <errno.h>
#include <unistd.h>
#include <math.h>
#include <sys/sysinfo.h>
#include "../../common/buffer.h"
#include "../../common/jackmixer.h"
#include "../../common/looperdata.h"
#include "../../common/shuttle.h"
#include "../../common/tranzport.h"
#include "../../common/serialio.h"
#include "../../common/speciallist.h"

#ifndef SOUND_MIXER_READ
# define SOUND_MIXER_READ(x) MIXER_READ(x)
#endif
#ifndef SOUND_MIXER_WRITE
# define SOUND_MIXER_WRITE(x) MIXER_WRITE(x)
#endif

#ifdef MAX
#  undef MAX
#endif
#ifdef MIN
#  undef MIN
#endif

#define MAX(a,b) (((a)>(b))?(a):(b))
#define MIN(a,b) (((a)<(b))?(a):(b))

typedef struct _looper_settings {
	char  		name[21];
	looper_data_t* 	looper_data;
} looper_settings_t;

int 	startup_linelevel;
int 	last_foot = 0;
int	last_footled = 0;
double 	last_tap = 0;
struct 	timeval timeout_tv;
long	tranzport_lcd_timeout;
int	tranzport_mode = 0;
int	tranzport_status = TRANZPORT_STATUS_OFFLINE;
char    *lcd_value_text = NULL;
char	*lcd_stereo_buf = NULL;
speciallist_t *settingslist = NULL;
looper_settings_t *active_settings = NULL;

const float initial_length = 0.05; /* in seconds (was 0.05)*/
const float initial_speed = 1;
const float initial_ngrains = 15;
const float max_ngrains = 15;
const float initial_volume  = 0.9;
const int initial_graindensity = 100;
const double mintimediff = 20000.;
const double semitone = 1.059463094359;

int ossmixer_getlevel_line (){
	int fd = -1;
	int vol = 0;

	fd = open("/dev/mixer", O_RDONLY);
	if (fd > 0){
		if (ioctl(fd, SOUND_MIXER_READ(SOUND_MIXER_LINE), &vol) == -1){
			close (fd);
			return -2;
		} else {
			close (fd);
			return (vol & 255);
		}
	} 
	return -1;
}

int ossmixer_getlevel_pcm (){
        int fd = -1;
        int vol = 0;

        fd = open("/dev/mixer", O_RDONLY);
        if (fd > 0){
                if (ioctl(fd, SOUND_MIXER_READ(SOUND_MIXER_PCM), &vol) == -1){
                        close (fd);
                        return -2;
                } else {
                        close (fd);
                        return (vol & 255);
                }
        }
        return -1;
}
void ossmixer_setlevel_line (int vol){
	int fd = -1;
	int svol = vol | (vol << 8);
	fd = open("/dev/mixer", O_WRONLY);
	ioctl(fd, SOUND_MIXER_WRITE(SOUND_MIXER_LINE), &svol);
	close (fd);
}

void ossmixer_setlevel_pcm (int vol){
        int fd = -1;
        int svol = vol | (vol << 8);
        fd = open("/dev/mixer", O_WRONLY);
        ioctl(fd, SOUND_MIXER_WRITE(SOUND_MIXER_PCM), &svol);
        close (fd);
}

static double midi2pitch (int note){
        double pitch = pow(2.,(double)(note - 48.)/12.);
        return pitch;
}

void set_distortion (looper_data_t *ld, float dist){
	if (dist < 0.) dist = 0.;
	if (dist > 1.) dist = 1.;	
	looperdata_lock(ld);
        looperdata_set_vol(ld, initial_volume + (dist * 10));
        looperdata_set_limknee (ld, .6 - (dist * .57));
        looperdata_set_limit (ld, 1. - (dist * .9));
        looperdata_set_recmix(ld, 1. - (dist * .5)); /* also see get_distortion */
        looperdata_unlock(ld);
}

float get_distortion (looper_data_t *ld){
	return (1. - looperdata_get_recmix(ld)) * 2.;
}

void reset_looperdata(looper_data_t *looper_data, jack_info_t *jack_info){
        looperdata_set_playing(looper_data,1);
        looperdata_set_recording(looper_data,1);
        looperdata_set_ngrains(looper_data, initial_ngrains);
        looperdata_set_graindensity(looper_data, initial_graindensity);
	looperdata_set_mingrainspeed(looper_data, initial_speed);
	looperdata_set_maxgrainspeed(looper_data, initial_speed);
        looperdata_set_speed(looper_data, initial_speed);
        looperdata_set_vol(looper_data, initial_volume);
	looperdata_set_recmix(looper_data, 1);
        looperdata_set_loopend(looper_data, jack_info->samplerate * initial_length); /* .1 sec */
        looperdata_set_recend(looper_data, jack_info->samplerate  * initial_length);
        looperdata_set_mingrainlength(looper_data, jack_info->samplerate  * initial_length);
        looperdata_set_maxgrainlength(looper_data, jack_info->samplerate  * initial_length);
	looperdata_set_stereodiversity(looper_data, 0.);
	looperdata_set_playpos (looper_data, 0);
	looperdata_set_recpos (looper_data, 0);
	set_distortion(looper_data,0);
} 

void serialio_pollall (serialio_data_t  *serialio, speciallist_t *looperdatalist){
	looper_data_t *wld;
	int i;

	wld = speciallist_get_first(looperdatalist);	
	
	if (wld->isrecording != last_footled){
		if (wld->isrecording)
			serialio_set_footled(serialio,0);
		else
			serialio_set_footled(serialio,1);

		last_footled = wld->isrecording	;
	}

	if (!wld->isrecording){
		serialio_toggle_footled(serialio);
	}

	/* check footswitch */
	if ((i = serialio_get_foot(serialio)) && !last_foot){
		printf ("foot changed\n");
		if (wld->isrecording){
			serialio_set_footled(serialio,1);
			while (wld){
				wld->isrecording = 0;
				wld = speciallist_get_next(looperdatalist,wld);
			}
		}else{
			serialio_set_footled(serialio,0);
			while (wld){
				wld->isrecording = 1;
				wld = speciallist_get_next(looperdatalist,wld);
			}
		}
	}

/*	printf ("last_foot:%d, i:%d\n",last_foot,i);*/
	last_foot = i;
}

int set_lcd (tranzport_t *tranzport, const char* lcd0, int offset1, const char* lcd1, int offset2, int timeout){
	struct timeval tv;
	int ok = 0;

	if (lcd0) ok += tranzport_write_lcd(tranzport,lcd0,offset1,0);
	else ok += tranzport_write_lcd(tranzport,"                    ",0,0);
	if (lcd1) ok += tranzport_write_lcd(tranzport,lcd1,offset1,1);
	else if (!lcd_stereo_buf) ok += tranzport_write_lcd(tranzport,"                    ",0,1);

	if (lcd_value_text){
		free (lcd_value_text); /* reset text */
		lcd_value_text = NULL;
	}
	if (lcd_stereo_buf){
		free (lcd_stereo_buf);
		lcd_stereo_buf  = NULL;
	}

	gettimeofday(&tv,0);
	if (timeout) tranzport_lcd_timeout = (tv.tv_sec  * 10 ) + (long)(tv.tv_usec / 100000) + timeout;
	else tranzport_lcd_timeout = 0;		

	return ok;
}

void set_lcd_slider_stereo (tranzport_t *tranzport, long left, long right, long min, long max){
	char charbuf[2];
        int sliderleft = (int)((float)(left - min) / (float)(max - min) * 40.);
	int sliderright = (int)((float)(right - min) / (float)(max - min) * 40.);
	int i = 0;


        if (!lcd_stereo_buf){
                tranzport_write_lcd(tranzport,"                   ",0,1);
		lcd_stereo_buf = malloc(21 * sizeof(char));
		memset (lcd_stereo_buf,0,21);
        }

	for (i = 0; i < 39; i += 2){
		if (sliderleft < i){ 						/* left empty */
			if (sliderright < i) 		charbuf[0] = 0x20;/* both empty */
			else if (sliderright == i) 	charbuf[0] = 0x02;/* left none, right one */
			else 			 	charbuf[0] = 0x05;/* left none, right two */
		}else if (sliderleft == i){ 					/* left == one */
			if (sliderright < i) 		charbuf[0] = 0x08;/* left one, right none */
                        else if (sliderright == i) 	charbuf[0] = 0x03;/* left one, right one */
                        else 				charbuf[0] = 0x06;/* left one, right two */
		}else { 							/* left = both */
			if (sliderright < i) 	 	charbuf[0] = 0x01;/* left two, right none */
                        else if (sliderright == i) 	charbuf[0] = 0x04;/* left two, right one */
                        else 				charbuf[0] = 0x07;/* left two, right two */
		}
		charbuf[1] = 0x00;
		if (strncmp((char*)(lcd_stereo_buf + (int)(i/2)),(char*)&charbuf,1)){
			tranzport_write_lcd(tranzport,(char*)&charbuf,(int)(i/2),1);
			lcd_stereo_buf[(int)(i/2)] = charbuf[0];
		}
	}
}


void set_lcd_value (tranzport_t *tranzport, const char* text, long value, long min, long max){
	char *buf  = malloc (21 * sizeof(char));
	struct timeval tv;
	int maxlen;

	snprintf (buf,20,"%ld",max);
	maxlen = strlen(buf);

	if (!lcd_value_text){
		lcd_value_text = malloc (21 *  sizeof(char));
		memset(lcd_value_text,0,21);
	}

	if (!strncmp(lcd_value_text,text,20)){
		snprintf (buf,20,"%ld                    ",value);
		tranzport_write_lcd(tranzport,buf,(20 - maxlen),0);
		set_lcd_slider_stereo(tranzport,value, value, min, max);

	}else{
		strncpy (lcd_value_text,text,20);
		snprintf (buf,20,"%s                    ",text);
		tranzport_write_lcd(tranzport,buf,0,0);	
		snprintf (buf,20,"%ld                    ",value);
                tranzport_write_lcd(tranzport,buf,(20 - maxlen),0);		
		set_lcd_slider_stereo(tranzport,value,value, min, max);
	}
	
	gettimeofday(&tv,0);    
	tranzport_lcd_timeout = (tv.tv_sec * 10) + (long)(tv.tv_usec / 100000) + 50;

	free(buf);
}

void set_lcd_value_float (tranzport_t *tranzport, const char* text, float value, float min, float max){
        char *buf  = malloc (21 * sizeof(char));
        struct timeval tv;
        int maxlen;

        snprintf (buf,20,"%d",(int)max);
        maxlen = strlen(buf) + 5;

        if (!lcd_value_text){
		lcd_value_text = malloc (21 * sizeof(char));
		memset(lcd_value_text,0,21);
	}

        if (!strncmp(lcd_value_text,text,20)){
                snprintf (buf,20,"%.4f                    ",value);
                tranzport_write_lcd(tranzport,buf,(20 - maxlen),0);
        }else{
                strncpy (lcd_value_text,text,20);
                snprintf (buf,20,"%s                    ",text);
                tranzport_write_lcd(tranzport,buf,0,0); 
                snprintf (buf,20,"%.4f                    ",value);
                tranzport_write_lcd(tranzport,buf,(20 - maxlen),0);
                tranzport_write_lcd(tranzport,"                    ",0,1);
        }


        gettimeofday(&tv,0);
        tranzport_lcd_timeout = (tv.tv_sec * 10) + (long)(tv.tv_usec / 100000) + 50;

        free(buf);
}


void tranzport_pollall (tranzport_t *tranzport, speciallist_t *looperdatalist, jack_info_t *jack_info){
        looper_data_t *wld;
	struct timeval tv;
	int wheel = 0;

	/* LATER: include a clock */

	/* check the state of serial foot */
	wld = speciallist_get_first(looperdatalist);
	if (!looperdata_get_recording(wld)  &&  !tranzport_get_led(tranzport,TRANZPORT_LIGHT_RECORD)){
                tranzport_set_led(tranzport,TRANZPORT_LIGHT_RECORD,1);
		set_lcd (tranzport,"      LOOPING       ",0,NULL,0,50);
	}
	if (looperdata_get_recording(wld)  &&  tranzport_get_led(tranzport,TRANZPORT_LIGHT_RECORD)){
               	tranzport_set_led(tranzport,TRANZPORT_LIGHT_RECORD,0);
		set_lcd (tranzport,"     RECORDING      ",0,NULL,0,50);
	}


        while (!tranzport_do_io(tranzport)){
        	if (tranzport_get_status(tranzport) == TRANZPORT_STATUS_OFFLINE){
			printf ("offline!\n");
			if(lcd_value_text){
                		free (lcd_value_text); /* reset text */
                		lcd_value_text = NULL;
			}
//			tranzport_status = TRANZPORT_STATUS_OFFLINE;
			return;
		}

		if (tranzport_status == TRANZPORT_STATUS_OFFLINE){
			tranzport_status = TRANZPORT_STATUS_ONLINE;
			tranzport_clear_leds(tranzport);
			set_lcd (tranzport," klopfer says hello! ",0,"  kluppe.klingt.org  ",0,30);
		}

		/* ****************** CHECK the BUTTONS: ************ */
		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_TRACKLEFT)){
			int l = 0;
			if (tranzport_buttonstate(tranzport, TRANZPORT_BUTTON_SHIFT)){
				l = 0;
				ossmixer_setlevel_pcm(l);
			}else{
				l = ossmixer_getlevel_pcm();
				l -= 5;
				if (tranzport_buttonstate(tranzport, TRANZPORT_BUTTON_SHIFT)) l -= 5;
				if (l < 0) l = 0;
				ossmixer_setlevel_pcm(l);
			}
			set_lcd_value (tranzport,"OutputVolume",l,0,100);			
		}
		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_TRACKRIGHT)){
			int l;
			if (tranzport_buttonstate(tranzport, TRANZPORT_BUTTON_SHIFT)){
				l = 100;
                                ossmixer_setlevel_pcm(l);
			}else{
				l = ossmixer_getlevel_pcm();
                        	l += 5;
				if (tranzport_buttonstate(tranzport, TRANZPORT_BUTTON_SHIFT)) l += 5;
                        	if (l > 100) l = 100;
                        	ossmixer_setlevel_pcm(l);
			}
                        set_lcd_value (tranzport,"OutputVolume",l,0,100);      
		}

		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_RECORD | TRANZPORT_FOOTSWITCH)){
			wld = speciallist_get_first(looperdatalist);
                	if (looperdata_get_recording(wld)){
				/* LED AND LCD is DONE at the top of this function */
                                while (wld){
                                	looperdata_set_recording(wld,0);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
                        }else{
                               	while (wld){
                               		looperdata_set_recording(wld,1);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
                        }
		} /* RECORD BUTTON PRESSED */

		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_PLAY)){
			wld = speciallist_get_first(looperdatalist);
                	if (looperdata_get_playing(wld)){
				set_lcd (tranzport,"      THROUGH       ",0,NULL,0,50);
                                while (wld){
                                        looperdata_set_playing(wld,0);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
                                ossmixer_setlevel_line(startup_linelevel);
                        }else{
				set_lcd (tranzport,"      PROCESS       ",0,NULL,0,50);
                                while (wld){
                                        looperdata_set_playing(wld,1);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
                                ossmixer_setlevel_line(0);
                        }
                } /* PLAY BUTTON PRESSED */

		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_TRACKREC)){
			if (tranzport_mode & TRANZPORT_BUTTON_TRACKREC){
				tranzport_mode ^= TRANZPORT_BUTTON_TRACKREC;
				tranzport_set_led(tranzport, TRANZPORT_LIGHT_TRACKREC,0);
				set_lcd (tranzport,"SpreadStereoBtn off ",0,"                    ",0,20);
			}else{
				float f;
				tranzport_mode |= TRANZPORT_BUTTON_TRACKREC;
				tranzport_set_led(tranzport, TRANZPORT_LIGHT_TRACKREC,1);
				wld = speciallist_get_first(looperdatalist);
				f = looperdata_get_stereodiversity(wld);
				set_lcd_value_float (tranzport,"SpreadStereo",f,0.,1.);
			}
		} /* TRACKREC BUTTON == PITCH SHIFT PRESSED */

                if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_TRACKMUTE)){
                        if (tranzport_mode & TRANZPORT_BUTTON_TRACKMUTE){
                                tranzport_mode ^= TRANZPORT_BUTTON_TRACKMUTE;
                                tranzport_set_led(tranzport, TRANZPORT_LIGHT_TRACKMUTE,0);
                                set_lcd (tranzport,"LoopLengthButton off",0,"                    ",0,20);
                        }else{
                                long d;
                                tranzport_mode |= TRANZPORT_BUTTON_TRACKMUTE;
                                tranzport_set_led(tranzport, TRANZPORT_LIGHT_TRACKMUTE,1);
                                wld = speciallist_get_first(looperdatalist);
                                d = looperdata_get_mingrainlength(wld);
                                set_lcd_value (tranzport,"LoopLength",d,0, jack_info->samplerate  * 60);
                        }
                } /* TRACKMUTE BUTTON == LOOP LENGTH PRESSED */


		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_TRACKSOLO)){
                        if (tranzport_mode & TRANZPORT_BUTTON_TRACKSOLO){
                                tranzport_mode ^= TRANZPORT_BUTTON_TRACKSOLO;
                                tranzport_set_led(tranzport, TRANZPORT_LIGHT_TRACKSOLO,0);
                                set_lcd (tranzport,"DistortionButton off",0,"                    ",0,20);
                        }else{
				float f;
                                tranzport_mode |= TRANZPORT_BUTTON_TRACKSOLO;
                                tranzport_set_led(tranzport, TRANZPORT_LIGHT_TRACKSOLO,1);
                                wld = speciallist_get_first(looperdatalist);
                                f = get_distortion(wld);
                                set_lcd_value (tranzport,"Distortion",(int)(f * 100),0, 100);
                        }
                } /* TRACKMUTE BUTTON == LOOP LENGTH PRESSED */

		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_PUNCH)){
                        if (tranzport_mode & TRANZPORT_BUTTON_PUNCH){
                                tranzport_mode ^= TRANZPORT_BUTTON_PUNCH;
                                tranzport_set_led(tranzport, TRANZPORT_LIGHT_PUNCH,0);
                                set_lcd (tranzport,"GrainDensityBtn off ",0,"                    ",0,20);
                        }else{
				long d;
                                tranzport_mode |= TRANZPORT_BUTTON_PUNCH;
                                tranzport_set_led(tranzport, TRANZPORT_LIGHT_PUNCH,1);
				wld = speciallist_get_first(looperdatalist);
				d = looperdata_get_graindensity(wld);
				set_lcd_value (tranzport,"GrainDensity",d,1,100);
                        }
                } /* PUNCH BUTTON == FRAIN DENSITY PRESSED */

		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_LOOP)){
                        if (tranzport_mode & TRANZPORT_BUTTON_LOOP){
                                tranzport_mode ^= TRANZPORT_BUTTON_LOOP;
                                tranzport_set_led(tranzport, TRANZPORT_LIGHT_LOOP,0);
                                set_lcd (tranzport,"NrGrainsBtn off     ",0,"                    ",0,20);
                        }else{
				long d;
                                tranzport_mode |= TRANZPORT_BUTTON_LOOP;
                                tranzport_set_led(tranzport, TRANZPORT_LIGHT_LOOP,1);
				wld = speciallist_get_first(looperdatalist);
                                d = looperdata_get_ngrains(wld);
				set_lcd_value (tranzport,"Nr. of Grains",d,1,max_ngrains);
                        }
                } /* LOOP BUTTON == NRofGRAINS PRESSED */

		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_IN)){
                        if (tranzport_mode & TRANZPORT_BUTTON_IN){
                                tranzport_mode ^= TRANZPORT_BUTTON_IN;
                                tranzport_set_led(tranzport, TRANZPORT_LIGHT_ANYSOLO,0);
                                set_lcd (tranzport,"ReverbTime off     ",0,"                    ",0,20);
                        }else{
                                long d;
                                tranzport_mode |= TRANZPORT_BUTTON_IN;
                                tranzport_set_led(tranzport, TRANZPORT_LIGHT_ANYSOLO,1);
                                wld = speciallist_get_first(looperdatalist);
                                d = looperdata_get_reverblength(wld);
                                set_lcd_value (tranzport,"ReverbTime",((d * 1000) / jack_info->samplerate),0,1000);
                        }
                }
		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_OUT)){
                        if (tranzport_mode & TRANZPORT_BUTTON_OUT){
                                tranzport_mode ^= TRANZPORT_BUTTON_OUT;
                                tranzport_set_led(tranzport, TRANZPORT_LIGHT_ANYSOLO,0);
                                set_lcd (tranzport,"Feedback off     ",0,"                    ",0,20);
                        }else{
                                float f;
                                tranzport_mode |= TRANZPORT_BUTTON_OUT;
                                tranzport_set_led(tranzport, TRANZPORT_LIGHT_ANYSOLO,1);
                                wld = speciallist_get_first(looperdatalist);
                                f = looperdata_get_reverblength(wld);
                                set_lcd_value_float (tranzport,"Feedback",f,0.,1.);
                        }
                }



		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_NEXT)){
			char msg[21] = "                     ";
			active_settings = speciallist_get_next(settingslist,active_settings);
			if (!active_settings) active_settings = speciallist_get_first(settingslist);
			snprintf (msg,21,"preset: %s           ",active_settings->name);
			set_lcd (tranzport,msg,0,NULL,0,20);	
		}
		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_PREV)){
			char msg[21] = "                     ";	
			active_settings = speciallist_get_previous(settingslist,active_settings);
			if (!active_settings) active_settings = speciallist_get_last(settingslist);
			snprintf (msg,21,"preset: %s           ",active_settings->name);
                        set_lcd (tranzport,msg,0,NULL,0,20);
		}
		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_REWIND)){
			float f;
                        wld = speciallist_get_first(looperdatalist);
                        f = looperdata_get_mingrainspeed(wld);
                        f *= pow(semitone,-1.);
                        if (tranzport_buttonstate(tranzport, TRANZPORT_BUTTON_SHIFT))
                                f *= pow(semitone,-1.);
                        if ((f > 0.0001) && (f < 100.)){
                                while (wld){    /* grain speed */
                                        looperdata_lock(wld);
                                        looperdata_set_mingrainspeed(wld,f);
                                        looperdata_set_maxgrainspeed(wld,f);
                                        looperdata_unlock(wld);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
                                set_lcd_value_float (tranzport,"GrainPitch",f,0.,100.);
                        }
                }
		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_FASTFORWARD)){
			float f;
                        wld = speciallist_get_first(looperdatalist);
                        f = looperdata_get_mingrainspeed(wld);
                        f *= pow(semitone,1.);
                        if (tranzport_buttonstate(tranzport, TRANZPORT_BUTTON_SHIFT))
                                f *= pow(semitone,1.);
                        if ((f > 0.0001) && (f < 100.)){
                                while (wld){    /* grain speed */
                                        looperdata_lock(wld);
                                        looperdata_set_mingrainspeed(wld,f);
                                        looperdata_set_maxgrainspeed(wld,f);
                                        looperdata_unlock(wld);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
                                set_lcd_value_float (tranzport,"GrainPitch",f,0.,100.);
                        }
                }
		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_STOP)){
			while (wld){    /* grain speed */
				looperdata_lock(wld);
                                looperdata_set_mingrainspeed(wld,1.);
                                looperdata_set_maxgrainspeed(wld,1.);
                                looperdata_unlock(wld);
                                wld = speciallist_get_next(looperdatalist,wld);
                        }
			set_lcd_value_float (tranzport,"GrainPitch",1.,0.,100.);
		}
		

		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_ADD)){
			char msg[21] = "                     ";
                        wld = speciallist_get_first(looperdatalist);
                        while (wld){
				if (active_settings){
					int isrec = looperdata_get_recording(wld);
					looperdata_lock(wld);
					looperdata_copy_settings(wld,active_settings->looper_data);
					looperdata_set_recording(wld,isrec);
					looperdata_unlock(wld);
				}
                                wld = speciallist_get_next(looperdatalist,wld);
                        }
			snprintf (msg,21,"active: %s            ",active_settings->name);
			set_lcd (tranzport,msg,0,NULL,0,20);
                }	


		/* ***************** RESET *************************/
		if (tranzport_pressed(tranzport, TRANZPORT_BUTTON_UNDO)){
/*
			if (tranzport_mode & TRANZPORT_BUTTON_TRACKREC){
				set_lcd_value_float (tranzport,"GrainPitch",1.,0.,100.);
                                wld = speciallist_get_first(looperdatalist);
                                while (wld){  
                                        looperdata_lock(wld);
                                        looperdata_set_mingrainspeed(wld,1.);
                                        looperdata_set_maxgrainspeed(wld,1.);
                                        looperdata_unlock(wld);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
                        }
*/

			if (tranzport_mode & TRANZPORT_BUTTON_TRACKREC){
                                set_lcd_value_float (tranzport,"SpreadStereo",0.,0.,1.);
                                wld = speciallist_get_first(looperdatalist);
                                while (wld){  
                                        looperdata_lock(wld);
                                        looperdata_set_stereodiversity(wld,0.);
                                        looperdata_unlock(wld);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
                        }

			
			if (tranzport_mode & TRANZPORT_BUTTON_TRACKMUTE){
				long d = jack_info->samplerate * initial_length;
                                set_lcd_value (tranzport,"LoopLength",d ,0, jack_info->samplerate * 60);
                                wld = speciallist_get_first(looperdatalist);
                                while (wld){    /* grain speed */
                                        looperdata_lock(wld);
					looperdata_set_maxgrainlength (wld, d);
                                        looperdata_set_loopend (wld, d);
                                        looperdata_set_recend (wld, d);
                                        looperdata_set_mingrainlength(wld,d);
                                        looperdata_unlock(wld);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
			}
			if (tranzport_mode & TRANZPORT_BUTTON_TRACKSOLO){
                                set_lcd_value (tranzport,"Distortion",0,0,100);
                                wld = speciallist_get_first(looperdatalist);
                                while (wld){  
					set_distortion(wld,0.);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
                        }
			if (tranzport_mode & TRANZPORT_BUTTON_LOOP){
				set_lcd_value (tranzport,"Nr. of Grains",max_ngrains,1,max_ngrains);
				wld = speciallist_get_first(looperdatalist);
                                while (wld){    /* grain speed */
                                        looperdata_lock(wld);
					looperdata_set_ngrains(wld,max_ngrains);
					looperdata_unlock(wld);
                                        wld = speciallist_get_next(looperdatalist,wld);
				}
                        }
			if (tranzport_mode & TRANZPORT_BUTTON_PUNCH){
				set_lcd_value (tranzport,"GrainDensity",100,0,100);
				wld = speciallist_get_first(looperdatalist);
                                while (wld){    /* grain speed */
                                        looperdata_lock(wld);
                                        looperdata_set_graindensity(wld,100.);
                                        looperdata_unlock(wld);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
                        }
			if (tranzport_mode & TRANZPORT_BUTTON_IN){
				set_lcd_value (tranzport,"ReverbTime",100,0,1000);
				wld = speciallist_get_first(looperdatalist);
                                while (wld){    /* grain speed */
                                        looperdata_lock(wld);
                                        looperdata_set_reverblength(wld,100.);
                                        looperdata_unlock(wld);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
			}	
			if (tranzport_mode & TRANZPORT_BUTTON_OUT){
                                set_lcd_value_float (tranzport,"Feedback",0.,0.,1.);
                                wld = speciallist_get_first(looperdatalist);
                                while (wld){    /* grain speed */
                                        looperdata_lock(wld);
                                        looperdata_set_feedback(wld,0.);
                                        looperdata_unlock(wld);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
                        }       

			/* restart */
			if (tranzport_buttonstate(tranzport, TRANZPORT_BUTTON_SHIFT)){
				set_lcd (tranzport,"        EXIT        ",0,"  kluppe.klingt.org ",0,1);
				exit(0);
			}
		} /* end RESET */
		
	
		/* ***************************   SHUTTLE WHEEL ************************** */
		if ((wheel = tranzport_get_wheel(tranzport))){
			if (!tranzport_mode){
				float f;
				wld = speciallist_get_first(looperdatalist);
				f = looperdata_get_mingrainspeed(wld);
				if (tranzport_buttonstate(tranzport, TRANZPORT_BUTTON_SHIFT))
					f *= 1 + ((double)wheel * .1);
				else f *= 1 + ((double)wheel * .01);
				if ((f > 0.0001) && (f < 100.)){
                               		while (wld){    /* grain speed */
                                        	looperdata_lock(wld);
                                        	looperdata_set_mingrainspeed(wld,f);
                                        	looperdata_set_maxgrainspeed(wld,f);
                                        	looperdata_unlock(wld);
                                        	wld = speciallist_get_next(looperdatalist,wld);
                                	}
					set_lcd_value_float (tranzport,"GrainPitch",f,0.,100.);
				}
			} /* end pitch shift == NO BUTTON PRESSED */

			if (tranzport_mode & TRANZPORT_BUTTON_TRACKREC){
				float f;
				wld = speciallist_get_first(looperdatalist);
				f = looperdata_get_stereodiversity(wld);
				f += (double)wheel * .05;
				if (tranzport_buttonstate(tranzport, TRANZPORT_BUTTON_SHIFT))
					f += (double)wheel * .05;
				if (f < 0.) f = 0.;
				if (f > 1.) f = 1.;
				while (wld){
					looperdata_lock(wld);
					looperdata_set_stereodiversity(wld,f);
					looperdata_unlock(wld);
                                        wld = speciallist_get_next(looperdatalist,wld);
				}
				set_lcd_value_float (tranzport,"SpreadStereo",f,0.,1.);
			}/* end stereo diversity = TRACKREC */

			if (tranzport_mode & TRANZPORT_BUTTON_TRACKMUTE){
				long d = 0;
				wld = speciallist_get_first(looperdatalist);
                        	while (wld){ /* grain length */
                                	looperdata_lock(wld);
                                	if (looperdata_get_mingrainlength(wld) > 40){
                                        	d = looperdata_get_mingrainlength(wld);
						d = (long)((float)d * ( 1. + (float)wheel * .03));
                                	} else d = looperdata_get_mingrainlength(wld) + wheel;

                                	if (d > buffer_get_size(wld->buf)) d = buffer_get_size(wld->buf);
                                	if (d < 3) d = 3;
                                	looperdata_set_maxgrainlength (wld, d);
                                	looperdata_set_loopend (wld, d);
                                	looperdata_set_recend (wld, d);
                                        looperdata_set_mingrainlength(wld,d);
                                	looperdata_unlock(wld);
                                	wld = speciallist_get_next(looperdatalist,wld);
				}
				set_lcd_value (tranzport,"LoopLength",d,1,jack_info->samplerate  *  60);
			}

                        if (tranzport_mode & TRANZPORT_BUTTON_TRACKSOLO){
                                float f;
                                wld = speciallist_get_first(looperdatalist);
                                f = get_distortion(wld);
				f += wheel * .05;
				if (f < 0.) f = 0.;
				if (f > 1.) f = 1.;
                                while (wld){    /* grain speed */
					set_distortion(wld,f);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
                                set_lcd_value (tranzport,"Distortion",(int)(f * 100),0,100);
                        } /* end pitch shift == TRACKSOLO */


			if (tranzport_mode & TRANZPORT_BUTTON_LOOP){
				long d = 0;
				wld = speciallist_get_first(looperdatalist);
                        	while (wld){ 
                                	d = looperdata_get_ngrains(wld);
                                        d += wheel;
                                        if (d < 1) d = 1;
                                        if (d > max_ngrains) d = max_ngrains; 
                                        looperdata_lock(wld);
/*                                      printf ("ngrains:%d\n", diff);*/
                                        looperdata_set_ngrains(wld, d);
                                        looperdata_unlock(wld);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }	
				set_lcd_value (tranzport,"Nr. of Grains",d,1,max_ngrains);
			}

                     	if (tranzport_mode & TRANZPORT_BUTTON_PUNCH){
				long d = 0;
				wld = speciallist_get_first(looperdatalist);
                                while (wld){    
                                        d = looperdata_get_graindensity(wld);
					if (tranzport_buttonstate(tranzport, TRANZPORT_BUTTON_SHIFT))
						d += wheel * 10;
					else d += wheel * wheel * wheel;
					if (d < 1) d = 1;
					if (d > 100) d = 100;
					looperdata_lock(wld); 
					looperdata_set_graindensity(wld, d);
					looperdata_unlock(wld);
					wld = speciallist_get_next(looperdatalist,wld);
                                }     
                                set_lcd_value (tranzport,"GrainDensity",d,1,100);
			} /* end if PUNCH */

			if (tranzport_mode & TRANZPORT_BUTTON_IN){
				long d = 0;
                                wld = speciallist_get_first(looperdatalist);
                                while (wld){
                                        d = looperdata_get_reverblength(wld);
                                        if (tranzport_buttonstate(tranzport, TRANZPORT_BUTTON_SHIFT))
                                                d += wheel * wheel * wheel;
                                        else d += wheel * wheel * wheel * 250;
                                        if (d < 1) d = 1;
                                        if (d > 44100) d = 44100;
                                        looperdata_lock(wld);
                                        looperdata_set_reverblength(wld, d);
                                        looperdata_unlock(wld);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
                                set_lcd_value (tranzport,"ReverbTime (ms)",((d * 1000) / jack_info->samplerate),0,1000);
			}/* end if IN */
			if (tranzport_mode & TRANZPORT_BUTTON_OUT){
                                float f = 0;
                                wld = speciallist_get_first(looperdatalist);
                                while (wld){
                                        f = looperdata_get_feedback(wld);
                                        if (tranzport_buttonstate(tranzport, TRANZPORT_BUTTON_SHIFT))
                                                f += wheel * .1;
                                        else f += wheel * wheel * wheel / 100.;
                                        if (f < 0.) f = 0.;
                                        if (f > 1.) f = 1.;
                                        looperdata_lock(wld);
                                        looperdata_set_feedback(wld, f);
                                        looperdata_unlock(wld);
                                        wld = speciallist_get_next(looperdatalist,wld);
                                }
                                set_lcd_value_float (tranzport,"Feedback",f,0.,1.);
                        }/* end if OUT */
		}/* end if wheel */
	} /* end while poll */



	if (tranzport_lcd_timeout){
		/* idle lcd ? */
		gettimeofday(&tv,0);
		if (((tv.tv_sec * 10) + (long)(tv.tv_usec / 100000)) >= tranzport_lcd_timeout){
			wld = speciallist_get_first(looperdatalist);
			if (looperdata_get_playing(wld)){
				float vL,vR;
				set_lcd (tranzport,ctime(&tv.tv_sec),0,NULL,0,1);

				wld = speciallist_get_first(looperdatalist);
				looperdata_get_recmeters(wld, &vL, &vR);
                		vL = 20.0f * log10f(vL);
				vR = 20.0f * log10f(vR);
				if (vL > -50.) vL += 50.;
				else vL = 0.;
				if (vR > -50.) vR += 50.;
                                else vR = 0.;
				set_lcd_slider_stereo(tranzport,(int)vL,(int)vR,0,50);
			}else	set_lcd (tranzport,ctime(&tv.tv_sec),0,"    SOUND THROUGH    ",0,10);
				
		}
	} /* end of idle stuff */
}


void shuttle_pollall (shuttle_data_t  *shuttle, speciallist_t *looperdatalist, jack_info_t *jack_info){
	looper_data_t *wld;

	wld = speciallist_get_first(looperdatalist);

	if (shuttle->fd < 1) return; /* not so clever */

	if (shuttle->fd < 1) {
		shuttle_open(shuttle);
		if (shuttle->fd > 0) printf ("shuttle device found\n");
	}

	if (shuttle->fd < 1) return;

	while (shuttle_poll (shuttle) > 0){
		switch (shuttle->code){
			case SHUTTLE_LEFTMOST :/* play */
				if ((!shuttle->value) && (shuttle->lastcode == shuttle->code)){
					if (wld->isplaying){
						while (wld){
							wld->isplaying = 0;
							wld = speciallist_get_next(looperdatalist,wld);
						}
						ossmixer_setlevel_line(startup_linelevel);
					}else{
						while (wld){
							wld->isplaying = 1;
							wld = speciallist_get_next(looperdatalist,wld);
						}
						ossmixer_setlevel_line(0);
					}
				}
				break;
			case SHUTTLE_LEFT : /* invert grain speed */
				if ((!shuttle->value) && (shuttle->lastcode == shuttle->code)){
					while (wld){
                                        	looperdata_lock(wld);
						looperdata_set_mingrainspeed(wld,
							looperdata_get_mingrainspeed(wld) * -1.);
                                        	looperdata_set_maxgrainspeed(wld,
							looperdata_get_maxgrainspeed(wld) * -1.);
						looperdata_unlock(wld);
						wld = speciallist_get_next(looperdatalist,wld);
					} 
				}
				break;
			case SHUTTLE_MIDDLE : /* reset */
				if ((!shuttle->value) && (shuttle->lastcode == shuttle->code)){
					if (shuttle->buttonstates & SHUTTLE_LEFTMOST){
						while (wld){
							looperdata_lock(wld);
							looperdata_set_loopend (wld, 
								jack_info->samplerate * initial_length);
                                                	looperdata_set_recend (wld, 
								jack_info->samplerate * initial_length);
                                                	looperdata_set_maxgrainlength (wld, 
								jack_info->samplerate * initial_length);
                                                        looperdata_set_mingrainlength(wld, 
								jack_info->samplerate * initial_length);
							looperdata_unlock(wld);
							wld = speciallist_get_next(looperdatalist,wld);
						}
					} else if (shuttle->buttonstates & SHUTTLE_LEFT){
						while (wld){
                                                	looperdata_lock(wld);
                                                	looperdata_set_mingrainspeed(wld,1.);
                                                	looperdata_set_maxgrainspeed(wld,1.);
                                                	looperdata_unlock(wld);
                                                	wld = speciallist_get_next(looperdatalist,wld);
                                        	}       
					} else if (shuttle->buttonstates & SHUTTLE_RIGHTMOST){
						while (wld){
							looperdata_lock(wld);
							looperdata_set_recpos(wld,
								looperdata_get_recstart(wld));
							looperdata_unlock(wld);
							wld = speciallist_get_next(looperdatalist,wld);
						}
					} else if (shuttle->buttonstates & SHUTTLE_RIGHT){
					} else {
						while (wld){
							looperdata_lock(wld);
							reset_looperdata (wld, jack_info);
							looperdata_unlock(wld);
                                                        wld = speciallist_get_next(looperdatalist,wld);
						}
					}
				}
				break;
			case SHUTTLE_RIGHT : /* distortion */
				if ((!shuttle->value) && (shuttle->lastcode == shuttle->code)){
					if (looperdata_get_vol(wld) < 1.){
						while (wld){
							looperdata_lock(wld);
							looperdata_set_vol(wld, 3);
							looperdata_set_limknee (wld, .1);
							looperdata_set_limit (wld, .4);
							looperdata_set_recmix(wld, .6);
							looperdata_unlock(wld);
							wld = speciallist_get_next(looperdatalist,wld);	
						}
					} else {
						while (wld){
							looperdata_lock(wld);
							looperdata_set_vol(wld, .9);
							looperdata_set_limknee (wld, .6);
							looperdata_set_limit (wld, 1.);
							looperdata_set_recmix(wld, 1.);
							looperdata_unlock(wld);
							wld = speciallist_get_next(looperdatalist,wld);
						}
					}
				}
				break;
			case SHUTTLE_RIGHTMOST :  /* tap loop length */
				if ((!shuttle->value) && (shuttle->lastcode == shuttle->code)){
					struct timeval tv;
					double now;
					long diff; /* milliseconds between last two events */
					long since; /* milliseconds since event */

					gettimeofday(&tv,NULL);		
					since = (tv.tv_sec - shuttle->lastevent.tv_sec)	 * 1000;
					since += (long)((tv.tv_usec - shuttle->lastevent.tv_usec) / 1000.);

					now = (double)shuttle->lastevent.tv_sec * 1000. + (double)shuttle->lastevent.tv_usec / 1000.;
					diff = (long)(now - last_tap);
					if (diff < 3000){
						while (wld){
							looperdata_lock(wld);
							looperdata_set_playpos(wld, since * jack_info->samplerate / 1000);
							looperdata_set_recpos(wld, since * jack_info->samplerate / 1000);

							looperdata_set_loopend(wld, diff * jack_info->samplerate / 1000);
							looperdata_set_recend(wld, diff * jack_info->samplerate / 1000);
							looperdata_unlock(wld);
							wld = speciallist_get_next(looperdatalist,wld);
						}
					}
/*					printf ("now:%lf, last:%lf, since:%ld, diff:%ld\n",now, last_tap, since, diff);*/
					last_tap = now;
					
				}
#if 0
				/* record */
				if ((!shuttle->value) && (shuttle->lastcode == shuttle->code)){
					if (wld->isrecording){
						while (wld){
							wld->isrecording = 0;
							wld = speciallist_get_next(looperdatalist,wld);
						}
					}else{
						while (wld){
							wld->isrecording = 1;
							wld = speciallist_get_next(looperdatalist,wld);
						}
					}
				}
#endif
				break;
			case SHUTTLE_SHUTTLE :
				if (!shuttle->buttonstates){
					while (wld){ /* no button = number of grains */
						int d = looperdata_get_ngrains(wld);
						int diff = d + (shuttle->shuttledelta / 4);
						if (diff < 1) diff = 1;
						if (diff > max_ngrains) diff = max_ngrains;
						if ((diff > 0) && (diff < 16)){
							looperdata_lock(wld);
/*							printf ("ngrains:%d\n", diff);*/
							looperdata_set_ngrains(wld, diff);
							looperdata_unlock(wld);
						}
						wld = speciallist_get_next(looperdatalist,wld);	
					}
				} else if (shuttle->buttonstates & SHUTTLE_LEFT){
					while (wld){	/* grain speed */
                                		float f = looperdata_get_mingrainspeed(wld);
/*                                		f = f + f * .0002 * (double)(shuttle->jogstate * shuttle->jogstate * shuttle->jogstate);*/
						f *= pow(semitone,(double)shuttle->shuttledelta);
/*						printf ("factor:%lf, f:%lf\n",pow(semitone,(double)shuttle->shuttledelta),f);*/
                                		if ((f > 0) && (f < 100)){
                                        		looperdata_lock(wld);
                                        		looperdata_set_mingrainspeed(wld,f);
                                        		looperdata_set_maxgrainspeed(wld,f);
                                        		looperdata_unlock(wld);
                                		}
                                		wld = speciallist_get_next(looperdatalist,wld);
                        		}
				} else if (shuttle->buttonstates & SHUTTLE_LEFTMOST){ /* grain length */
					while (wld){
						long d = looperdata_get_mingrainlength (wld) + shuttle->shuttledelta;
 	                                	if (d > buffer_get_size(wld->buf)) d = buffer_get_size(wld->buf);
                                		if (d < 3) d = 3;
						looperdata_lock(wld);
                                		looperdata_set_loopend (wld, d);
                                		looperdata_set_recend (wld, d);
	                        		looperdata_set_maxgrainlength (wld, d);
                                        	looperdata_set_mingrainlength(wld,(long)(d));
                                		looperdata_unlock(wld);
                                		wld = speciallist_get_next(looperdatalist,wld);
					}
				}
				break;
		}
	}

	if (shuttle->jogstate){
		if (!shuttle->buttonstates){
			while (wld){ /* grain density */
				int d = looperdata_get_graindensity(wld);
				if ((d + shuttle->jogstate > 1) && ((d + (int)( shuttle->jogstate / 4)) <= 100)){
					looperdata_lock(wld);
					looperdata_set_graindensity(wld, d + (int)(shuttle->jogstate / 4));
					looperdata_unlock(wld);
				} /* this will most likely not go down to 1. */
				wld = speciallist_get_next(looperdatalist,wld);
			}
		} else if (shuttle->buttonstates & SHUTTLE_LEFTMOST){
			while (wld){ /* grain length */
				looperdata_lock(wld);
				long d;
/*
				float f  = (float)looperdata_get_mingrainlength(wld)
                                        / (float)looperdata_get_maxgrainlength(wld);
*/

				if (looperdata_get_mingrainlength(wld) > 20){
					double f = (double)looperdata_get_mingrainlength(wld) * .0002 *
                                                        (double)(shuttle->jogstate * shuttle->jogstate * shuttle->jogstate);
					if (f > 0.0) f += 0.999;
					else  f -= 0.999;
					d = looperdata_get_mingrainlength(wld) + (long)f;
				} else d = looperdata_get_mingrainlength(wld) + shuttle->jogstate;

				if (d > buffer_get_size(wld->buf)) d = buffer_get_size(wld->buf);
				if (d < 3) d = 3;
				looperdata_set_maxgrainlength (wld, d);
                                looperdata_set_loopend (wld, d);
                                looperdata_set_recend (wld, d);
/*				if ((long)(d) > 5)*/
					looperdata_set_mingrainlength(wld,(long)(d));
				looperdata_unlock(wld);
/*
				printf ("2:size:%ld, min:%ld, max:%ld\n",
					d,looperdata_get_mingrainlength(wld),looperdata_get_maxgrainlength(wld));
*/
				wld = speciallist_get_next(looperdatalist,wld);
                        }
		} else if (shuttle->buttonstates & SHUTTLE_LEFT){
                        while (wld){ /* grain speed */
				/* grain speed */
				float f = looperdata_get_mingrainspeed(wld);
				f = f + f * .0002 * (double)(shuttle->jogstate * shuttle->jogstate * shuttle->jogstate);
				if ((f > 0) && (f < 100)){
                                	looperdata_lock(wld);
/*                                	looperdata_set_speed(wld,f);*/
                                	looperdata_set_mingrainspeed(wld,f);
                                	looperdata_set_maxgrainspeed(wld,f);
                                	looperdata_unlock(wld);
				}
/*                              printf ("speed:%f\n",looperdata_get_speed(wld));*/
                                wld = speciallist_get_next(looperdatalist,wld);
                        }
                } else if (shuttle->buttonstates & SHUTTLE_RIGHTMOST){
			while (wld){ /* just the minimum grain length */
				float f = (float)looperdata_get_mingrainlength(wld) 
					/ (float)looperdata_get_maxgrainlength(wld);
				f = f + f * .0005 * (float)(shuttle->jogstate * shuttle->jogstate * shuttle->jogstate);
				if (f > 1.) f = 1.;
				if (f <= .1) f = .1;
				if ((long)(looperdata_get_maxgrainlength(wld) * f) > 10){
					looperdata_lock(wld);
					looperdata_set_mingrainlength(wld,(long)(looperdata_get_maxgrainlength(wld) * f));
					looperdata_unlock(wld);
				}
/*				printf ("mingrain:%ld, maxgrain:%ld, f:%f\n",
					looperdata_get_mingrainlength(wld),looperdata_get_maxgrainlength(wld),f);*/
				wld = speciallist_get_next(looperdatalist,wld);
			}
		}
	}
	return;
}

#if 0
void sigproc(){
	 signal(SIGINT, sigproc); /*  */
	 /* NOTE some versions of UNIX will reset signal to default
	 after each call. So for portability reset signal each time */
}

void quitproc(){
	printf ("exiting\n");
	exit(0);
}

#endif

void create_settings(buffer_info_t* buf, jack_info_t* jack_info){
	looper_data_t *ld;
	looper_settings_t *ls;
	double note;

	settingslist = speciallist_new();	

	/* plain and empty */	
	ld = looperdata_new(buf, jack_info->samplerate, 1);
	reset_looperdata(ld,jack_info);
	ld->isrecording = 1;
	ls = malloc(sizeof(looper_settings_t));
	ls->looper_data = ld;
	snprintf((char*)&ls->name,20,"PLAIN");
	speciallist_append(settingslist,(void*)ls);
	active_settings = ls;

	/* dominantseptakkord */
	ld = looperdata_new(buf, jack_info->samplerate, 1);
	reset_looperdata(ld,jack_info);
	looperdata_lock(ld);
        looperdata_set_grainpitchmode(ld,GRAINPITCH_CHORD);
        note = midi2pitch(48);
        looperdata_set_grainpitchtablevalue (ld, note, 1.0);
        note = midi2pitch(51);
        looperdata_set_grainpitchtablevalue (ld, note, 1.0);
        note = midi2pitch(54);
        looperdata_set_grainpitchtablevalue (ld, note, 1.0);
        note = midi2pitch(56);
        looperdata_set_grainpitchtablevalue (ld, note, 1.0);
	looperdata_set_stereodiversity(ld,0.);
	ld->isrecording = 1;
        looperdata_unlock(ld);
        ls = malloc(sizeof(looper_settings_t));
        ls->looper_data = ld;
        snprintf((char*)&ls->name,20,"D7");
        speciallist_append(settingslist,(void*)ls);

	/* resonator / ringmod-like */
	ld = looperdata_new(buf, jack_info->samplerate, 1);
        reset_looperdata(ld,jack_info);
        looperdata_lock(ld);
	looperdata_set_ngrains(ld,1);
        looperdata_set_stereodiversity(ld,0.4);
        looperdata_set_maxgrainlength (ld, 56);
        looperdata_set_loopend (ld, 56);
        looperdata_set_recend (ld, 56);
        looperdata_set_mingrainlength(ld,56);
        looperdata_set_stereodiversity(ld,0.);
        ld->isrecording = 1;
        looperdata_unlock(ld);
	set_distortion(ld,0.6);
        ls = malloc(sizeof(looper_settings_t));
        ls->looper_data = ld;
        snprintf((char*)&ls->name,20,"Resonator");
        speciallist_append(settingslist,(void*)ls);

	/* dominantseptakkord miniloop distorted*/
        ld = looperdata_new(buf, jack_info->samplerate, 1);
	reset_looperdata(ld,jack_info);
        looperdata_lock(ld);
        looperdata_set_grainpitchmode(ld,GRAINPITCH_CHORD);
        note = midi2pitch(48);
        looperdata_set_grainpitchtablevalue (ld, note, 1.0);
        note = midi2pitch(51);
        looperdata_set_grainpitchtablevalue (ld, note, 1.0);
        note = midi2pitch(54);
        looperdata_set_grainpitchtablevalue (ld, note, 1.0);
        note = midi2pitch(56);
        looperdata_set_grainpitchtablevalue (ld, note, 1.0);
	looperdata_set_maxgrainlength (ld, 256);
        looperdata_set_loopend (ld, 256);
        looperdata_set_recend (ld, 256);
        looperdata_set_mingrainlength(ld,256);
	looperdata_set_stereodiversity(ld,0.);
	ld->isrecording = 1;
        looperdata_unlock(ld);
        set_distortion(ld,1.);  
        ls = malloc(sizeof(looper_settings_t));
        ls->looper_data = ld;
        snprintf((char*)&ls->name,20,"D7 DIST 256");
        speciallist_append(settingslist,(void*)ls);

	/*  miniloop */
        ld = looperdata_new(buf, jack_info->samplerate, 1);
        reset_looperdata(ld,jack_info);
        looperdata_lock(ld);
	looperdata_set_maxgrainlength (ld, 13);
        looperdata_set_loopend (ld, 13);
        looperdata_set_recend (ld, 13);
        looperdata_set_mingrainlength(ld,13);
	looperdata_set_stereodiversity(ld,0.);
        ld->isrecording = 1;
        looperdata_unlock(ld);
        ls = malloc(sizeof(looper_settings_t));
        ls->looper_data = ld;
        snprintf((char*)&ls->name,20,"LENGTH 13");
        speciallist_append(settingslist,(void*)ls);

	
        /*  miniloop */
        ld = looperdata_new(buf, jack_info->samplerate, 1);
        reset_looperdata(ld,jack_info);
        looperdata_lock(ld);
	looperdata_set_graindensity(ld,13);
	looperdata_set_ngrains(ld,5);
	looperdata_set_maxgrainlength (ld, 79);
        looperdata_set_loopend (ld, 79);
        looperdata_set_recend (ld, 79);
        looperdata_set_mingrainlength(ld,79);
	looperdata_set_stereodiversity(ld,0.);
        ld->isrecording = 1;
        looperdata_unlock(ld);
        ls = malloc(sizeof(looper_settings_t));
        ls->looper_data = ld;
        snprintf((char*)&ls->name,20,"ROUGH");
        speciallist_append(settingslist,(void*)ls);

	/* dominantseptakkord miniloop distorted*/
        ld = looperdata_new(buf, jack_info->samplerate, 1);
	reset_looperdata(ld,jack_info);
        looperdata_lock(ld);
        looperdata_set_mingrainspeed(ld,2.);
        looperdata_set_maxgrainspeed(ld,2.);
	looperdata_set_stereodiversity(ld,0.);
	ld->isrecording = 1;
        looperdata_unlock(ld);
        set_distortion(ld,1.);
        ls = malloc(sizeof(looper_settings_t));
        ls->looper_data = ld;
        snprintf((char*)&ls->name,20,"PITCH 2.");
        speciallist_append(settingslist,(void*)ls);

        /* dominantseptakkord miniloop distorted*/
        ld = looperdata_new(buf, jack_info->samplerate, 1);
	reset_looperdata(ld,jack_info);
        looperdata_lock(ld);
	looperdata_set_mingrainspeed(ld,.5);
        looperdata_set_maxgrainspeed(ld,.5);
	looperdata_set_stereodiversity(ld,0.);
	ld->isrecording = 1;
        looperdata_unlock(ld);
        set_distortion(ld,1.);
        ls = malloc(sizeof(looper_settings_t));
        ls->looper_data = ld;
        snprintf((char*)&ls->name,20,"PITCH .5");
        speciallist_append(settingslist,(void*)ls);

	/* dominantseptakkord miniloop distorted*/
        ld = looperdata_new(buf, jack_info->samplerate, 1);
	reset_looperdata(ld,jack_info);
        looperdata_lock(ld);
        looperdata_set_mingrainspeed(ld,.25);
        looperdata_set_maxgrainspeed(ld,.25);
	looperdata_set_stereodiversity(ld,0.);
	ld->isrecording = 1;
        looperdata_unlock(ld);
        set_distortion(ld,1.);
        ls = malloc(sizeof(looper_settings_t));
        ls->looper_data = ld;
        snprintf((char*)&ls->name,20,"PITCH .25");
        speciallist_append(settingslist,(void*)ls);

        /* dominantseptakkord miniloop distorted*/
        ld = looperdata_new(buf, jack_info->samplerate, 1);
	reset_looperdata(ld,jack_info);
        looperdata_lock(ld);
        looperdata_set_mingrainspeed(ld,.05);
        looperdata_set_maxgrainspeed(ld,.05);
	looperdata_set_stereodiversity(ld,0.);
	ld->isrecording = 1;
        looperdata_unlock(ld);
        set_distortion(ld,1.);
        ls = malloc(sizeof(looper_settings_t));
        ls->looper_data = ld;
        snprintf((char*)&ls->name,20,"PITCH .05");
        speciallist_append(settingslist,(void*)ls);
}


/*--------MAIN--------*/
int main(int argc, char *argv[]){
	int 		i;
	buffer_info_t 	*buf;
	looper_data_t 	*looper_data;
	struct timeval 	tv;
	speciallist_t   *looperdatalist;
	speciallist_t   *buffer_list;
/*	int	     	looperindexcounter = 0;*/
	int	     	bufferindexcounter = 0;
	jack_info_t     *jack_info;
	int	     	alive = 1;
	long	    	lastxruns = 0;
	shuttle_data_t  *shuttle;
	tranzport_t     *tranzport;
	serialio_data_t *serialio;
/*	int 		rounds = 0;*/
#if 0
	struct timeval ts;
	double ta; 
	double tb = 0;
#endif

/*
	signal(SIGINT, sigproc);
	signal(SIGQUIT, quitproc);
*/

/*	printf ("zwzw: %.12lf\n",pow (2, 1./12.));*/
	startup_linelevel = ossmixer_getlevel_line();
	if (startup_linelevel < 1) startup_linelevel = 80;
/*	printf ("line:%d\n",startup_linelevel);*/
	printf ("pcm:%d\n", ossmixer_getlevel_pcm());

	shuttle = shuttle_new();
	if (shuttle_open(shuttle) < 1){
		fprintf(stderr,"could not open shuttle device!\n");
/*		exit (0); */
	}

	serialio = serialio_new();
	if (serialio_open(serialio) < 1){
		fprintf(stderr,"could not open serial device!");
	}

        tranzport = tranzport_new();
	if (!tranzport){ /* LATER: check for actual device */
		 fprintf(stderr,"could not open tranzport device!");
	}else{
		tranzport_set_nativemode(tranzport);
	}

	/* global data initialisation */
	buffer_list = speciallist_new();
	looperdatalist = speciallist_new();

	/* start the loadbuffer thread */
	buffer_discthread_launch(buffer_list);
	
	/* start the jack threads */
	jack_info = mixer_launch(looperdatalist);


	if (!jack_info->samplerate){
		fprintf(stderr,"could not connect to jack!\n no playback/recording possible.");	   
		sleep (2);
		exit (0);
	}
	
	/* create 3 players of 10sec each: */
	for (i = 0;i < 1; i++){
		buf = buffer_new(bufferindexcounter++,jack_info->samplerate);
		buffer_set_channels(buf,2);
		buffer_lock(buf);	
		buffer_resize (buf, jack_info->samplerate  * 60 ); /* one minute size */
		buf->status = BUFFER_STATUS_READY;
		buffer_unlock(buf);
		speciallist_append(buffer_list,buf);

		looper_data = looperdata_new(buf, jack_info->samplerate, bufferindexcounter);
		reset_looperdata (looper_data, jack_info);

		speciallist_append(looperdatalist,(void*)looper_data);
	}

	ossmixer_setlevel_line(0);

	create_settings(buf, jack_info);	

	looper_data = speciallist_get_first(looperdatalist);
        while (looper_data){
		looper_data->isrecording = 1;
        	looper_data = speciallist_get_next(looperdatalist,looper_data);
        }


	while(1){
/*		check_buffer_list(buffer_list);*/
#if 0
		gettimeofday(&ts, NULL);
		ta = (double)ts.tv_sec * 1000000.0 + (double)ts.tv_usec;
		if (ta  > tb + mintimediff){
			shuttle_pollall(shuttle, looperdatalist, jack_info);
			tb = ta;
		}
#endif 

		shuttle_pollall (shuttle, looperdatalist, jack_info);
		serialio_pollall (serialio, looperdatalist);
		tranzport_pollall (tranzport, looperdatalist, jack_info);

		if (alive && !jack_info->alive){
			alive = jack_info->alive;
			if (tranzport) set_lcd (tranzport,"      EXITING       ",0,"    (jack error)    ",0,10);
			fprintf(stderr,"jack kicked us out.\n restarting.\n");
			ossmixer_setlevel_line(startup_linelevel);
			exit (0);
		}
		
		if (jack_info->xruns != lastxruns){
			lastxruns = jack_info->xruns;
			if (tranzport) set_lcd (tranzport,"      X-RUN       ",0,NULL,0,10);
			fprintf(stderr,"XRUN! [%ld xruns total]\n",lastxruns);
		}

		tv.tv_sec = 0; 
		tv.tv_usec = 30000; /*was 30000 */
		if (!tranzport) select(0, NULL, NULL, NULL, &tv);
	}

	mixer_close(jack_info);
	ossmixer_setlevel_line(startup_linelevel);
	printf ("bye\n");
  	return 0;
}
