#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <math.h>
#include <sys/stat.h>
#include "looper.h"
#include "../../common/looperdata.h"
#include "gtkmeter.h"
#include "graindialog.h"
#include "griddialog.h"
#include "threshdialog.h"
#include "customcurve.h"
#include "../../common/speciallist.h"
#include "../../common/alphatrack.h"

#define LOGTEN 2.302585092994

/* conversion helper functions */

static gdouble samples2secs (looper_gui_t* looper, long samples){
	return (gdouble)samples / (gdouble)looper->samplerate;
}

static guint32 secs2samples (looper_gui_t* looper, gdouble secs){
	return (guint32)((gdouble)secs * (gdouble)looper->samplerate);
}

static gdouble samples2speedsecs (looper_gui_t* looper, long samples){
        return (gdouble)samples / (gdouble)looper->samplerate / fabs(looper->data->speed);
}
        
static guint32 speedsecs2samples (looper_gui_t* looper, gdouble secs){
        return (guint32)((gdouble)secs * (gdouble)looper->samplerate * fabs(looper->data->speed));
}       


/* looperlist functions */
 
looper_list_t* looper_list_new(){
	looper_list_t *list = (looper_list_t*) malloc(sizeof(looper_list_t));

	list->listptr = NULL;
	list->listmutex = g_mutex_new();

	return list;
}

looper_list_t* looper_list_append_looper(looper_list_t* list, looper_gui_t *loop){

	g_mutex_lock(list->listmutex);
	list->listptr = g_list_append(list->listptr, loop);	
	g_mutex_unlock(list->listmutex);
		
	return list;
}

looper_list_t* looper_list_remove_looper(looper_list_t* list, looper_gui_t *looper){
	GList   *tmp;

	tmp = list->listptr;
	while (tmp){
		if (looper == (looper_gui_t*)tmp->data){
			list->listptr = g_list_remove(list->listptr, looper);
			tmp = list->listptr;
		}
		tmp  = g_list_next(tmp);
	}

	looperdata_lock(looper->data);
        if (looper->data){
                looper->data->isplaying = 0;
                looper->data->isrecording = 0;
        }
        looperdata_unlock(looper->data);
	looperdata_free (looper->data);

	return list;
}



/* looper functions */
void looper_set_samplerate (looper_gui_t* looper, long samplerate){
	looper->samplerate = samplerate;
	return;
}

void looper_close (looper_gui_t *looper){
	gint lastwidth;
	gint lastheight;

	if (looper->data){
		looper->data->isplaying = 0;
		looper->data->isrecording = 0;
	}
	if (looper->customcurvedialog){
		gtk_widget_destroy (looper->customcurvedialog);
		looper->customcurvedialog = NULL;
	}
	gtk_widget_destroy (looper->mainbox);

	gtk_window_get_size ((GtkWindow*)looper->parent,&lastwidth,&lastheight);
        gtk_window_resize((GtkWindow*)looper->parent,lastwidth,1);

	looper->parent = NULL;
	looper->id = 0;
}

static void looper_close_looper_callback (GtkMenuItem *menuitem, gpointer data){
	looper_gui_t *looper = (looper_gui_t *)data;

	looper_close (looper);
}

void looper_set_remote_controlled (looper_gui_t *looper, int rc){
	if (!looper) return;
	looper->remote_controlled = rc;
	looper->bg_set = 0;
	looper->dirty = 1;
}

int looper_get_remote_controlled (looper_gui_t *looper){
	if (!looper) return 0;
	return looper->remote_controlled;
}



static void looper_normalize_callback (GtkWidget *normbutton, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;
        float factor;

        if (looper->data->buf){
                factor =  buffer_analyze (looper->data->buf, looper->gid, 0 , 0);
		if (factor != 0){
			factor = 1. / factor;
/*                	buffer_normalize (looper->data->buf, factor, 0, 0);*/
			buffer_set_volume_factor(looper->data->buf, factor);
			looper_update_bufferlist (looper, looper->bufferlist);
		}
        }
}

static void looper_upbutton_callback (GtkWidget *upbutton, gpointer data ){
	looper_gui_t *looper = (looper_gui_t*)data;
	GValue value = { 0, };
	g_value_init (&value, G_TYPE_INT);
	gint pos;

	if (looper->parentcontainer){
		gtk_container_child_get_property(GTK_CONTAINER(looper->parentcontainer),
                        	looper->mainbox, "position", &value);
		pos = g_value_get_int (&value) - 1;
		if (pos < 2) pos = -1;
		gtk_box_reorder_child (GTK_BOX(looper->parentcontainer),looper->mainbox,pos);
		looper->vposition = pos;
	}
}

static void looper_downbutton_callback (GtkWidget *downbutton, gpointer data ){
	looper_gui_t *looper = (looper_gui_t*)data;
        GValue value = { 0, };
        g_value_init (&value, G_TYPE_INT);
        gint pos;

        if (looper->parentcontainer){
                gtk_container_child_get_property(GTK_CONTAINER(looper->parentcontainer),
                                looper->mainbox, "position", &value);
                pos = g_value_get_int (&value) + 1;
		/* LATER: search for highest value and eventually wrap around*/
                gtk_box_reorder_child (GTK_BOX(looper->parentcontainer),looper->mainbox,pos);
		looper->vposition = pos;
        }
}

int looper_get_position(looper_gui_t *looper){
	GValue value = { 0, };
        g_value_init (&value, G_TYPE_INT);
        gint pos = looper->vposition;

	if (looper->parentcontainer){
		gtk_container_child_get_property(GTK_CONTAINER(looper->parentcontainer),
                                looper->mainbox, "position", &value);
                pos = g_value_get_int (&value);
		looper->vposition = pos;
	}
	return pos;
}

looper_data_t* looper_get_looperdata (looper_gui_t *looper){
	if (looper)
		if (looper->data)
			return looper->data;
	return NULL;
}

void looper_set_position(looper_gui_t *looper, int pos){
        GValue value = { 0, };
        g_value_init (&value, G_TYPE_INT);

        if (looper->parentcontainer && pos){
		gtk_box_reorder_child (GTK_BOX(looper->parentcontainer),looper->mainbox,pos);
		looper->vposition = pos;
        }
}

static void looper_denormalize_callback (GtkWidget *normbutton, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;

        if (looper->data->buf){
              	buffer_set_volume_factor(looper->data->buf, 1.);
        }
	gtk_widget_set_sensitive(GTK_WIDGET(normbutton),FALSE);
}

static void looper_normalize_playselection_callback (GtkWidget *normbutton, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;
        float factor;

        if (looper->data->buf){
                factor =  buffer_analyze (looper->data->buf, looper->gid,
			looperdata_get_loopstart(looper->data), looperdata_get_loopend(looper->data));
                if (factor != 0){
                        factor = 1. / factor;
                        buffer_normalize (looper->data->buf, factor, 
				looperdata_get_loopstart(looper->data), looperdata_get_loopend(looper->data));
                }
        }
}

static void looper_normalize_recselection_callback (GtkWidget *normbutton, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;
        float factor;

        if (looper->data->buf){
                factor =  buffer_analyze (looper->data->buf, looper->gid,
			looperdata_get_recstart(looper->data), looperdata_get_recend(looper->data));
                if (factor != 0){
                        factor = 1. / factor;
                        buffer_normalize (looper->data->buf, factor,
				looperdata_get_recstart(looper->data), looperdata_get_recend(looper->data));
                }
        }
}

static void looper_clear_callback (GtkWidget *normbutton,gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;

        if (looper->data->buf){
               	buffer_normalize (looper->data->buf, 0.0, 0, 0);
        }
}

static void looper_clear_playselection_callback (GtkWidget *normbutton,gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;

        if (looper->data->buf){
                buffer_normalize (looper->data->buf, 0.0, 
			looperdata_get_loopstart(looper->data), looperdata_get_loopend(looper->data));
        }
}

static void looper_clear_recselection_callback (GtkWidget *normbutton,gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;

        if (looper->data->buf){
		buffer_normalize (looper->data->buf, 0.0, 
			looperdata_get_recstart(looper->data), looperdata_get_recend(looper->data));
        }
}

static gint confirm_overwrite (GtkWindow *parent, const gchar *filename){
        GtkWidget *dialog;
        gint response;  
                        
        dialog = gtk_message_dialog_new (parent,
                        GTK_DIALOG_DESTROY_WITH_PARENT,
                        GTK_MESSAGE_QUESTION,
                        GTK_BUTTONS_NONE,
                        "\"%s\" already exists.  Do you want to replace it ?",
                        filename);
        gtk_dialog_add_buttons (GTK_DIALOG (dialog),
                        GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
                        "_Replace", GTK_RESPONSE_ACCEPT, 
                        NULL);

        response = gtk_dialog_run (GTK_DIALOG (dialog));

        gtk_widget_destroy (dialog); 
                        
        return response;
}

static char* looper_get_save_filename(GtkWindow *parent){
	GtkWidget *gfc;
        struct stat fstat;
	char* filename = NULL;
	gint response = GTK_RESPONSE_ACCEPT;

	gfc =  gtk_file_chooser_dialog_new("select a soundfile to save the buffer",
                        GTK_WINDOW(parent),
                        GTK_FILE_CHOOSER_ACTION_SAVE,
                        GTK_STOCK_CANCEL,
                        GTK_RESPONSE_CANCEL,
                        GTK_STOCK_SAVE,
                        GTK_RESPONSE_ACCEPT,
                                        NULL);
	gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER (gfc),TRUE);

        if (gtk_dialog_run (GTK_DIALOG (gfc)) == GTK_RESPONSE_ACCEPT){
                filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (gfc));

	        if (strncasecmp (".wav",(char*)(filename + strlen(filename) - 4),4)){
                	int i = strlen(filename);
                	filename = realloc (filename,(sizeof(char)*(i + 5)));
                	memset((char*)(filename + i),0,4);
                	strncpy((filename + i),".wav",4);
        	}

                if (!stat (filename, &fstat))
                        response = confirm_overwrite(parent,(gchar*)filename);

                if (!(response == GTK_RESPONSE_ACCEPT)){
			free(filename);
			filename = NULL;
		}
                gtk_widget_destroy (gfc);
        }else gtk_widget_destroy (gfc);

	return filename;
}

static void looper_savebuffer2file_callback (GtkWidget *dummy, gpointer data){
	looper_gui_t *looper = (looper_gui_t*)data;
        buffer_info_t *buf = NULL;
	char* filename;
	int ret;

        if (looper->data)
                if (looper->data->buf)
                        buf = looper->data->buf;
        if (!buf) return;

	filename = looper_get_save_filename(GTK_WINDOW(looper->parent));
	if (filename){
		ret = buffer_save(looper->data->buf, filename, 
			looper->samplerate,  0, buffer_get_size(looper->data->buf));
		if (ret < 1) printf ("error saving file\n");
		else{
/*			printf("saved");*/
			buffer_set_filename(buf,filename);
			
		}
		
		free(filename);
	}else printf ("filename error saving file\n");
}

static void looper_saveplayselection2file_callback (GtkWidget *dummy, gpointer data){
        looper_gui_t *looper = (looper_gui_t*)data;
        buffer_info_t *buf = NULL;
        char* filename;
        int ret;

        if (looper->data)
                if (looper->data->buf)
                        buf = looper->data->buf;
        if (!buf) return;

        filename = looper_get_save_filename(GTK_WINDOW(looper->parent));
        if (filename){
                ret = buffer_save(looper->data->buf, filename, looper->samplerate,  
			looperdata_get_loopstart(looper->data),
			looperdata_get_loopend(looper->data));
                if (ret < 1) printf ("error saving file\n");
                else    printf("saved");
                free(filename);
        }else printf ("filename error saving file\n");
}

static void looper_saverecselection2file_callback (GtkWidget *dummy, gpointer data){
        looper_gui_t *looper = (looper_gui_t*)data;
        buffer_info_t *buf = NULL;
        char* filename;
        int ret;

        if (looper->data)
                if (looper->data->buf)
                        buf = looper->data->buf;
        if (!buf) return;

        filename = looper_get_save_filename(GTK_WINDOW(looper->parent));
        if (filename){
                ret = buffer_save(looper->data->buf, filename, looper->samplerate, 
			looperdata_get_recstart(looper->data),
                        looperdata_get_recend(looper->data));
                if (ret < 1) printf ("error saving file\n");
                else    printf("saved");
                free(filename);
        }else printf ("filename error saving file\n");
}



/* not in use any more: */
/*
static void looper_normbutton_callback (GtkWidget *normbutton,gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;
        float factor;

        if (looper->data->buf){
                factor = buffer_analyze (looper->data->buf, 0 , 0);
		if (factor){
			printf ("factor:%f\n",factor);
			factor = 1.0 / factor;
                	buffer_normalize (looper->data->buf, factor, 0, 0);
		}
        }
}
*/

void looper_update_widgets(looper_gui_t *looper){
	if (!looper->data) return;
	if (!looper->data->buf) return;
	if (!buffer_get_size(looper->data->buf)) return;

	if (!looper->bg_set){
	 	if (looper->remote_controlled) gtk_widget_modify_bg(GTK_WIDGET(looper->menu_bar),
                                        GTK_STATE_NORMAL,
                                        &(GdkColor){65535, 65535, 0 ,0});
        	else gtk_widget_modify_bg(GTK_WIDGET(looper->menu_bar), GTK_STATE_NORMAL, NULL);
		looper->bg_set = 1;
	}

	if (looper->data->keepabsolute){
                looper->looplengthblock = 1;
		gtk_spin_button_set_range((GtkSpinButton*)looper->loopstartspin,
                	0,samples2speedsecs(looper,buffer_get_size(looper->data->buf)));
		gtk_spin_button_set_range((GtkSpinButton*)looper->loopendspin,
                	0,samples2speedsecs(looper,buffer_get_size(looper->data->buf)));
		gtk_spin_button_set_range((GtkSpinButton*)looper->looplengthspin,
                	0,samples2speedsecs(looper,buffer_get_size(looper->data->buf)));

        	looper->looplengthblock = 1;
		gtk_spin_button_set_value ((GtkSpinButton*)looper->looplengthspin,
                        samples2speedsecs(looper,
                        (looper->data->loopend - looper->data->loopstart)));
		gtk_spin_button_set_value ((GtkSpinButton*)looper->loopstartspin,
                        samples2speedsecs(looper,looper->data->loopstart));
		gtk_spin_button_set_value ((GtkSpinButton*)looper->loopendspin,
                        samples2speedsecs(looper,looper->data->loopend));

		looper->recstartblock = 1;
                gtk_spin_button_set_range((GtkSpinButton*)looper->recstartspin,
                        0,samples2speedsecs(looper,buffer_get_size(looper->data->buf)));
                gtk_spin_button_set_range((GtkSpinButton*)looper->recendspin,
                        0,samples2speedsecs(looper,buffer_get_size(looper->data->buf)));
                gtk_spin_button_set_range((GtkSpinButton*)looper->reclengthspin,
                        0,samples2speedsecs(looper,buffer_get_size(looper->data->buf)));

                looper->reclengthblock = 1;
		gtk_spin_button_set_value ((GtkSpinButton*)looper->reclengthspin,
                        samples2speedsecs(looper,
                        (looper->data->recend - looper->data->recstart)));
		gtk_spin_button_set_value ((GtkSpinButton*)looper->recstartspin,
                        samples2speedsecs(looper,looper->data->recstart));
                gtk_spin_button_set_value ((GtkSpinButton*)looper->recendspin,
                        samples2speedsecs(looper,looper->data->recend));

	}else{
                looper->looplengthblock = 1;
		gtk_spin_button_set_range((GtkSpinButton*)looper->looplengthspin,
                        0,samples2secs(looper,buffer_get_size(looper->data->buf)));
		gtk_spin_button_set_range((GtkSpinButton*)looper->loopstartspin,
                	0,samples2secs(looper,buffer_get_size(looper->data->buf)));
        	gtk_spin_button_set_range((GtkSpinButton*)looper->loopendspin,
                	0,samples2secs(looper,buffer_get_size(looper->data->buf)));

                looper->looplengthblock = 1;
		gtk_spin_button_set_value ((GtkSpinButton*)looper->looplengthspin,
                        samples2secs(looper,
                        (looper->data->loopend - looper->data->loopstart)));
		gtk_spin_button_set_value ((GtkSpinButton*)looper->loopstartspin,
                        samples2secs(looper,looper->data->loopstart));
                gtk_spin_button_set_value ((GtkSpinButton*)looper->loopendspin,
                        samples2secs(looper,looper->data->loopend));

        	looper->reclengthblock = 1;
		gtk_spin_button_set_range((GtkSpinButton*)looper->reclengthspin,
                        0,samples2secs(looper,buffer_get_size(looper->data->buf)));
        	gtk_spin_button_set_range((GtkSpinButton*)looper->recstartspin,
                	0,samples2secs(looper,buffer_get_size(looper->data->buf)));
        	gtk_spin_button_set_range((GtkSpinButton*)looper->recendspin,
                	0,samples2secs(looper,buffer_get_size(looper->data->buf)));

                looper->reclengthblock = 1;
		gtk_spin_button_set_value ((GtkSpinButton*)looper->reclengthspin,
                        samples2secs(looper,
                        (looper->data->recend - looper->data->recstart)));
                gtk_spin_button_set_value ((GtkSpinButton*)looper->recstartspin,
                        samples2secs(looper,looper->data->recstart));
                gtk_spin_button_set_value ((GtkSpinButton*)looper->recendspin,
                        samples2secs(looper,looper->data->recend));
	}
	if (looperdata_get_vol(looper->data)){
		looper->volblock = 1;
		gtk_range_set_value ((GtkRange*)looper->volslider,
/*			logf(looper->data->vol) * 70.0 / 8.0590477);*/
			logf(looperdata_get_vol(looper->data)) * 8.656170245);
	} else gtk_range_set_value ((GtkRange*)looper->volslider, -48.);
/*	printf ("vol_sliderset:%f\n",logf(looperdata_get_vol(looper->data)) * 8.656170245);*/
	gtk_range_set_value ((GtkRange*)looper->ngrainslider,
			looper->data->ngrains);
	gtk_range_set_value ((GtkRange*)looper->panslider,
                        looper->data->pan);
	gtk_range_set_value ((GtkRange*)looper->recmixslider,
                        looper->data->recmix);

	gtk_spin_button_set_value ((GtkSpinButton*)looper->speedspin,
                        looper->data->speed);
	gtk_spin_button_set_value ((GtkSpinButton*)looper->panswingspin,
                        looper->data->panswing);
	gtk_spin_button_set_value ((GtkSpinButton*)looper->densityspin,
                        looper->data->graindensity);
	gtk_spin_button_set_value ((GtkSpinButton*)looper->diversityspin,
			looperdata_get_stereodiversity(looper->data));
	
	gtk_loopview_update_loopstart((GtkLoopview*)looper->loopview);
        gtk_loopview_update_loopend((GtkLoopview*)looper->loopview);
	gtk_loopview_update_recstart((GtkLoopview*)looper->loopview);
        gtk_loopview_update_recend((GtkLoopview*)looper->loopview);

/*	gtk_loopview_update_waveform(GTK_LOOPVIEW(looper->loopview)); */
}

void looper_set_buffer_by_id (looper_gui_t *looper, int id){
	looper_data_t *new_data;
	looper_data_t *old_data;
	buffer_info_t *buf = NULL;
	buffer_info_t *tmp = NULL;
	char* title = calloc(sizeof(char), 255);

	if (looper->customcurvedialog){
		gtk_widget_destroy (looper->customcurvedialog);
		looper->customcurvedialog = NULL;
	}

	tmp = speciallist_get_first(looper->bufferlist);

	while(tmp){
		if (tmp->id == id) buf = tmp;
		tmp = speciallist_get_next(looper->bufferlist,tmp);
	}
	if (buf){
		gtk_loopview_suspend_gui(GTK_LOOPVIEW(looper->loopview),1);

		if (looper->gid && looper->data->buf){
/*			printf ("looper deleting gid:%d\n",looper->gid);*/
			buffer_lock(looper->data->buf);
			buffer_delete_group(looper->data->buf, looper->gid);
			buffer_unlock(looper->data->buf);
		}


		looperdata_lock(looper->data);
		new_data = looperdata_new(buf, looper->samplerate, looper->id);
		looper->gid = buffer_get_group (buf, DS_PRIORITY_RELAXED);
/*		printf ("looper get gid:%d\n",looper->gid);*/

		if (looper->data){
			looperdata_copy_settings (new_data, looper->data);
			looper->data->isplaying = 0;
			looper->data->isrecording = 0;
		}

		if (buffer_get_type(buf) == BUFFER_TYPE_DISCSTREAM)
                                looperdata_set_recording(new_data,0);
/*		looperdata_set_thresholdmode (new_data, 1);*/

		old_data = looper->data;
		looper->data = new_data;

		/* try: */
                /* LATER: replayce with gui */
/*                printf ("set reverb\n");
                looperdata_set_reverblength(looper->data,10000);
                looperdata_set_feedback(looper->data,.9);
                printf ("done\n");
*/

		looperdata_unlock(old_data);

/*		printf ("set buffer:%s\n",looper->data->buf->shortname);*/
	 	speciallist_lock(looper->looperdatalist);
                speciallist_delete_nolock(looper->looperdatalist, (void*)old_data);
                speciallist_append_nolock(looper->looperdatalist, (void*)new_data);
                speciallist_unlock(looper->looperdatalist);

		snprintf(title,254,"[%d]:%s",looper->data->id,buf->shortname);
		gtk_frame_set_label(GTK_FRAME(looper->controlframe),(gchar*)title);
		free(title);
		gtk_loopview_set_looperdata(GTK_LOOPVIEW(looper->loopview),looper->data);

                if (buffer_get_type(buf) == BUFFER_TYPE_DISCSTREAM)
                        gtk_widget_set_sensitive(GTK_WIDGET(looper->recbutton), FALSE);
                else    gtk_widget_set_sensitive(GTK_WIDGET(looper->recbutton), TRUE);

		looper_update_widgets(looper);
		looper_update_bufferlist(looper,looper->bufferlist);
		gtk_loopview_update_waveform(GTK_LOOPVIEW(looper->loopview));
		gtk_loopview_suspend_gui(GTK_LOOPVIEW(looper->loopview),0);

		looperdata_free (old_data);
	}
/*	printf ("set_bufferby_id callback done\n");*/
}

static void looper_set_buffer_callback(GtkMenuItem *menuitem, gpointer data){
        looper_gui_t *looper = (looper_gui_t *)data;
        buffer_info_t *buf = g_object_get_data (G_OBJECT(menuitem),"kluppe-buffer");
        looper_set_buffer_by_id(looper, buf->id);
}

int looper_update_bufferlist (looper_gui_t* looper, speciallist_t *bufferlist){
	GtkWidget     	*menu_item;
        GtkWidget     	*menu;
	buffer_info_t 	*buf;

	if (looper != NULL){
		looper->bufferlist = bufferlist;

		gtk_widget_destroy (looper->buffermenu);

		menu = gtk_menu_new ();

		if (speciallist_get_first(bufferlist) && looper->data->buf){
        		menu_item =  gtk_menu_item_new_with_label ("normalize active buffer");
        		g_signal_connect (G_OBJECT (menu_item), "activate",
                                                	G_CALLBACK (looper_normalize_callback),
                                                	(gpointer)looper);
        		gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
        		gtk_widget_show (menu_item);

			menu_item =  gtk_menu_item_new_with_label ("de-normalize active buffer");
                        g_signal_connect (G_OBJECT (menu_item), "activate",
                                                        G_CALLBACK (looper_denormalize_callback),
                                                        (gpointer)looper);
                        gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
			if (buffer_get_volume_factor(looper->data->buf) == 1.)
				gtk_widget_set_sensitive(GTK_WIDGET(menu_item), FALSE);
                        gtk_widget_show (menu_item);	
		
                       	menu_item =  gtk_menu_item_new_with_label ("normalize play selection");
                       	g_signal_connect (G_OBJECT (menu_item), "activate",
                                                       	G_CALLBACK (looper_normalize_playselection_callback),
                                                       	(gpointer)looper);
                       	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
			if (buffer_get_type(looper->data->buf) == BUFFER_TYPE_DISCSTREAM)
                               	gtk_widget_set_sensitive(GTK_WIDGET(menu_item), FALSE);
                       	gtk_widget_show (menu_item);

                       	menu_item =  gtk_menu_item_new_with_label ("normalize rec selection");
                       	g_signal_connect (G_OBJECT (menu_item), "activate",
                                                       	G_CALLBACK (looper_normalize_recselection_callback),
                                                       	(gpointer)looper);
                       	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
			if (buffer_get_type(looper->data->buf) == BUFFER_TYPE_DISCSTREAM)
                               	gtk_widget_set_sensitive(GTK_WIDGET(menu_item), FALSE);
                       	gtk_widget_show (menu_item);

                        menu_item = gtk_separator_menu_item_new();
                        gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
                        gtk_widget_show (menu_item);
	
			menu_item =  gtk_menu_item_new_with_label ("clear active buffer");
       			g_signal_connect (G_OBJECT (menu_item), "activate",
                                               		G_CALLBACK (looper_clear_callback),
                                               		(gpointer)looper);
       			gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
			if (buffer_get_type(looper->data->buf) == BUFFER_TYPE_DISCSTREAM)
                               	gtk_widget_set_sensitive(GTK_WIDGET(menu_item), FALSE);
       			gtk_widget_show (menu_item);
	
                       	menu_item =  gtk_menu_item_new_with_label ("clear play selection");
                       	g_signal_connect (G_OBJECT (menu_item), "activate",
                                                       	G_CALLBACK (looper_clear_playselection_callback),
                                                       	(gpointer)looper);
                       	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
			if (buffer_get_type(looper->data->buf) == BUFFER_TYPE_DISCSTREAM)
				gtk_widget_set_sensitive(GTK_WIDGET(menu_item), FALSE);
                       	gtk_widget_show (menu_item);

                       	menu_item =  gtk_menu_item_new_with_label ("clear rec selection");
                       	g_signal_connect (G_OBJECT (menu_item), "activate",
                                                       	G_CALLBACK (looper_clear_recselection_callback),
                                                       	(gpointer)looper);
                       	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
			if (buffer_get_type(looper->data->buf) == BUFFER_TYPE_DISCSTREAM)
				gtk_widget_set_sensitive(GTK_WIDGET(menu_item), FALSE);
                       	gtk_widget_show (menu_item);
               	
                       	menu_item = gtk_separator_menu_item_new();
                       	gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
                       	gtk_widget_show (menu_item);

			menu_item =  gtk_menu_item_new_with_label ("save buffer content to file");
                        g_signal_connect (G_OBJECT (menu_item), "activate",
                                                        G_CALLBACK (looper_savebuffer2file_callback),
                                                        (gpointer)looper);
                        gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
                        gtk_widget_show (menu_item);

			menu_item =  gtk_menu_item_new_with_label ("save play selection to file");
                        g_signal_connect (G_OBJECT (menu_item), "activate",
                                                        G_CALLBACK (looper_saveplayselection2file_callback),
                                                        (gpointer)looper);
                        gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
                        gtk_widget_show (menu_item);

			menu_item =  gtk_menu_item_new_with_label ("save rec selection to file");
                        g_signal_connect (G_OBJECT (menu_item), "activate",
                                                        G_CALLBACK (looper_saverecselection2file_callback),
                                                        (gpointer)looper);
                        gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
                        gtk_widget_show (menu_item);


	        	menu_item = gtk_separator_menu_item_new();
        		gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
        		gtk_widget_show (menu_item);
		}


		buf = speciallist_get_first(bufferlist);
		while(buf){
			if (buf->status == BUFFER_STATUS_READY){
				menu_item = gtk_menu_item_new_with_label ((gchar*)buffer_get_shortname(buf));
/*				gtk_widget_modify_font(menu_item,looper->font);*/
				gtk_menu_shell_append (GTK_MENU_SHELL (menu), menu_item);
				g_object_set_data (G_OBJECT (menu_item), "kluppe-buffer",&buf->id);
				g_signal_connect (G_OBJECT (menu_item), "activate",
                             		G_CALLBACK (looper_set_buffer_callback),
                               		looper);
				gtk_widget_show (menu_item);
			}
			buf = speciallist_get_next(bufferlist,buf);
		}
		gtk_widget_show (menu);	
		looper->buffermenu = gtk_menu_item_new_with_label ("buffer");
        	gtk_menu_item_set_submenu (GTK_MENU_ITEM (looper->buffermenu), menu);
		gtk_widget_modify_font(looper->buffermenu,looper->font);
        	gtk_widget_show (looper->buffermenu);

		gtk_menu_shell_prepend (GTK_MENU_SHELL (looper->menu_bar), looper->buffermenu);

		return 0;
	}
	return -1;
}

void looper_shuttle_poll (looper_gui_t* looper){
        while (shuttle_poll (looper->shuttle) > 0){
        	switch (looper->shuttle->code){
               		case SHUTTLE_LEFTMOST :
				if ((!looper->shuttle->value) && (looper->shuttle->lastcode == looper->shuttle->code)){
					if (gtk_toggle_button_get_active((GtkToggleButton*)looper->playbutton)) 
						gtk_toggle_button_set_active((GtkToggleButton*)looper->playbutton, FALSE);
        				else
        					gtk_toggle_button_set_active((GtkToggleButton*)looper->playbutton, TRUE);       
					}
				break;
			case SHUTTLE_MIDDLE :
				if ((!looper->shuttle->value) && (looper->shuttle->lastcode == looper->shuttle->code)){
					if (looper->shuttle->buttonstates & SHUTTLE_LEFTMOST){
						looperdata_lock(looper->data);  
        					looperdata_set_playpos(looper->data, 
							looperdata_get_loopstart(looper->data));
        					looperdata_unlock(looper->data); 
					} 
					if (looper->shuttle->buttonstates & SHUTTLE_RIGHTMOST){
						looperdata_lock(looper->data);
                                        	looperdata_set_recpos(looper->data, 
                                                	looperdata_get_recstart(looper->data));
                                        	looperdata_unlock(looper->data);
					}
				}
				break;
			case SHUTTLE_RIGHTMOST :
                                if ((!looper->shuttle->value) && (looper->shuttle->lastcode == looper->shuttle->code)){
                                        if (gtk_toggle_button_get_active((GtkToggleButton*)looper->recbutton))
                                                gtk_toggle_button_set_active((GtkToggleButton*)looper->recbutton, FALSE);
                                        else
                                                gtk_toggle_button_set_active((GtkToggleButton*)looper->recbutton, TRUE);
                                        }
                                break;
			case SHUTTLE_SHUTTLE:	
/*				printf ("delta:%0.04f, %d\n",looper->shuttle->shuttledelta,looper->shuttle->shuttlestate);*/
				if (!looper->shuttle->buttonstates){
		                        gtk_range_set_value ((GtkRange*)looper->panslider,
                               			gtk_range_get_value((GtkRange*)looper->panslider) +
                               			looper->shuttle->shuttledelta * .01);

				}else if (looper->shuttle->buttonstates & SHUTTLE_LEFTMOST){
					gtk_spin_button_set_value ((GtkSpinButton*)looper->loopstartspin,
                                		(gfloat)looper->shuttle->shuttledelta / 100. *
                                		gtk_spin_button_get_value ((GtkSpinButton*)looper->looplengthspin) +
						gtk_spin_button_get_value ((GtkSpinButton*)looper->loopstartspin)
                        		);
				}else if (looper->shuttle->buttonstates & SHUTTLE_LEFT){
					gtk_spin_button_set_value ((GtkSpinButton*)looper->densityspin,
						(gfloat)looper->shuttle->shuttledelta / 2. +
						gtk_spin_button_get_value ((GtkSpinButton*)looper->densityspin)
					);
				}else if (looper->shuttle->buttonstates & SHUTTLE_MIDDLE){
					gtk_range_set_value ((GtkRange*)looper->ngrainslider,0);
				}else if (looper->shuttle->buttonstates & SHUTTLE_RIGHT){
                                        gtk_range_set_value ((GtkRange*)looper->recmixslider,
                                                gtk_range_get_value((GtkRange*)looper->recmixslider) +
                                                looper->shuttle->shuttledelta * .05);
				}else if (looper->shuttle->buttonstates & SHUTTLE_RIGHTMOST){
                                        gtk_spin_button_set_value ((GtkSpinButton*)looper->recstartspin,
                                                (gfloat)looper->shuttle->shuttledelta / 100. *
                                                gtk_spin_button_get_value ((GtkSpinButton*)looper->reclengthspin) +
                                                gtk_spin_button_get_value ((GtkSpinButton*)looper->recstartspin)
                                        );
                                }/* end if shuttle and !buttons */
				break;
		}
	}

	if (looper->shuttle->jogstate){
		if (!looper->shuttle->buttonstates){
                                gtk_spin_button_set_value ((GtkSpinButton*)looper->speedspin,
/*                                        gtk_spin_button_get_value ((GtkSpinButton*)looper->speedspin) / 100. * looper->shuttle->value +*/
					looper->shuttle->jogstate * looper->shuttle->jogstate  * looper->shuttle->jogstate * .002 +
                                        gtk_spin_button_get_value ((GtkSpinButton*)looper->speedspin)
                                        );
                } else if (looper->shuttle->buttonstates & SHUTTLE_LEFTMOST){
			gtk_spin_button_set_value ((GtkSpinButton*)looper->looplengthspin,
				(gfloat)looper->shuttle->jogstate / 100. *  
				gtk_spin_button_get_value ((GtkSpinButton*)looper->looplengthspin) + 
				gtk_spin_button_get_value ((GtkSpinButton*)looper->looplengthspin)	
			);
		} else if (looper->shuttle->buttonstates & SHUTTLE_LEFT){
			gtk_range_set_value ((GtkRange*)looper->ngrainslider,
                                gtk_range_get_value((GtkRange*)looper->ngrainslider) +
                                looper->shuttle->jogstate / 10.);
		} else if (looper->shuttle->buttonstates & SHUTTLE_MIDDLE){
			gtk_spin_button_set_value ((GtkSpinButton*)looper->speedspin,1.0);
		} else if (looper->shuttle->buttonstates & SHUTTLE_RIGHT){
                        gtk_range_set_value ((GtkRange*)looper->volslider,
                                gtk_range_get_value((GtkRange*)looper->volslider) +
                                looper->shuttle->jogstate / 5.);
		} else if (looper->shuttle->buttonstates & SHUTTLE_RIGHTMOST){
                        gtk_spin_button_set_value ((GtkSpinButton*)looper->reclengthspin,
                                (gfloat)looper->shuttle->jogstate / 100. *
                                gtk_spin_button_get_value ((GtkSpinButton*)looper->reclengthspin) +
                                gtk_spin_button_get_value ((GtkSpinButton*)looper->reclengthspin)
                        );
                }else printf ("buttonstate:%d\n",looper->shuttle->buttonstates);/* end if jog and !buttons */
	}

	/* cosmetics: */
	if (looper->shuttle->fd < 1) gtk_toggle_button_set_active((GtkToggleButton*)looper->shuttlebutton,FALSE);
	
}

void looper_update_cursorposition (looper_gui_t* looper){
	GtkAdjustment *adj;
	float vL, vR;
	gdouble v;

	if (looper->customcurvedialog){
		customcurve_update( customcurvedialog_get_curve(looper->customcurvedialog), looper->data);
	}

	if (looper->dirty){ looper_update_widgets(looper); looper->dirty = 0;}

	looper_shuttle_poll (looper);

	if (looper->vublock) looper->vublock = FALSE;
	else{
		looper->vublock = TRUE;

		/* play meters */
        	adj = gtk_meter_get_adjustment(GTK_METER(looper->outLmeter));
/*		looperdata_lock(looper->data);*/
		looperdata_get_playmeters(looper->data, &vL, &vR);
/*		looperdata_unlock(looper->data);*/
        	v = 20.0f * log10f(vL);
        	if (v > -70.){
                	gtk_adjustment_set_value(adj,v);
        	}else{
                	gtk_adjustment_set_value(adj,-70.);
        	}
	
	
        	v = 20.0f * log10f(vR);
        	adj = gtk_meter_get_adjustment(GTK_METER(looper->outRmeter));
        	if (v > -70.){
                	gtk_adjustment_set_value(adj,v);
        	}else{
                	gtk_adjustment_set_value(adj,-70.);
        	}
	
		/* rec meters */
/*		looperdata_lock(looper->data);*/
		looperdata_get_recmeters(looper->data, &vL, &vR);
/*		looperdata_unlock(looper->data);*/


		adj = gtk_meter_get_adjustment(GTK_METER(looper->inLmeter));
		v = 20.0f * log10f(vL);
		if (v > -70.){ 
			gtk_adjustment_set_value(adj,v);
		}else{
			gtk_adjustment_set_value(adj,-70.);
		}
	
	
		v = 20.0f * log10f(vR);
		adj = gtk_meter_get_adjustment(GTK_METER(looper->inRmeter));
        	if (v > -70.){ 
                	gtk_adjustment_set_value(adj,v);
        	}else{
                	gtk_adjustment_set_value(adj,-70.);
        	}

	}

       	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(looper->playbutton)))
                if (!looperdata_get_playing(looper->data))
                        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(looper->playbutton),FALSE);

        if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(looper->recbutton)))
                if (!looperdata_get_recording(looper->data))
                        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(looper->recbutton),FALSE);

	gtk_loopview_update_cursorposition(GTK_LOOPVIEW(looper->loopview));	
	return;
}

/* callback functions */
static void looper_grainsettings_callback (GtkMenuItem *menuitem, gpointer data){
        looper_gui_t *looper = (looper_gui_t *)data;
	graindialog_new(looper->parent, looper->data, looper->id); 
}

static void looper_threshsettings_callback (GtkMenuItem *menuitem, gpointer data){
	looper_gui_t *looper = (looper_gui_t *)data;
        threshdialog_new(looper->parent, looper->data);
}

static void looper_gridsettings_callback (GtkMenuItem *menuitem, gpointer data){
        looper_gui_t *looper = (looper_gui_t *)data;
        griddialog_new(looper->parent, GTK_LOOPVIEW(looper->loopview));
}

static void looper_gridfromplayselection_callback (GtkMenuItem *menuitem, gpointer data){
	looper_gui_t *looper = (looper_gui_t *)data;
	long s, e, o;
	if (looper->data){
		if (looper->data->buf){
		 	s = looperdata_get_loopstart(looper->data);
			e = looperdata_get_loopend(looper->data);
			o = s % (e - s);
/*			printf ("o:%ld, s:%ld, e:%ld, l:%ld\n",(long)o,(long)s,(long)e,(long)(e - s));*/
			gtk_loopview_set_bpm(GTK_LOOPVIEW(looper->loopview), (gdouble)(looper->samplerate  * 60.)/ (gdouble)(e - s));
			gtk_loopview_set_bpb(GTK_LOOPVIEW(looper->loopview),1);
			gtk_loopview_set_gridoffset(GTK_LOOPVIEW(looper->loopview),o);
/*			 printf ("o:%ld, bpm:%ld, bpb:%ld\n",
				(long)gtk_loopview_get_gridoffset(GTK_LOOPVIEW(looper->loopview)),
				(long)gtk_loopview_get_bpm(GTK_LOOPVIEW(looper->loopview)),
				(long)gtk_loopview_get_bpb(GTK_LOOPVIEW(looper->loopview)));*/
		}
	}
}

static void looper_gridfromrecselection_callback (GtkMenuItem *menuitem, gpointer data){
        looper_gui_t *looper = (looper_gui_t *)data;
        long s, e, o;
        if (looper->data){
                if (looper->data->buf){
                        s = looperdata_get_recstart(looper->data);
                        e = looperdata_get_recend(looper->data);
                        o = s % (e - s);
                        gtk_loopview_set_bpm(GTK_LOOPVIEW(looper->loopview), (gdouble)(looper->samplerate  * 60.)/ (gdouble)(e - s));
                        gtk_loopview_set_bpb(GTK_LOOPVIEW(looper->loopview),1);
                        gtk_loopview_set_gridoffset(GTK_LOOPVIEW(looper->loopview),o);
                }
        }
}



static void looper_custommode_destroy_callback(GtkWidget *dialog, gpointer data){
/*	looper_gui_t *looper = (looper_gui_t *)data; 
	this doesn't work because data=responsecode*/

	looper_gui_t *looper = g_object_get_data (G_OBJECT (dialog),"looper");

	looper->customcurvedialog = NULL;
	gtk_widget_destroy (dialog);
}

static void looper_custommode_callback(GtkMenuItem *menuitem, gpointer data){
	looper_gui_t *looper = (looper_gui_t *)data;

	if (!looper->customcurvedialog){
		looper->customcurvedialog = 
			customcurvedialog_new (looper->parent, looper->data, looper->id);
		g_object_set_data (G_OBJECT (looper->customcurvedialog), "looper", data);
		g_signal_connect (G_OBJECT(looper->customcurvedialog), "response",
                           	G_CALLBACK (looper_custommode_destroy_callback),data);
	}
}

static void loopview_changed_loopborders (GtkWidget *loopview, gpointer data){
	looper_gui_t *looper = (looper_gui_t*)data;

	looper->loopstartblock = 1;
	looper->loopendblock = 1;
	looper->looplengthblock = 1;

	if (looper->data->keepabsolute){
		gtk_spin_button_set_value ((GtkSpinButton*)looper->loopstartspin,
                	samples2speedsecs(looper,looper->data->loopstart));
		gtk_spin_button_set_value ((GtkSpinButton*)looper->loopendspin,
                	samples2speedsecs(looper,looper->data->loopend));
		gtk_spin_button_set_value ((GtkSpinButton*)looper->looplengthspin,
                	samples2speedsecs(looper,(looper->data->loopend - looper->data->loopstart)));
	}else{
		gtk_spin_button_set_value ((GtkSpinButton*)looper->loopstartspin,
                	samples2secs(looper,looper->data->loopstart));
        	gtk_spin_button_set_value ((GtkSpinButton*)looper->loopendspin,
                	samples2secs(looper,looper->data->loopend));
        	gtk_spin_button_set_value ((GtkSpinButton*)looper->looplengthspin,
                	samples2secs(looper,(looper->data->loopend - looper->data->loopstart)));
	}

	if (looper->data->linkrec2play){
		looperdata_lock(looper->data);
                looper->data->recstart = looper->data->loopstart;
                looper->data->recend = looper->data->loopend;
		looperdata_unlock(looper->data);
                looper->recstartblock = 1;
                looper->recstartblock = 1;
                looper->reclengthblock = 1;

                gtk_spin_button_set_value ((GtkSpinButton*)looper->recstartspin,
                        samples2secs(looper,looper->data->recstart));
                gtk_spin_button_set_value ((GtkSpinButton*)looper->recendspin,
                        samples2secs(looper,looper->data->recend));
                gtk_spin_button_set_value ((GtkSpinButton*)looper->reclengthspin,
                        samples2secs(looper,
                                looper->data->recend - looper->data->recstart));
                gtk_loopview_update_recstart((GtkLoopview*)looper->loopview);
                gtk_loopview_update_recend((GtkLoopview*)looper->loopview);
        }
}

static void loopview_changed_recborders (GtkWidget *loopview, gpointer data){
        looper_gui_t *looper = (looper_gui_t*)data;

	looper->recstartblock = 1;
	looper->recendblock = 1;
	looper->reclengthblock = 1;                                                
        gtk_spin_button_set_value ((GtkSpinButton*)looper->recstartspin,
                samples2secs(looper,looper->data->recstart));
        gtk_spin_button_set_value ((GtkSpinButton*)looper->recendspin,
                samples2secs(looper,looper->data->recend));
	gtk_spin_button_set_value ((GtkSpinButton*)looper->reclengthspin,
                samples2secs(looper,(looper->data->recend - looper->data->recstart)));

}              

static void loopview_changed_speed (GtkWidget *loopview, gpointer data){
        looper_gui_t *looper = (looper_gui_t*)data;

	looper->speedblock = 1;
	gtk_spin_button_set_value ((GtkSpinButton*)looper->speedspin,
                looper->data->speed);
}

static void looper_playbutton_callback (GtkWidget *playbutton,gpointer data ){
	looper_gui_t *looper = (looper_gui_t*)data;

	if (gtk_toggle_button_get_active((GtkToggleButton*)playbutton))	{
		looperdata_lock(looper->data);	
		looper->data->isplaying = 1;
		looperdata_unlock(looper->data);
	}else{
                looperdata_lock(looper->data);
                looper->data->isplaying = 0;
                looperdata_unlock(looper->data);
	}
	
}

static void looper_recbutton_callback (GtkWidget *recbutton,gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;

        if (gtk_toggle_button_get_active((GtkToggleButton*)recbutton)) {
                looperdata_lock(looper->data);
                /*looper->data->isrecording = 1;*/
		looperdata_set_recording(looper->data,1);
                looperdata_unlock(looper->data);
        }else{
                looperdata_lock(looper->data);
                /*looper->data->isrecording = 0;*/
		looperdata_set_recording(looper->data,0);
		looperdata_unlock(looper->data);
        }
}

void looper_set_minimized (looper_gui_t* looper, gboolean minimized){
	gint lastwidth = 0;
        gint lastheight = 0;

	if (minimized){
                gtk_widget_hide((GtkWidget*)looper->smallbox);
                gtk_widget_hide((GtkWidget*)looper->middlebox);
		gtk_window_get_size ((GtkWindow*)looper->parent,&lastwidth,&lastheight);
                gtk_window_resize((GtkWindow*)looper->parent,lastwidth,1);
	}else{	
		gtk_widget_show((GtkWidget*)looper->smallbox);
                gtk_widget_show((GtkWidget*)looper->middlebox);
	}
}

static void looper_minimizebutton_callback(GtkWidget *minimizebutton,gpointer data ){
	looper_gui_t *looper = (looper_gui_t*)data;

	if (gtk_toggle_button_get_active((GtkToggleButton*)minimizebutton)) 
		looper_set_minimized (looper, TRUE);
	else
		looper_set_minimized (looper, FALSE);
}

static void looper_shuttlebutton_callback (GtkWidget *shuttlebutton,gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;

        if (gtk_toggle_button_get_active((GtkToggleButton*)shuttlebutton)) {
		if (shuttle_open(looper->shuttle) < 1){
			gtk_toggle_button_set_active((GtkToggleButton*)shuttlebutton,FALSE);
		}
        }else{
                shuttle_close(looper->shuttle);
        }
}

static void looper_panslider_callback (GtkWidget *panslider, gpointer data ){
	looper_gui_t *looper = (looper_gui_t*)data;

	looperdata_lock(looper->data);
/*	looper->data->pan = gtk_range_get_value(GTK_RANGE(looper->panslider));*/
	looperdata_set_pan(looper->data,
		gtk_range_get_value(GTK_RANGE(looper->panslider)) );
	looperdata_unlock(looper->data);
}

static void looper_ngrainslider_callback (GtkWidget *ngrainslider, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;

       	looperdata_lock(looper->data);
	looperdata_set_ngrains(looper->data,
		(int)gtk_range_get_value(GTK_RANGE(looper->ngrainslider)));
       	looperdata_unlock(looper->data);
}       

static void looper_densityspin_callback (GtkWidget *densityspin, gpointer data ){
	looper_gui_t *looper = (looper_gui_t*)data;

//	printf ("densityspin callback\n");
	looperdata_lock(looper->data);
	looperdata_set_graindensity(looper->data,
		(int)gtk_spin_button_get_value((GtkSpinButton*)looper->densityspin));
	looperdata_unlock(looper->data);
}

static void looper_panswingspin_callback (GtkWidget *panswingspin, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;
        looperdata_lock(looper->data);
        looperdata_set_panswing(looper->data,
                (float)gtk_spin_button_get_value((GtkSpinButton*)looper->panswingspin));
        looperdata_unlock(looper->data);
}

/*
static void looper_grainlenslider_callback (GtkWidget *grainlenslider, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;

        looperdata_lock(looper->data);
        looperdata_set_grainlength(looper->data,
                (int)gtk_range_get_value(GTK_RANGE(looper->grainlenslider)));
        looperdata_unlock(looper->data);
}
*/


static void looper_recmixslider_callback (GtkWidget *recmixslider, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;

        looperdata_lock(looper->data);
/*        looper->data->recmix = gtk_range_get_value(GTK_RANGE(looper->recmixslider));*/
	looperdata_set_recmix(looper->data,
		gtk_range_get_value(GTK_RANGE(looper->recmixslider)) );
        looperdata_unlock(looper->data);
}

static void looper_toggle_link_rec2play(GtkWidget  *menu_item, gpointer data ){
	looper_gui_t *looper = (looper_gui_t*)data;

	if (gtk_check_menu_item_get_active((GtkCheckMenuItem*)menu_item)){
		looperdata_lock(looper->data);
        	looper->data->linkrec2play = 1;
        	looperdata_unlock(looper->data);
	}else{
		looperdata_lock(looper->data);
		looper->data->linkrec2play = 0;
        	looperdata_unlock(looper->data);
	}
}

static void looper_toggle_keep_looplength(GtkWidget  *menu_item, gpointer data ){
	looper_gui_t *looper = (looper_gui_t*)data;

	if (gtk_check_menu_item_get_active((GtkCheckMenuItem*)menu_item)){
		looperdata_lock(looper->data);
                looper->data->keepabsolute = 1;
                looperdata_unlock(looper->data);
	}else{
		looperdata_lock(looper->data);
                looper->data->keepabsolute = 0;
                looperdata_unlock(looper->data);
	}
}

static void looper_toggle_limiter_callback(GtkWidget  *menu_item, gpointer data ){
	looper_gui_t *looper = (looper_gui_t*)data;

        if (gtk_check_menu_item_get_active((GtkCheckMenuItem*)menu_item)) {
                looperdata_lock(looper->data);
                looper->data->limiter = 1;
                looperdata_unlock(looper->data);
        }else{
                looperdata_lock(looper->data);
                looper->data->limiter = 0;
                looperdata_unlock(looper->data);
        }
}

static void looper_toggle_snap2grid_callback(GtkWidget *menu_item, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;

        looperdata_lock(looper->data);
        gtk_loopview_set_snap2grid(GTK_LOOPVIEW(looper->loopview),
		gtk_check_menu_item_get_active((GtkCheckMenuItem*)menu_item));
        looperdata_unlock(looper->data);
}

static void looper_toggle_showgrid_callback(GtkWidget  *menu_item, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;

        looperdata_lock(looper->data);
        gtk_loopview_set_showgrid(GTK_LOOPVIEW(looper->loopview),
                gtk_check_menu_item_get_active((GtkCheckMenuItem*)menu_item));
        looperdata_unlock(looper->data);
}


static void looper_zoom_to_play_selection_callback(GtkWidget *menu_item, gpointer data ){
	looper_gui_t *looper = (looper_gui_t*)data;

	gtk_loopview_zoom_to_playselection((GtkLoopview*)looper->loopview);
}

static void looper_zoom_to_rec_selection_callback(GtkWidget *menu_item, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;

        gtk_loopview_zoom_to_recselection((GtkLoopview*)looper->loopview);
}   

static void looper_zoom_to_overview_callback(GtkWidget *menu_item, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;
        
        gtk_loopview_zoom_to_overview((GtkLoopview*)looper->loopview);
}

static void looper_playmode_callback(GtkWidget *menu_item, gpointer data ){
	looper_gui_t *looper = (looper_gui_t*)data;

	if (GTK_CHECK_MENU_ITEM(menu_item)->active) {
		looperdata_lock(looper->data);
		looperdata_set_playmode(looper->data,
			GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(menu_item),"playmode")));	
		looperdata_unlock(looper->data);
	}
}

static void looper_clickmode_callback(GtkWidget *menu_item, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;

        if (GTK_CHECK_MENU_ITEM(menu_item)->active) {
                looperdata_lock(looper->data);
                looperdata_set_clickmode_buf(looper->data,
			g_object_get_data (G_OBJECT(menu_item),"buffer"));
                looperdata_unlock(looper->data);
        }
}

static void looper_recmode_callback(GtkWidget *menu_item, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;

        if (GTK_CHECK_MENU_ITEM(menu_item)->active) {
                looperdata_lock(looper->data);
                looperdata_set_recmode(looper->data,
                        GPOINTER_TO_UINT(g_object_get_data (G_OBJECT(menu_item),"recmode")));
                looperdata_unlock(looper->data);
        }
}

static void looper_toggle_thresholdmode_callback(GtkWidget *menu_item, gpointer data ){
	looper_gui_t *looper = (looper_gui_t*)data;

	if (looper->data){
		looperdata_lock(looper->data);	
		if (GTK_CHECK_MENU_ITEM(menu_item)->active) {
			looperdata_set_thresholdmode(looper->data,1);
		} else looperdata_set_thresholdmode(looper->data,0);
		looperdata_unlock(looper->data);
	}
}


static void looper_volslider_callback (GtkWidget *volslider, gpointer data ){
	looper_gui_t *looper = (looper_gui_t*)data;
	float f;

	if (!looper->volblock){
		f = gtk_range_get_value(GTK_RANGE(looper->volslider));
        	looperdata_lock(looper->data);
		if (f < -47.) looperdata_set_vol (looper->data, 0);
		else looperdata_set_vol (looper->data, powf(10.0f, f * 0.05f));
/*		printf ("vol_slidercallback:%f\n",f);*/
        	looperdata_unlock(looper->data);
	}
	looper->volblock = 0;
}

static void looper_diversityspin_callback (GtkWidget *speedspin, gpointer data ){
	looper_gui_t *looper = (looper_gui_t*)data;
	GtkSpinButton *spin = GTK_SPIN_BUTTON (looper->diversityspin);

	if (looper->data){
		looperdata_lock(looper->data);
		looperdata_set_stereodiversity(looper->data,gtk_spin_button_get_value (spin));
		looperdata_unlock(looper->data);	
	}
}

static void looper_speedspin_callback (GtkWidget *speedspin, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;
	GtkSpinButton *spin = GTK_SPIN_BUTTON (looper->speedspin);
	float ltmp, rtmp;

	if (looper->speedblock){
		looper->speedblock = 0;
	}else{
		looperdata_lock(looper->data);
		if (gtk_spin_button_get_value(spin) && looper->data->keepabsolute){
				/* LATER: enhance this */
			ltmp = looper->data->loopend - looper->data->loopstart;
			ltmp *= fabs(looper->data->speed) / fabs(gtk_spin_button_get_value (spin));
			rtmp = looper->data->recend - looper->data->recstart;
			rtmp *= fabs(looper->data->speed) / fabs(gtk_spin_button_get_value (spin));
			
			
			looper->data->loopend = looper->data->loopstart + (long)ltmp;
			if (looper->data->loopend > buffer_get_size(looper->data->buf)){
                                looper->data->loopend = buffer_get_size(looper->data->buf);	
                        }
			if (looper->data->linkrec2play){
				looper->data->recend = looper->data->loopend;
			}else{
				looper->data->recend = looper->data->recstart + (long)rtmp;
				if (looper->data->recend > buffer_get_size(looper->data->buf)){
                                        looper->data->recend = buffer_get_size(looper->data->buf);
                                }
			}
		}

		looper->data->speed = gtk_spin_button_get_value (spin);
		looperdata_unlock(looper->data);
		
		if (looper->data->keepabsolute && looper->data->speed){
			/* crazy: set_range emits "value_changed" */
			looper->loopstartblock = 1;
			looper->loopendblock = 1;
			looper->looplengthblock = 1;
			gtk_spin_button_set_range((GtkSpinButton*)looper->loopstartspin,
                		0,samples2speedsecs(looper,buffer_get_size(looper->data->buf)));
        		gtk_spin_button_set_range((GtkSpinButton*)looper->loopendspin,
                		0,samples2speedsecs(looper,buffer_get_size(looper->data->buf)));
        		gtk_spin_button_set_range((GtkSpinButton*)looper->looplengthspin,
                		0,samples2speedsecs(looper,buffer_get_size(looper->data->buf)));

			looper->recstartblock = 1;
                        looper->recendblock = 1;
                        looper->reclengthblock = 1;
			gtk_spin_button_set_range((GtkSpinButton*)looper->recstartspin,
				0,samples2secs(looper,buffer_get_size(looper->data->buf)));
			gtk_spin_button_set_range((GtkSpinButton*)looper->recendspin,
                               	0,samples2secs(looper,buffer_get_size(looper->data->buf)));
			gtk_spin_button_set_range((GtkSpinButton*)looper->reclengthspin,
                               	0,samples2secs(looper,buffer_get_size(looper->data->buf)));
			

			looper->looplengthblock = 1;	
			looper->loopendblock = 1;
			gtk_spin_button_set_value ((GtkSpinButton*)looper->looplengthspin,
                        		samples2speedsecs(looper,
					looper->data->loopend - looper->data->loopstart));
			gtk_spin_button_set_value ((GtkSpinButton*)looper->loopendspin,
					looper->data->loopend);

			gtk_loopview_update_loopend((GtkLoopview*)looper->loopview);	

			looper->reclengthblock = 1;
                        looper->recendblock = 1;
			gtk_spin_button_set_value ((GtkSpinButton*)looper->reclengthspin,
                        	samples2speedsecs(looper,
                               	looper->data->recend - looper->data->recstart));
                       	gtk_spin_button_set_value ((GtkSpinButton*)looper->recendspin,
                                samples2speedsecs(looper,looper->data->recend));
                      	gtk_loopview_update_recend((GtkLoopview*)looper->loopview);

		}
	}
}        

static void looper_loopstartspin_callback (GtkWidget *loopstartspin, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;
        GtkSpinButton *spin = GTK_SPIN_BUTTON (looper->loopstartspin);
	long samples;

	if (looper->loopstartblock){
		looper->loopstartblock = 0;
	}else{
		if (looper->data->keepabsolute){
			samples = (long)speedsecs2samples(looper,gtk_spin_button_get_value (spin));
			looperdata_lock(looper->data);
                        looper->data->loopend += samples - looper->data->loopstart;
                        looperdata_unlock(looper->data);
                        looper->loopendblock = 1;
			gtk_spin_button_set_value ((GtkSpinButton*)looper->loopendspin,
                        	samples2speedsecs(looper,looper->data->loopend));
		}else{
			samples = (long)secs2samples(looper,gtk_spin_button_get_value (spin));
			looperdata_lock(looper->data);
/*			looper->data->loopend += samples - looper->data->loopstart; */
			looperdata_set_loopend(looper->data,
				looperdata_get_loopend(looper->data) + samples - looper->data->loopstart);
			looperdata_unlock(looper->data);
			looper->loopendblock = 1;
			gtk_spin_button_set_value ((GtkSpinButton*)looper->loopendspin,
				samples2secs(looper,looper->data->loopend));
		}
        	looperdata_lock(looper->data);
		looper->data->loopstart = samples;
        	looperdata_unlock(looper->data);
		gtk_loopview_update_loopstart((GtkLoopview*)looper->loopview);
		gtk_loopview_update_loopend((GtkLoopview*)looper->loopview);
		if (looper->data->linkrec2play){
			looperdata_lock(looper->data);
/*			looper->data->recstart = looper->data->loopstart;*/
/*			looper->data->recend = looper->data->loopend;*/
			looperdata_set_recstart(looper->data, looperdata_get_loopstart(looper->data));
			looperdata_set_recend(looper->data, looperdata_get_loopend(looper->data));
			looperdata_unlock(looper->data);
			looper->recstartblock = 1;
			looper->recstartblock = 1;
			gtk_spin_button_set_value ((GtkSpinButton*)looper->recstartspin,
				samples2secs(looper,looper->data->recstart));
			gtk_spin_button_set_value ((GtkSpinButton*)looper->recendspin,
				samples2secs(looper,looper->data->recend));

			gtk_loopview_update_recstart((GtkLoopview*)looper->loopview);
			gtk_loopview_update_recend((GtkLoopview*)looper->loopview);
		}
	}
}

static void looper_loopendspin_callback (GtkWidget *loopendspin, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;
        GtkSpinButton *spin = GTK_SPIN_BUTTON (looper->loopendspin);
        long samples;
	if (looper->data->keepabsolute){ 
                samples  = (long)speedsecs2samples(looper,gtk_spin_button_get_value (spin));
	}else{
                samples = (long)secs2samples(looper,gtk_spin_button_get_value (spin));
	}

        if (looper->loopendblock){
                looper->loopendblock = 0;
        }else{
                looperdata_lock(looper->data);
/*                looper->data->loopend = samples;*/
		looperdata_set_loopend (looper->data,samples);
		looperdata_unlock(looper->data);

		looper->looplengthblock = 1;
		if (looper->data->keepabsolute){
			gtk_spin_button_set_value ((GtkSpinButton*)looper->looplengthspin,
				samples2speedsecs(looper,
                        	looper->data->loopend - looper->data->loopstart));	
		}else{
			gtk_spin_button_set_value ((GtkSpinButton*)looper->looplengthspin,
                                samples2secs(looper,
                                looper->data->loopend - looper->data->loopstart));
		}
		gtk_loopview_update_loopend((GtkLoopview*)looper->loopview);

		if (looper->data->linkrec2play){
			looperdata_lock(looper->data);
/*			looper->data->recend = looper->data->loopend;*/
			looperdata_set_recend (looper->data,looperdata_get_loopend(looper->data));
			looperdata_unlock(looper->data);

			looper->recendblock = 1;
                        looper->reclengthblock = 1;
			gtk_spin_button_set_value ((GtkSpinButton*)looper->recendspin,
                                samples2secs(looper,looper->data->recend));
                        gtk_spin_button_set_value ((GtkSpinButton*)looper->reclengthspin,
                                samples2secs(looper,
                                        looper->data->recend - looper->data->recstart));

                        gtk_loopview_update_recend((GtkLoopview*)looper->loopview);
		}
        }
}

static void looper_looplengthspin_callback (GtkWidget *looplengthspin, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;
        GtkSpinButton *spin = GTK_SPIN_BUTTON (looper->looplengthspin);
        long samples;
	if (looper->data->keepabsolute){  
                samples = (long)speedsecs2samples(looper,gtk_spin_button_get_value (spin));
	}else{
		samples = (long)secs2samples(looper,gtk_spin_button_get_value (spin));
	}
        
        if (looper->looplengthblock){
                looper->looplengthblock = 0;
        }else{
		if (looper->data->buf){
			if ((samples + looper->data->loopstart) > buffer_get_size(looper->data->buf)){
				looperdata_lock(looper->data);
				looper->data->loopend = buffer_get_size(looper->data->buf);
				looperdata_unlock(looper->data);
			}else{
				looperdata_lock(looper->data);
                        	looper->data->loopend = looper->data->loopstart + samples;
                        	looperdata_unlock(looper->data);	
			}
			looper->loopendblock = 1;
			looper->looplengthblock = 1;
			if (looper->data->keepabsolute){
				gtk_spin_button_set_value ((GtkSpinButton*)looper->loopendspin,   
                               		samples2speedsecs(looper,looper->data->loopend) );
			}else{
				gtk_spin_button_set_value ((GtkSpinButton*)looper->loopendspin,
                                       	samples2secs(looper,looper->data->loopend) );
			}
	
			if (looper->data->linkrec2play){
				looperdata_lock(looper->data);
				looper->data->recend = looper->data->loopend;
				looperdata_unlock(looper->data);
				looper->recendblock = 1;
				looper->reclengthblock = 1;
				gtk_spin_button_set_value ((GtkSpinButton*)looper->recendspin,
					samples2secs(looper,looper->data->recend));
				gtk_spin_button_set_value ((GtkSpinButton*)looper->reclengthspin,
                                	samples2secs(looper,
                                        	looper->data->recend - looper->data->recstart));
				gtk_loopview_update_recend((GtkLoopview*)looper->loopview);
			}
			gtk_loopview_update_loopend((GtkLoopview*)looper->loopview);
		}
        }
}       

static void looper_recstartspin_callback (GtkWidget *recstartspin, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;
        GtkSpinButton *spin = GTK_SPIN_BUTTON (looper->recstartspin);
        long samples = (long)secs2samples(looper,gtk_spin_button_get_value (spin));

        if (looper->recstartblock){
                looper->recstartblock = 0;
        }else{
		looperdata_lock(looper->data);
		looper->data->recend += samples - looper->data->recstart;
                looper->data->recstart = samples;
                looperdata_unlock(looper->data);

		looper->recstartblock = 1;
		looper->recendblock = 1;
		looper->reclengthblock = 1;
		if (looper->data->keepabsolute){
                	gtk_spin_button_set_value ((GtkSpinButton*)looper->recendspin,
                        	samples2speedsecs(looper,looper->data->recend));
			gtk_spin_button_set_value ((GtkSpinButton*)looper->reclengthspin,
				samples2speedsecs(looper,looper->data->recend -
					looper->data->recstart));
		}else{
			gtk_spin_button_set_value ((GtkSpinButton*)looper->recendspin,
				samples2secs(looper,looper->data->recend));
			gtk_spin_button_set_value ((GtkSpinButton*)looper->reclengthspin,
                                samples2secs(looper,looper->data->recend -
                                        looper->data->recstart));
		}
                gtk_loopview_update_recstart((GtkLoopview*)looper->loopview);
		gtk_loopview_update_recend((GtkLoopview*)looper->loopview);
        }
}

static void looper_recendspin_callback (GtkWidget *recendspin, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;
        GtkSpinButton *spin = GTK_SPIN_BUTTON (looper->recendspin);
        long samples = (long)secs2samples(looper,gtk_spin_button_get_value (spin));

        if (looper->recendblock){
                looper->recendblock = 0;
        }else{
                looper->reclengthblock = 1;
                looperdata_lock(looper->data);
                looper->data->recend = samples;
		looperdata_unlock(looper->data);
		if (looper->data->keepabsolute){
			gtk_spin_button_set_value ((GtkSpinButton*)looper->reclengthspin,
                                samples2speedsecs(looper,
                                        looper->data->recend - looper->data->recstart));
		}else{
                	gtk_spin_button_set_value ((GtkSpinButton*)looper->reclengthspin,
                        	samples2secs(looper,
                        		looper->data->recend - looper->data->recstart));
		}
                gtk_loopview_update_recend((GtkLoopview*)looper->loopview);
        }
}

static void looper_reclengthspin_callback (GtkWidget *reclengthspin, gpointer data ){
        looper_gui_t *looper = (looper_gui_t*)data;
        GtkSpinButton *spin = GTK_SPIN_BUTTON (looper->reclengthspin);
        long samples = (long)secs2samples(looper,gtk_spin_button_get_value (spin));

        if (looper->reclengthblock){
                looper->reclengthblock = 0;
        }else{
		if (looper->data->buf){
			looperdata_lock(looper->data);
                	if ((samples + looper->data->recstart) > buffer_get_size(looper->data->buf)){
                        	looper->data->recend = buffer_get_size(looper->data->buf);
			}else{
				looper->data->recend = looper->data->recstart + samples;
			}
			looperdata_unlock(looper->data);
		
/*			looper->recendblock = 1;	*/
			looper->reclengthblock = 1;
			if (looper->data->keepabsolute){
                        	gtk_spin_button_set_value ((GtkSpinButton*)looper->recendspin,
                                	samples2speedsecs(looper,looper->data->recend));
                	}else{
                        	gtk_spin_button_set_value ((GtkSpinButton*)looper->recendspin,
                                	samples2secs(looper,looper->data->recend) );
                	}
		}
        }
}



/**** setup ******/

looper_gui_t* looper_new(GtkWidget* parent, GtkWidget* parentcontainer,
		alphatrack_t *alphatrack, speciallist_t *looperdatalist,
		speciallist_t *clickmodes, int id, int minimized){
        looper_gui_t  *looper;
	buffer_info_t *buf = NULL;
	GSList 	      *group = NULL;
	GSList	      *recgroup = NULL;
	GSList	      *clickgroup = NULL;
        GtkAdjustment *adj;
	GtkWidget     *menu_items;
	GtkWidget     *menu1;
	GtkWidget     *menu2;
	GtkWidget     *menu3;
	GtkWidget     *dummylabel;

	/* data initialisation */

        looper = (looper_gui_t*) malloc (sizeof(looper_gui_t));
	looper->data = looperdata_new(NULL, 44100, id);
	looper->parent = parent;
	looper->parentcontainer = parentcontainer;
	looper->id = id;
	looper->gid = 0;
	looper->dirty = 0;
	looper->bufferlist = speciallist_new();
	looper->vposition = 0;
	looper->remote_controlled = 0;
	looper->bg_set = 1;
	looper->looperdatalist = looperdatalist;
	looper->clickmodes = clickmodes;
	looper->shuttle = shuttle_new();
	looper->alphatrack = alphatrack;

	looper->encoderdelta_l = 0;
        looper->encoderdelta_m = 0;
        looper->encoderdelta_r = 0;

	looper->loopstartblock = 0;
	looper->loopendblock = 0;
	looper->looplengthblock = 0;
	looper->recstartblock = 0;
	looper->recendblock = 0;
	looper->reclengthblock = 0;
	looper->speedblock = 0;
	looper->vublock = 0;
	looper->volblock = 0;

#if 0
	/* try: */
        /* LATER: replayce with gui */
	printf ("set\n");	
        looperdata_set_reverblength(looper->data,44100);
        looperdata_set_feedback(looper->data,.8);
	printf ("done\n");
#endif

	
	/* widget initialisation */
	looper->customcurvedialog = NULL;

	looper->tooltips = gtk_tooltips_new ();
	looper->font = pango_font_description_from_string("Sans 7");

        /* small control widgets */
        looper->smalltable = gtk_table_new (3, 3, TRUE);

	/* playback loop control */
        adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 10.0, .001, .1, 0.0);
        looper->loopstartspin = gtk_spin_button_new (adj, .01, 4);
	g_signal_connect (G_OBJECT (adj), "value_changed",
                        G_CALLBACK (looper_loopstartspin_callback), (gpointer) looper);
	gtk_table_attach((GtkTable*)looper->smalltable, looper->loopstartspin,0,1,0,1,GTK_FILL,GTK_FILL,1,3);
	gtk_tooltips_set_tip (looper->tooltips,looper->loopstartspin, "playback start position", NULL);
	gtk_widget_modify_font(looper->loopstartspin,looper->font);
        gtk_widget_show (looper->loopstartspin);

        adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 10.0, .001, .1, 0.0);
        looper->loopendspin = gtk_spin_button_new (adj, 1, 4);
        g_signal_connect (G_OBJECT (adj), "value_changed",
                        G_CALLBACK (looper_loopendspin_callback), (gpointer) looper);
	gtk_table_attach((GtkTable*)looper->smalltable, looper->loopendspin,1,2,0,1,GTK_FILL,GTK_FILL,1,3);
	gtk_tooltips_set_tip (looper->tooltips,looper->loopendspin, "playback end position", NULL);
	gtk_widget_modify_font(looper->loopendspin,looper->font);
        gtk_widget_show (looper->loopendspin);

	adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 10.0, .001, .1, 0.0);
        looper->looplengthspin = gtk_spin_button_new (adj, .01, 4);
	g_signal_connect (G_OBJECT (adj), "value_changed",
                        G_CALLBACK (looper_looplengthspin_callback), (gpointer) looper);
        gtk_table_attach((GtkTable*)looper->smalltable, looper->looplengthspin,2,3,0,1,GTK_FILL,GTK_FILL,1,3);
        gtk_tooltips_set_tip (looper->tooltips,looper->looplengthspin, "playback looplength", NULL);
        gtk_widget_modify_font(looper->looplengthspin,looper->font);
        gtk_widget_show (looper->looplengthspin);

	/* recording loop control */
	adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 10.0, .001, .1, 0.0);
        looper->recstartspin = gtk_spin_button_new (adj, .01, 4);
	g_signal_connect (G_OBJECT (adj), "value_changed",
                        G_CALLBACK (looper_recstartspin_callback), (gpointer) looper);
        gtk_table_attach((GtkTable*)looper->smalltable, looper->recstartspin,0,1,1,2,GTK_FILL,GTK_FILL,1,3);
        gtk_tooltips_set_tip (looper->tooltips,looper->recstartspin, "rec start position", NULL);
	gtk_widget_modify_font(looper->recstartspin,looper->font);
        gtk_widget_show (looper->recstartspin);
                
        adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 10.0, .001, .1, 0.0);
        looper->recendspin = gtk_spin_button_new (adj, .01, 4);
	g_signal_connect (G_OBJECT (adj), "value_changed",
                        G_CALLBACK (looper_recendspin_callback), (gpointer) looper);
        gtk_table_attach((GtkTable*)looper->smalltable, looper->recendspin,1,2,1,2,GTK_FILL,GTK_FILL,1,3);
        gtk_tooltips_set_tip (looper->tooltips,looper->recendspin, "rec end position", NULL);
	gtk_widget_modify_font(looper->recendspin,looper->font);
        gtk_widget_show (looper->recendspin);

	adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 10.0, .001, .1, 0.0);
        looper->reclengthspin = gtk_spin_button_new (adj, .01, 4);
	g_signal_connect (G_OBJECT (adj), "value_changed",
                        G_CALLBACK (looper_reclengthspin_callback), (gpointer) looper);
        gtk_table_attach((GtkTable*)looper->smalltable, looper->reclengthspin,2,3,1,2,GTK_FILL,GTK_FILL,1,3);
        gtk_tooltips_set_tip (looper->tooltips,looper->reclengthspin, "recloop length", NULL);
        gtk_widget_modify_font(looper->reclengthspin,looper->font);
        gtk_widget_show (looper->reclengthspin);

	/* speed control */
	adj = (GtkAdjustment *) gtk_adjustment_new (1.0, -100.0, 100.0, .001, 1., 0); /* was 1. page smaller than tick */
        looper->speedspin = gtk_spin_button_new (adj, .005, 4);
	g_signal_connect (G_OBJECT (adj), "value_changed",
                        G_CALLBACK (looper_speedspin_callback), (gpointer) looper);	
        gtk_table_attach((GtkTable*)looper->smalltable, looper->speedspin,0,2,2,3,GTK_FILL,GTK_FILL,1,3);
        gtk_tooltips_set_tip (looper->tooltips,looper->speedspin, "playback speed", NULL);
	gtk_widget_modify_font(looper->speedspin,looper->font);
        gtk_widget_show (looper->speedspin);

	/* stereodiversity control */
        adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 1.0, .01, .1, 0.); /* was .1 page smaller than tick */
        looper->diversityspin = gtk_spin_button_new (adj, .0, 2);
	g_signal_connect (G_OBJECT (adj), "value_changed",
                        G_CALLBACK (looper_diversityspin_callback), (gpointer) looper);
        gtk_table_attach((GtkTable*)looper->smalltable, looper->diversityspin,2,3,2,3,GTK_FILL,GTK_FILL,1,3);
        gtk_tooltips_set_tip (looper->tooltips,looper->diversityspin, "stereo diversity", NULL);
        gtk_widget_modify_font(looper->diversityspin,looper->font);
        gtk_widget_show (looper->diversityspin);

	gtk_widget_show (looper->smalltable);


	looper->smallbox = gtk_vbox_new (FALSE,5);
	gtk_box_pack_start (GTK_BOX (looper->smallbox),looper->smalltable,FALSE, FALSE, 0);

	looper->grainbox = gtk_hbox_new (FALSE,5);	
	/* number of grains */
        looper->ngrainslider = gtk_hscale_new_with_range (0, 15, 1);
        gtk_scale_set_draw_value((GtkScale *)looper->ngrainslider, TRUE);
        gtk_scale_set_digits((GtkScale *)looper->ngrainslider, 0);
        gtk_scale_set_value_pos((GtkScale *)looper->ngrainslider, GTK_POS_LEFT);
        gtk_range_set_value (GTK_RANGE(looper->ngrainslider),0);
/*      gtk_table_attach((GtkTable*)looper->smalltable, looper->ngrainslider,0,3,3,4,GTK_FILL,GTK_FILL,1,3); */
	gtk_box_pack_start (GTK_BOX (looper->grainbox),looper->ngrainslider,TRUE, TRUE, 0);
        gtk_tooltips_set_tip (looper->tooltips,looper->ngrainslider, "number of grains", NULL);
        gtk_widget_modify_font(looper->ngrainslider,looper->font);
        gtk_widget_show (looper->ngrainslider);

	adj = (GtkAdjustment *) gtk_adjustment_new (100.0, 0.0, 100.0, .1, 1., 0); /* was page: 1. */
        looper->densityspin = gtk_spin_button_new (adj, 1.0, 0);
	g_signal_connect (G_OBJECT (adj), "value_changed",
                        G_CALLBACK (looper_densityspin_callback), (gpointer)looper);
	gtk_box_pack_end (GTK_BOX (looper->grainbox),looper->densityspin,FALSE, FALSE, 0);
        gtk_tooltips_set_tip (looper->tooltips,looper->densityspin, "grain density", NULL);
        gtk_widget_modify_font(looper->densityspin,looper->font);
        gtk_widget_show (looper->densityspin);

	gtk_box_pack_start (GTK_BOX (looper->smallbox),looper->grainbox, FALSE, FALSE, 0);
	gtk_widget_show (looper->grainbox);
/*
  	looper->grainlenslider = gtk_hscale_new_with_range (256, 44100, 1);
        gtk_scale_set_draw_value((GtkScale *)looper->grainlenslider, TRUE);
        gtk_scale_set_digits((GtkScale *)looper->grainlenslider, 0);
        gtk_scale_set_value_pos((GtkScale *)looper->grainlenslider, GTK_POS_LEFT);
        gtk_range_set_value (GTK_RANGE(looper->grainlenslider),0);
        gtk_box_pack_start (GTK_BOX (looper->smallbox),looper->grainlenslider,FALSE, FALSE, 0);
        gtk_tooltips_set_tip (looper->tooltips,looper->grainlenslider, "length of grains (samples)", NULL);
        gtk_widget_modify_font(looper->grainlenslider,looper->font);
        gtk_widget_show (looper->grainlenslider);
*/

	looper->panbox = gtk_hbox_new (FALSE,5);

        looper->panslider = gtk_hscale_new_with_range (-1., 1., .01);
	gtk_scale_set_draw_value((GtkScale *)looper->panslider, TRUE);
	gtk_scale_set_digits((GtkScale *)looper->panslider, 2);
	gtk_scale_set_value_pos((GtkScale *)looper->panslider, GTK_POS_BOTTOM);
	gtk_range_set_value (GTK_RANGE(looper->panslider),.0);
	gtk_box_pack_start (GTK_BOX (looper->panbox),looper->panslider, TRUE, TRUE, 0);
	gtk_tooltips_set_tip (looper->tooltips,looper->panslider, "pan L<->R", NULL);
	gtk_widget_modify_font(looper->panslider,looper->font);
	gtk_widget_show (looper->panslider);

	adj = (GtkAdjustment *) gtk_adjustment_new (0.0, 0.0, 30.0, .01, 1., 0.); /* was page: 1. */
        looper->panswingspin = gtk_spin_button_new (adj, .01, 2);
	g_signal_connect (G_OBJECT (adj), "value-changed",
                        G_CALLBACK (looper_panswingspin_callback), (gpointer) looper);
        gtk_box_pack_end (GTK_BOX (looper->panbox),looper->panswingspin,FALSE, FALSE, 0);
        gtk_tooltips_set_tip (looper->tooltips,looper->panswingspin, "auto panner (hz)", NULL);
        gtk_widget_modify_font(looper->panswingspin,looper->font);
        gtk_widget_show (looper->panswingspin);


        gtk_box_pack_start (GTK_BOX (looper->smallbox),looper->panbox, FALSE, FALSE, 0);
        gtk_widget_show (looper->panbox);
	
	gtk_widget_show (looper->smallbox);

        /* middle control elements */
        looper->middletable = gtk_table_new (1, 13, TRUE);

	adj = (GtkAdjustment *) gtk_adjustment_new (.0, -70., 6.0, .0, .0, 0.0);
	looper->outLmeter = gtk_meter_new(adj, GTK_METER_UP);
	gtk_table_attach((GtkTable*)looper->middletable, looper->outLmeter,0,1,0,1,GTK_FILL,GTK_FILL,1,3);
	gtk_tooltips_set_tip (looper->tooltips,looper->outLmeter, "left output", NULL);
	gtk_widget_show (looper->outLmeter);

	adj = (GtkAdjustment *) gtk_adjustment_new (.0, -70., 6.0, .0, .0, 0.0);
        looper->outRmeter = gtk_meter_new(adj, GTK_METER_UP);
        gtk_table_attach((GtkTable*)looper->middletable, looper->outRmeter,1,2,0,1,GTK_FILL,GTK_FILL,1,3);
	gtk_tooltips_set_tip (looper->tooltips,looper->outRmeter, "right output", NULL);
        gtk_widget_show (looper->outRmeter);

	looper->volslider = gtk_vscale_new (GTK_ADJUSTMENT (gtk_adjustment_new (0., -48., 18., 1., 6., 0.)));
	gtk_scale_set_draw_value((GtkScale *)looper->volslider, TRUE);
	gtk_scale_set_digits((GtkScale *)looper->volslider, 0);
	gtk_range_set_inverted (GTK_RANGE (looper->volslider), TRUE);
	gtk_scale_set_value_pos((GtkScale *)looper->volslider, GTK_POS_BOTTOM);
	gtk_range_set_value (GTK_RANGE(looper->volslider),.0);
	gtk_table_attach((GtkTable*)looper->middletable, looper->volslider,2,6,0,1,GTK_FILL,GTK_FILL,3,3);
	gtk_tooltips_set_tip (looper->tooltips,looper->volslider, "output volume", NULL);
	gtk_widget_modify_font(looper->volslider,looper->font);
	gtk_widget_show (looper->volslider);

	adj = (GtkAdjustment *) gtk_adjustment_new (.0, -70., 6.0, .0, .0, 0.0);
        looper->inLmeter = gtk_meter_new(adj, GTK_METER_UP);
        gtk_table_attach((GtkTable*)looper->middletable, looper->inLmeter,7,8,0,1,GTK_FILL,GTK_FILL,1,3);
        gtk_tooltips_set_tip (looper->tooltips,looper->inLmeter, "left input", NULL);
        gtk_widget_show (looper->inLmeter);

        adj = (GtkAdjustment *) gtk_adjustment_new (.0, -70., 6.0, .0, .0, 0.0);
        looper->inRmeter = gtk_meter_new(adj, GTK_METER_UP);
        gtk_table_attach((GtkTable*)looper->middletable, looper->inRmeter,8,9,0,1,GTK_FILL,GTK_FILL,1,3);
        gtk_tooltips_set_tip (looper->tooltips,looper->inRmeter, "right input", NULL);
        gtk_widget_show (looper->inRmeter);

	looper->recmixslider = gtk_vscale_new_with_range (-2., 2, .01);
        gtk_scale_set_draw_value((GtkScale *)looper->recmixslider, TRUE);
        gtk_scale_set_digits((GtkScale *)looper->recmixslider, 2);
	gtk_range_set_inverted (GTK_RANGE (looper->recmixslider), TRUE);
        gtk_scale_set_value_pos((GtkScale *)looper->recmixslider, GTK_POS_BOTTOM);
	gtk_range_set_value (GTK_RANGE(looper->recmixslider),1);
        gtk_table_attach((GtkTable*)looper->middletable, looper->recmixslider,9,13,0,1,GTK_FILL,GTK_FILL,3,3);
        gtk_tooltips_set_tip (looper->tooltips,looper->recmixslider, "recording<->original mix (>1 = overdrive)", NULL);
	gtk_widget_modify_font(looper->recmixslider,looper->font);
        gtk_widget_show (looper->recmixslider);

        gtk_widget_show (looper->middletable);

	looper->middlebox = gtk_hbox_new (FALSE,3);
	gtk_box_pack_start (GTK_BOX (looper->middlebox),looper->middletable, FALSE, FALSE, 0);
/*	gtk_box_pack_start (GTK_BOX (looper->middlebox),looper->smalltable, FALSE, FALSE, 0);*/	
	gtk_box_pack_start (GTK_BOX (looper->middlebox),looper->smallbox, FALSE, FALSE, 0);
	gtk_widget_show (looper->middlebox);


	/* topbox with bufferselector, play-rec-buttons */

	looper->topbox = gtk_hbox_new (FALSE,5);

	/* GtkOptionMenu might be easier ...??? */
	menu1 = gtk_menu_new ();
/*
        menu_items =  gtk_menu_item_new_with_label ("normalize buffer");
        g_signal_connect (G_OBJECT (menu_items), "activate",
                                                G_CALLBACK (looper_normalize_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu1), menu_items);
        gtk_widget_show (menu_items);

        menu_items =  gtk_menu_item_new_with_label ("clear buffer");
        g_signal_connect (G_OBJECT (menu_items), "activate",
                                                G_CALLBACK (looper_clear_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu1), menu_items);
        gtk_widget_show (menu_items);

        menu_items = gtk_separator_menu_item_new();
        gtk_menu_shell_append (GTK_MENU_SHELL (menu1), menu_items);
        gtk_widget_show (menu_items);
*/	
	menu_items = gtk_menu_item_new_with_label ("no buffer available");
	gtk_menu_shell_append (GTK_MENU_SHELL (menu1), menu_items);
	gtk_widget_show (menu_items);
	gtk_widget_show (menu1);

	looper->buffermenu = gtk_menu_item_new_with_label ("select buffer");
	gtk_menu_item_set_submenu (GTK_MENU_ITEM (looper->buffermenu), menu1);
	gtk_widget_show (looper->buffermenu);

	/* options menu */
	menu2 = gtk_menu_new ();
        menu_items =  gtk_check_menu_item_new_with_label ("link rec to play loop");
	gtk_check_menu_item_set_active ((GtkCheckMenuItem*)menu_items,TRUE);
	g_signal_connect (G_OBJECT (menu_items), "activate",
                                                G_CALLBACK (looper_toggle_link_rec2play),
                                                looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);

	menu_items =  gtk_check_menu_item_new_with_label ("keep absolute looplength");
        gtk_check_menu_item_set_active ((GtkCheckMenuItem*)menu_items,FALSE);
        g_signal_connect (G_OBJECT (menu_items), "activate",
                                                G_CALLBACK (looper_toggle_keep_looplength),
                                                looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);
        
        looper->limiteritem =  gtk_check_menu_item_new_with_label ("limit i/o signal");
        gtk_check_menu_item_set_active ((GtkCheckMenuItem*)looper->limiteritem,TRUE);
        g_signal_connect (G_OBJECT (looper->limiteritem), "activate",
                                                G_CALLBACK (looper_toggle_limiter_callback),
                                                looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), looper->limiteritem);
        gtk_widget_show (looper->limiteritem);
        
        menu_items = gtk_separator_menu_item_new();
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);
	
	menu_items =  gtk_menu_item_new_with_label ("zoom to play selection");
        g_signal_connect (G_OBJECT (menu_items), "activate",
                                                G_CALLBACK (looper_zoom_to_play_selection_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);

        menu_items =  gtk_menu_item_new_with_label ("zoom to rec selection");
        g_signal_connect (G_OBJECT (menu_items), "activate",
                                                G_CALLBACK (looper_zoom_to_rec_selection_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);

	menu_items =  gtk_menu_item_new_with_label ("zoom to overview");
        g_signal_connect (G_OBJECT (menu_items), "activate",
                                                G_CALLBACK (looper_zoom_to_overview_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);

        menu_items = gtk_separator_menu_item_new();
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);


	/* grid stuff */
        looper->snap2griditem =  gtk_check_menu_item_new_with_label ("snap to grid");
        gtk_check_menu_item_set_active ((GtkCheckMenuItem*)looper->snap2griditem,FALSE);
        g_signal_connect (G_OBJECT (looper->snap2griditem), "activate",
                                                G_CALLBACK (looper_toggle_snap2grid_callback),
                                                looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), looper->snap2griditem);
        gtk_widget_show (looper->snap2griditem);

        looper->showgriditem =  gtk_check_menu_item_new_with_label ("show grid");
        gtk_check_menu_item_set_active ((GtkCheckMenuItem*)looper->showgriditem,FALSE);
        g_signal_connect (G_OBJECT (looper->showgriditem), "activate",
                                                G_CALLBACK (looper_toggle_showgrid_callback),
                                                looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), looper->showgriditem);
        gtk_widget_show (looper->showgriditem);

	menu_items =  gtk_menu_item_new_with_label ("grid from play selection");
	g_signal_connect (G_OBJECT (menu_items), "activate",
						G_CALLBACK (looper_gridfromplayselection_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);
	
	menu_items =  gtk_menu_item_new_with_label ("grid from rec selection");
        g_signal_connect (G_OBJECT (menu_items), "activate",
                                                G_CALLBACK (looper_gridfromrecselection_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);

        menu_items =  gtk_menu_item_new_with_label ("grid settings");
        g_signal_connect (G_OBJECT (menu_items), "activate",
                                                G_CALLBACK (looper_gridsettings_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);

        menu_items = gtk_separator_menu_item_new();
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);

	/* grain stuff */
	menu_items =  gtk_menu_item_new_with_label ("grain settings");
        g_signal_connect (G_OBJECT (menu_items), "activate",
                                                G_CALLBACK (looper_grainsettings_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);


        menu_items = gtk_separator_menu_item_new();
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);

        menu_items =  gtk_menu_item_new_with_label ("threshold recording settings");
        g_signal_connect (G_OBJECT (menu_items), "activate",
                                                G_CALLBACK (looper_threshsettings_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);

        menu_items = gtk_separator_menu_item_new();
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);


        menu_items =  gtk_menu_item_new_with_label ("close this looper");
        g_signal_connect (G_OBJECT (menu_items), "activate",
                                                G_CALLBACK (looper_close_looper_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu2), menu_items);
        gtk_widget_show (menu_items);

        gtk_widget_show (menu2);



        looper->optionsmenu = gtk_menu_item_new_with_label ("options");
        gtk_menu_item_set_submenu (GTK_MENU_ITEM (looper->optionsmenu), menu2);
        gtk_widget_show (looper->optionsmenu);

	/* playmode menu */
	menu3 = gtk_menu_new ();

        looper->loopmode = gtk_radio_menu_item_new_with_label (group,"loop");
	group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (looper->loopmode));
	g_object_set_data (G_OBJECT (looper->loopmode), "playmode",GUINT_TO_POINTER(LOOP_PLAYMODE_LOOP));
	gtk_check_menu_item_set_active ((GtkCheckMenuItem*)looper->loopmode,TRUE);
        g_signal_connect (G_OBJECT (looper->loopmode), "activate",
                                                G_CALLBACK (looper_playmode_callback),
                                                looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu3), looper->loopmode);
        gtk_widget_show (looper->loopmode);

	looper->backforthmode =  gtk_radio_menu_item_new_with_label (group,"back'n'forth");
	group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (looper->backforthmode));
        gtk_check_menu_item_set_active ((GtkCheckMenuItem*)looper->backforthmode,FALSE);
        g_object_set_data (G_OBJECT (looper->backforthmode), "playmode",GUINT_TO_POINTER(LOOP_PLAYMODE_BACKFORTH));
        g_signal_connect (G_OBJECT (looper->backforthmode), "activate",
                                                G_CALLBACK (looper_playmode_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu3), looper->backforthmode);
        gtk_widget_show (looper->backforthmode);

        looper->oncemode =  gtk_radio_menu_item_new_with_label (group,"once");
        group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (looper->oncemode));
        gtk_check_menu_item_set_active ((GtkCheckMenuItem*)looper->oncemode,FALSE);
        g_object_set_data (G_OBJECT (looper->oncemode), "playmode",GUINT_TO_POINTER(LOOP_PLAYMODE_ONCE));
        g_signal_connect (G_OBJECT (looper->oncemode), "activate",
                                                G_CALLBACK (looper_playmode_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu3), looper->oncemode);
        gtk_widget_show (looper->oncemode);

	looper->sinewavemode =  gtk_radio_menu_item_new_with_label (group,"sinewave");
	group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (looper->sinewavemode));
        gtk_check_menu_item_set_active ((GtkCheckMenuItem*)looper->sinewavemode,FALSE);
        g_object_set_data (G_OBJECT (looper->sinewavemode), "playmode",GUINT_TO_POINTER(LOOP_PLAYMODE_SINE));
        g_signal_connect (G_OBJECT (looper->sinewavemode), "activate",
                                                G_CALLBACK (looper_playmode_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu3), looper->sinewavemode);
        gtk_widget_show (looper->sinewavemode);
	
        looper->externalposmode =  gtk_radio_menu_item_new_with_label (group,"external pointer");
        group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (looper->externalposmode));
        gtk_check_menu_item_set_active ((GtkCheckMenuItem*)looper->externalposmode,FALSE);
        g_object_set_data (G_OBJECT (looper->externalposmode), "playmode",GUINT_TO_POINTER(LOOP_PLAYMODE_EXTERNALPOS));
        g_signal_connect (G_OBJECT (looper->externalposmode), "activate",
                                                G_CALLBACK (looper_playmode_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu3), looper->externalposmode);
        gtk_widget_show (looper->externalposmode);

	looper->customspeedmode =  gtk_radio_menu_item_new_with_label (group,"custom speed");
        group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (looper->customspeedmode));
        gtk_check_menu_item_set_active ((GtkCheckMenuItem*)looper->customspeedmode,FALSE);
        g_object_set_data (G_OBJECT (looper->customspeedmode), "playmode",GUINT_TO_POINTER(LOOP_PLAYMODE_CUSTOMSPEED));
        g_signal_connect (G_OBJECT (looper->customspeedmode), "activate",
                                                G_CALLBACK (looper_playmode_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu3), looper->customspeedmode);
        gtk_widget_show (looper->customspeedmode);

        looper->customposmode =  gtk_radio_menu_item_new_with_label (group,"custom position");
        group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (looper->customposmode));
        gtk_check_menu_item_set_active ((GtkCheckMenuItem*)looper->customposmode,FALSE);
        g_object_set_data (G_OBJECT (looper->customposmode), "playmode",GUINT_TO_POINTER(LOOP_PLAYMODE_POSITION));
        g_signal_connect (G_OBJECT (looper->customposmode), "activate",
                                                G_CALLBACK (looper_playmode_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu3), looper->customposmode);
        gtk_widget_show (looper->customposmode);

	/* clickmode menu */
	menu_items = gtk_separator_menu_item_new();
	gtk_menu_shell_append (GTK_MENU_SHELL (menu3), menu_items);
	gtk_widget_show (menu_items);

	menu_items = gtk_radio_menu_item_new_with_label (clickgroup,"no click");
        clickgroup = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (menu_items));
        gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM(menu_items),TRUE);
        g_signal_connect (G_OBJECT (menu_items), "activate",
        	G_CALLBACK (looper_clickmode_callback), looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu3), menu_items);
        gtk_widget_show (menu_items);

	buf = speciallist_get_first(looper->clickmodes);
        while(buf){
		menu_items = gtk_radio_menu_item_new_with_label (clickgroup,buffer_get_shortname(buf));
		clickgroup = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (menu_items));
                g_object_set_data (G_OBJECT (menu_items), "buffer",buf);
                g_signal_connect (G_OBJECT (menu_items), "activate",
                        G_CALLBACK (looper_clickmode_callback), looper);
		gtk_menu_shell_append (GTK_MENU_SHELL (menu3), menu_items);
                gtk_widget_show (menu_items);
                buf = speciallist_get_next(looper->clickmodes,buf);
        }

	menu_items = gtk_separator_menu_item_new();
        gtk_menu_shell_append (GTK_MENU_SHELL (menu3), menu_items);
        gtk_widget_show (menu_items);

	menu_items =  gtk_menu_item_new_with_label ("edit custom playmode");
        g_signal_connect (G_OBJECT (menu_items), "activate",
                                                G_CALLBACK (looper_custommode_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu3), menu_items);
        gtk_widget_show (menu_items);

        menu_items = gtk_separator_menu_item_new();
        gtk_menu_shell_append (GTK_MENU_SHELL (menu3), menu_items);
        gtk_widget_show (menu_items);

        looper->recloopmode = gtk_radio_menu_item_new_with_label (recgroup,"looped recording");
        recgroup = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (looper->recloopmode));
        g_object_set_data (G_OBJECT (looper->recloopmode), "recmode",GUINT_TO_POINTER(LOOP_RECMODE_LOOP));
        gtk_check_menu_item_set_active ((GtkCheckMenuItem*)looper->recloopmode,TRUE);
        g_signal_connect (G_OBJECT (looper->recloopmode), "activate",
                                                G_CALLBACK (looper_recmode_callback),
                                                looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu3), looper->recloopmode);
        gtk_widget_show (looper->recloopmode);

        looper->reconcemode =  gtk_radio_menu_item_new_with_label (recgroup,"single recording");
        recgroup = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (looper->reconcemode));
        gtk_check_menu_item_set_active ((GtkCheckMenuItem*)looper->reconcemode,FALSE);
        g_object_set_data (G_OBJECT (looper->reconcemode), "recmode",GUINT_TO_POINTER(LOOP_RECMODE_ONCE));
        g_signal_connect (G_OBJECT (looper->reconcemode), "activate",
                                                G_CALLBACK (looper_recmode_callback),
                                                (gpointer)looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu3), looper->reconcemode);
        gtk_widget_show (looper->reconcemode);

	/*looperdata_set_thresholdmode (new_data, 1); */
	looper->thresholdtoggle =  gtk_check_menu_item_new_with_label ("threshold recording");
        gtk_check_menu_item_set_active ((GtkCheckMenuItem*)looper->thresholdtoggle,FALSE);
        g_signal_connect (G_OBJECT (looper->thresholdtoggle), "activate",
                                                G_CALLBACK (looper_toggle_thresholdmode_callback),
                                                looper);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu3), looper->thresholdtoggle);
        gtk_widget_show (looper->thresholdtoggle);

	gtk_widget_show (menu3);

        looper->playmodemenu = gtk_menu_item_new_with_label ("modes");
        gtk_menu_item_set_submenu (GTK_MENU_ITEM (looper->playmodemenu), menu3);
        gtk_widget_show (looper->playmodemenu);

	/* menu bar */
        looper->menu_bar = gtk_menu_bar_new ();
        gtk_menu_shell_append (GTK_MENU_SHELL (looper->menu_bar), looper->buffermenu);
        gtk_menu_shell_append (GTK_MENU_SHELL (looper->menu_bar), looper->optionsmenu);
	gtk_menu_shell_append (GTK_MENU_SHELL (looper->menu_bar), looper->playmodemenu);
        gtk_box_pack_start (GTK_BOX (looper->topbox),looper->menu_bar,FALSE, FALSE, 0);
        gtk_widget_modify_font(looper->menu_bar,looper->font);
        gtk_widget_show (looper->menu_bar);

	
	looper->playimage = gtk_image_new_from_stock (GTK_STOCK_GO_FORWARD,GTK_ICON_SIZE_SMALL_TOOLBAR);
	looper->playbutton = gtk_toggle_button_new ();
	gtk_container_add (GTK_CONTAINER (looper->playbutton),looper->playimage);
	gtk_box_pack_start (GTK_BOX (looper->topbox),looper->playbutton,FALSE, FALSE, 0);
	gtk_tooltips_set_tip (looper->tooltips,looper->playbutton,"play/loop", NULL);
	gtk_widget_show (looper->playimage);
	gtk_widget_show (looper->playbutton);

	looper->recimage = gtk_image_new_from_stock (GTK_STOCK_NO,GTK_ICON_SIZE_SMALL_TOOLBAR);
	looper->recbutton = gtk_toggle_button_new ();
	gtk_container_add (GTK_CONTAINER (looper->recbutton),looper->recimage);
        gtk_box_pack_start (GTK_BOX (looper->topbox),looper->recbutton,FALSE, FALSE, 0);
        gtk_tooltips_set_tip (looper->tooltips,looper->recbutton,"record", NULL);
	gtk_widget_show (looper->recimage);
        gtk_widget_show (looper->recbutton);

        looper->shuttlebutton = gtk_toggle_button_new_with_label ("S");
        gtk_box_pack_start (GTK_BOX (looper->topbox),looper->shuttlebutton,FALSE, FALSE, 0);
        gtk_tooltips_set_tip (looper->tooltips,looper->shuttlebutton,"toggle external shuttle control", NULL);
        gtk_widget_show (looper->shuttlebutton);

/*
	looper->limiterbutton = gtk_toggle_button_new_with_label ("L");
        gtk_box_pack_start (GTK_BOX (looper->topbox),looper->limiterbutton,FALSE, FALSE, 0);
        gtk_tooltips_set_tip (looper->tooltips,looper->limiterbutton,"activate input-/output limiter", NULL);
        gtk_widget_show (looper->limiterbutton);
*/
/*
	looper->normbutton = gtk_button_new_with_label ("N");
	gtk_box_pack_start (GTK_BOX (looper->topbox),looper->normbutton,FALSE, FALSE, 0);
	gtk_tooltips_set_tip (looper->tooltips,looper->normbutton,"normalize buffer", NULL);
	gtk_widget_show (looper->normbutton);
*/
	looper->minimizebutton = gtk_toggle_button_new_with_label ("M");
        gtk_box_pack_start (GTK_BOX (looper->topbox),looper->minimizebutton,FALSE, FALSE, 0);
        gtk_tooltips_set_tip (looper->tooltips,looper->minimizebutton,"minimize looper", NULL);
        gtk_widget_show (looper->minimizebutton);

	looper->upimage = gtk_image_new_from_stock (GTK_STOCK_GO_UP,GTK_ICON_SIZE_SMALL_TOOLBAR);
	looper->upbutton = gtk_button_new();
	gtk_container_add (GTK_CONTAINER (looper->upbutton),looper->upimage);
	gtk_box_pack_start (GTK_BOX (looper->topbox),looper->upbutton,FALSE, FALSE, 0);
	gtk_tooltips_set_tip (looper->tooltips,looper->upbutton,"move looper up", NULL);
	gtk_widget_show (looper->upimage);
	gtk_widget_show (looper->upbutton);

	looper->downimage = gtk_image_new_from_stock (GTK_STOCK_GO_DOWN,GTK_ICON_SIZE_SMALL_TOOLBAR);
	looper->downbutton = gtk_button_new ();
	gtk_container_add (GTK_CONTAINER (looper->downbutton),looper->downimage);
        gtk_box_pack_start (GTK_BOX (looper->topbox),looper->downbutton,FALSE, FALSE, 0);
        gtk_tooltips_set_tip (looper->tooltips,looper->downbutton,"move looper down", NULL);
	gtk_widget_show (looper->downimage);
        gtk_widget_show (looper->downbutton);

	
	dummylabel = gtk_label_new("  ");
	gtk_box_pack_start (GTK_BOX (looper->topbox),dummylabel,FALSE, FALSE, 0);
	gtk_widget_show (dummylabel);


	gtk_widget_show (looper->topbox);

	/* controlbox with controlframe */

	looper->controlframe = gtk_frame_new ("...");
	gtk_frame_set_shadow_type((GtkFrame*)looper->controlframe, GTK_SHADOW_ETCHED_IN);
	gtk_widget_show (looper->controlframe);

	looper->controlbox = gtk_vbox_new (FALSE,0);

	gtk_box_pack_start (GTK_BOX (looper->controlbox),looper->topbox, TRUE, TRUE, 0);
	gtk_box_pack_start (GTK_BOX (looper->controlbox),looper->middlebox, FALSE, FALSE, 0);

/*
	looper->panslider = gtk_hscale_new_with_range (-1., 1., .01);
        gtk_scale_set_draw_value((GtkScale *)looper->panslider, TRUE);
        gtk_scale_set_digits((GtkScale *)looper->panslider, 2);
        gtk_scale_set_value_pos((GtkScale *)looper->panslider, GTK_POS_BOTTOM);
	gtk_range_set_value (GTK_RANGE(looper->panslider),.0);
	gtk_box_pack_start (GTK_BOX (looper->controlbox),looper->panslider, FALSE, FALSE, 0);
        gtk_tooltips_set_tip (looper->tooltips,looper->panslider, "pan L<->R", NULL);
	gtk_widget_modify_font(looper->panslider,looper->font);
        gtk_widget_show (looper->panslider);
*/

	gtk_widget_show (looper->controlbox);
	gtk_container_add (GTK_CONTAINER (looper->controlframe), looper->controlbox);


	/* and finally the mainbox */

	looper->mainbox = gtk_hbox_new (FALSE,0);
	gtk_box_pack_start (GTK_BOX (looper->mainbox),looper->controlframe, FALSE, FALSE, 0);

	looper->loopview = gtk_loopview_new(looper->data);
	gtk_loopview_suspend_gui (GTK_LOOPVIEW(looper->loopview), 1);
	gtk_box_pack_end (GTK_BOX (looper->mainbox),looper->loopview, TRUE, TRUE, 0);
	gtk_widget_show (looper->loopview);

	gtk_widget_show (looper->mainbox);

	/* CONNECT SIGNALS */	

	g_signal_connect (G_OBJECT (looper->playbutton), "toggled",
		      	G_CALLBACK (looper_playbutton_callback), (gpointer) looper);

	g_signal_connect (G_OBJECT (looper->recbutton), "toggled",
                      	G_CALLBACK (looper_recbutton_callback), (gpointer) looper);

	g_signal_connect (G_OBJECT (looper->shuttlebutton), "toggled",
                        G_CALLBACK (looper_shuttlebutton_callback), (gpointer) looper);

	gtk_toggle_button_set_active((GtkToggleButton*)looper->shuttlebutton,TRUE);
/*
	g_signal_connect (G_OBJECT (looper->limiterbutton), "toggled",
                        G_CALLBACK (looper_limiterbutton_callback), (gpointer) looper);
        gtk_toggle_button_set_active((GtkToggleButton*)looper->limiterbutton,TRUE);
*/

	g_signal_connect (G_OBJECT (looper->minimizebutton), "toggled",
                        G_CALLBACK (looper_minimizebutton_callback), (gpointer) looper);

	g_signal_connect (G_OBJECT (looper->upbutton), "clicked",
                        G_CALLBACK (looper_upbutton_callback), (gpointer) looper);
	g_signal_connect (G_OBJECT (looper->downbutton), "clicked",
                        G_CALLBACK (looper_downbutton_callback), (gpointer) looper);
/*
	g_signal_connect (G_OBJECT (looper->normbutton), "clicked",
			G_CALLBACK (looper_normbutton_callback), (gpointer) looper);
*/

	g_signal_connect (G_OBJECT (looper->panslider), "value-changed",
			G_CALLBACK (looper_panslider_callback), (gpointer) looper);
/*
	g_signal_connect (G_OBJECT (looper->panswingspin), "value-changed",
                        G_CALLBACK (looper_panswingspin_callback), (gpointer) looper);
*/

	g_signal_connect (G_OBJECT (looper->ngrainslider), "value-changed",
                        G_CALLBACK (looper_ngrainslider_callback), (gpointer) looper);

/*
	g_signal_connect (G_OBJECT (looper->densityspin), "value-changed",
                        G_CALLBACK (looper_densityspin_callback), (gpointer) looper);

	g_signal_connect (G_OBJECT (looper->grainlenslider), "value-changed",
                        G_CALLBACK (looper_grainlenslider_callback), (gpointer) looper);
*/

	g_signal_connect (G_OBJECT (looper->volslider), "value-changed",
			G_CALLBACK (looper_volslider_callback), (gpointer) looper);

	g_signal_connect (G_OBJECT (looper->recmixslider), "value-changed",
                        G_CALLBACK (looper_recmixslider_callback), (gpointer) looper);

/*	
	g_signal_connect (G_OBJECT (looper->speedspin), "value_changed",
                        G_CALLBACK (looper_speedspin_callback), (gpointer) looper);
*/
/*

	g_signal_connect (G_OBJECT (looper->diversityspin), "value_changed",
			G_CALLBACK (looper_diversity_callback), (gpointer) looper);

	g_signal_connect (G_OBJECT (looper->loopstartspin), "value_changed",
                        G_CALLBACK (looper_loopstartspin_callback), (gpointer) looper);

	g_signal_connect (G_OBJECT (looper->loopendspin), "value_changed",
                        G_CALLBACK (looper_loopendspin_callback), (gpointer) looper);

	g_signal_connect (G_OBJECT (looper->looplengthspin), "value_changed",
                        G_CALLBACK (looper_looplengthspin_callback), (gpointer) looper);

        g_signal_connect (G_OBJECT (looper->recstartspin), "value_changed",
                        G_CALLBACK (looper_recstartspin_callback), (gpointer) looper);

        g_signal_connect (G_OBJECT (looper->recendspin), "value_changed",
                        G_CALLBACK (looper_recendspin_callback), (gpointer) looper);

        g_signal_connect (G_OBJECT (looper->reclengthspin), "value_changed",
                        G_CALLBACK (looper_reclengthspin_callback), (gpointer) looper);
*/

	g_signal_connect (G_OBJECT (looper->loopview), "loopborders-changed",
			G_CALLBACK (loopview_changed_loopborders),(gpointer)looper);

	g_signal_connect (G_OBJECT (looper->loopview), "recborders-changed",
                        G_CALLBACK (loopview_changed_recborders),(gpointer)looper);

	g_signal_connect (G_OBJECT (looper->loopview), "speed-changed",
                        G_CALLBACK (loopview_changed_speed),(gpointer)looper);

	looper_set_minimized (looper, minimized);

	gtk_loopview_suspend_gui (GTK_LOOPVIEW(looper->loopview), 0);

        return looper;
}

