#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <getopt.h>
#include <math.h>
#include <jack/jack.h>
#include <jack/ringbuffer.h>
#include <jack/types.h>
#include "looperdata.h"
#include "jackmixer.h"
#include "speciallist.h"

#define DEFAULT_RB_SIZE 16384
#define MAX_NAMESIZE 255

typedef struct _submix {
	jack_ringbuffer_t *rb_inL;
	jack_ringbuffer_t *rb_inR;
	jack_ringbuffer_t *rb_inP;
	jack_ringbuffer_t *rb_outL;
	jack_ringbuffer_t *rb_outR;
	jack_ringbuffer_t *rb_outP;
	jack_port_t *port_inL;
	jack_port_t *port_inR;
	jack_port_t *port_inP;
	jack_port_t *port_outL;
	jack_port_t *port_outR;
	jack_port_t *port_outP;
	int	needs_input;
	int 	did_output;
	int	did_pointer;
	int	needs_pointer;
	char	*name;
	int	id;
} submix_t;

typedef struct _thread_info{
	volatile size_t		sample_size;
	submix_t 		**submixes;
	speciallist_t 		*looperdatalist;
	volatile int 		nmixes;
	volatile jack_nframes_t rb_size;
	volatile jack_nframes_t last_nframes;
      	volatile int 		can_process;
	volatile int		inside;
	volatile int		outside;
	volatile long		inrounds;
	volatile long		outrounds;
	jack_info_t 		*jack_info;
	pthread_t 		mix_thread_id;
	volatile long 		xruns;
	volatile int		freewheeling;
	pthread_mutex_t 	mix_thread_lock;/* = PTHREAD_MUTEX_INITIALIZER;*/
	pthread_cond_t  	data_ready;/* = PTHREAD_COND_INITIALIZER;*/
} thread_info_t;


/* submix helpers */
submix_t* submix_new (thread_info_t *info, int id, char* name){
	submix_t *submix = (submix_t*) malloc(sizeof(submix_t));
	char *tmp;
	
	submix->rb_inL = jack_ringbuffer_create (info->sample_size * info->rb_size);
	submix->rb_inR = jack_ringbuffer_create (info->sample_size * info->rb_size);
	submix->rb_inP = jack_ringbuffer_create (info->sample_size * info->rb_size);
	submix->rb_outL = jack_ringbuffer_create (info->sample_size * info->rb_size);
	submix->rb_outR = jack_ringbuffer_create (info->sample_size * info->rb_size);
	submix->rb_outP = jack_ringbuffer_create (info->sample_size * info->rb_size);

	memset(submix->rb_inL->buf, 0, submix->rb_inL->size);
	memset(submix->rb_inR->buf, 0, submix->rb_inR->size);
	memset(submix->rb_inP->buf, 0, submix->rb_inP->size);
	memset(submix->rb_outL->buf, 0, submix->rb_outL->size);
	memset(submix->rb_outR->buf, 0, submix->rb_outR->size);
	memset(submix->rb_outP->buf, 0, submix->rb_outP->size);

	/* try mlock */
	jack_ringbuffer_mlock(submix->rb_inL);
	jack_ringbuffer_mlock(submix->rb_inR);
	jack_ringbuffer_mlock(submix->rb_inP);
	jack_ringbuffer_mlock(submix->rb_outL);
	jack_ringbuffer_mlock(submix->rb_outR);
	jack_ringbuffer_mlock(submix->rb_outP);

	submix->needs_input = 0;
	submix->did_output = 0;
	submix->needs_pointer = 0;
	submix->did_pointer = 0;

	tmp = (char*) malloc (MAX_NAMESIZE * sizeof(char));

	if (id > 0){
	/* this is a hack, because we don't need main in L+R */
		info->outside = 100;
		snprintf(tmp,MAX_NAMESIZE,"%s_in_L",name);
		submix->port_inL = jack_port_register (info->jack_info->client, 
					tmp, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
		if (!submix->port_inL) fprintf (stderr,"could not create %s_in_L\n",name);
		snprintf(tmp,MAX_NAMESIZE,"%s_in_R",name);
		submix->port_inR = jack_port_register (info->jack_info->client, 
					tmp, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
		if (!submix->port_inR) fprintf (stderr,"could not create %s_in_R\n",name);
		snprintf(tmp,MAX_NAMESIZE,"%s_in_Position",name);
		submix->port_inP = jack_port_register (info->jack_info->client, 
					tmp, JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
		if (!submix->port_inP) fprintf (stderr,"could not create %s_in_Position\n",name);
		snprintf(tmp,MAX_NAMESIZE,"%s_out_Position",name);
		submix->port_outP = jack_port_register (info->jack_info->client, 
					tmp, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
		if (!submix->port_outP) fprintf (stderr,"could not create %s_out_L\n",name);
		info->outside = 101;

	}
	snprintf(tmp,MAX_NAMESIZE,"%s_out_L",name);
	submix->port_outL = jack_port_register (info->jack_info->client, 
				tmp, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
	if (!submix->port_outL) fprintf (stderr,"could not create %s_out_L\n",name);
	snprintf(tmp,MAX_NAMESIZE,"%s_out_R",name);
	submix->port_outR = jack_port_register (info->jack_info->client, 
				tmp, JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
	if (!submix->port_outR) fprintf (stderr,"could not create %s_out_R\n",name);
	free(tmp);

	submix->id = id;
/*	printf ("registering id:%d\n",id);*/
	submix->name = (char*) malloc(sizeof(char) * (strlen(name)+1));
	strcpy (submix->name,name);

	return submix;
}

void submix_append(thread_info_t* info,submix_t* mix, int id){
	int i;
	int s = info->nmixes;

	if ((id  + 1) > info->nmixes){	
		info->nmixes = id + 1;
		info->submixes = (submix_t**) realloc(info->submixes, sizeof(submix_t*) * info->nmixes);
		for (i = s; i < info->nmixes; i++) info->submixes[i] = NULL;
	}

	*(info->submixes + id) = mix;
}

void submix_remove_by_id(thread_info_t* info,int id){
	submix_t* submix;
	int i;
	int err;

	for (i = 1;i < info->nmixes;i++){
		submix = (submix_t*)(info->submixes[i]);
		if (submix){
			if (submix->id == id){
				err = jack_port_unregister (info->jack_info->client,submix->port_inL);
				if (err) printf ("could not unregister jack port");
				jack_ringbuffer_free(submix->rb_inL);

	       			err = jack_port_unregister (info->jack_info->client,submix->port_inR);
				if (err) printf ("could not unregister jack port");
				jack_ringbuffer_free(submix->rb_inR);

				err = jack_port_unregister (info->jack_info->client,submix->port_inP);
				if (err) printf ("could not unregister jack port");
				jack_ringbuffer_free(submix->rb_inP);

      				err = jack_port_unregister (info->jack_info->client,submix->port_outL);
				if (err) printf ("could not unregister jack port");
				jack_ringbuffer_free(submix->rb_outL);

	     			err = jack_port_unregister (info->jack_info->client,submix->port_outR);
				if (err) printf ("could not unregister jack port");
				jack_ringbuffer_free(submix->rb_outR);

				err = jack_port_unregister (info->jack_info->client,submix->port_outP);
				if (err) printf ("could not unregister jack port");
				jack_ringbuffer_free(submix->rb_outP);
	
				submix->did_output = 0;
				submix->needs_input = 0;	
				free (submix->name);
				free (submix);	
				info->submixes[i] = NULL;	
/*				printf("submix:%d, unregistered\n",id);*/
			}
		}
	}
}

submix_t* search_submix_by_id(thread_info_t* info, int id){
	int i;
	submix_t* submix;

	if (!info->nmixes){return NULL;}

	for (i = 0;i < info->nmixes;i++){
		submix = (submix_t*)(info->submixes[i]);
		if (submix)
			if (submix->id == id){return submix;}
	}
	return NULL;
}

looper_data_t* search_looper_data_by_id(thread_info_t* info, int id){
	looper_data_t *ld = speciallist_get_first_nolock (info->looperdatalist);

	while(ld){
		if (ld->id == id) return ld;
		ld = speciallist_get_next_nolock (info->looperdatalist, ld);
	}
	return NULL;
}

int search_lost_submix(thread_info_t* info){
	submix_t *submix;
	int i;

	for (i = 1;i < info->nmixes;i++){
		submix = (submix_t*)(info->submixes[i]);
/*		printf ("i:%d,id:%d\n",i,submix->id);*/
		if (submix && (search_looper_data_by_id(info, i) == NULL))
			if (submix->did_output || submix->needs_input)
				return submix->id;
	}
	return 0;
}


/* jack administrative functions */

void jack_shutdown (void *arg){
	thread_info_t *info = (thread_info_t*) arg;
        fprintf (stderr, "JACK shutdown - mixer is a zombie (process status: %d,%d)\n",info->inside,info->outside);
/*	fprintf (stderr, "JACK shutdown - mixer is a zombie\n");*/
	info->jack_info->alive = 0;
        /* abort(); */
}

void errfunc(const char *msg){
      	printf ("jack error: %s\n",msg);
}

void freewheelfunc (int starting, void *arg){
        thread_info_t *info = (thread_info_t*) arg;
        info->freewheeling = starting;
}

int xrunfunc (void *arg){
        thread_info_t *info = (thread_info_t*)arg;
	info->outside = 401;
        info->xruns++;
	info->outside = 402;
	printf ("jack xrun\n");
	return 0;
}

void mixer_close(jack_info_t *jack_info){
        if (jack_info)
                if (jack_info->client){
                        jack_client_close(jack_info->client);
                        jack_info->client = 0;
                }
}

/* the work functions */

int process (jack_nframes_t nframes, void *arg){
        thread_info_t *info = (thread_info_t *) arg;
        int i,rs;                       
        jack_ringbuffer_t *rb;          
        jack_port_t *port;              
        jack_default_audio_sample_t *rh;
        
	info->inside = -3;                                        
	/*printf ("processing\n");*/
        if ((i=pthread_mutex_trylock (&info->mix_thread_lock))){
		/*printf ("lock not possible:%d, (EBUSY=%d) \n",i , EBUSY);*/
		pthread_cond_broadcast (&info->data_ready);
        	info->inside = 2;
        	pthread_mutex_unlock (&info->mix_thread_lock);
        	info->inside = 3;

                return 0;       
        }                               
                                
        /* wait for data if freewheeling; else go on */
	info->inside = -2;
        if (info->freewheeling)         
                while (info->can_process != 1); 
        else if (info->can_process == 0){
		/* -1 for free wheeling, 1 for new data, 2 for init*/
/*		printf ("srun in process\n");*/
		info->xruns++;
                return 0;
        }  else if (info->can_process != 1) {
/*		printf ("xrun in process\n");*/
                return 0;
        }
               
	info->inside = -1; 
	info->inrounds++;	
        info->last_nframes = nframes;
	
        if (info->submixes){ 
                for (i = 0; i < info->nmixes;i++){
                        if (info->submixes[i]){
				info->inside = 1;
                                if (info->submixes[i]->did_output){
                                        port = info->submixes[i]->port_outL;
                                        rb = info->submixes[i]->rb_outL;
                                        rh = (jack_default_audio_sample_t *) jack_port_get_buffer (port,nframes);
                                        if ((rs = jack_ringbuffer_read (rb,(void*)rh,info->sample_size * nframes))
                                                < nframes * info->sample_size){ info->xruns++;
/*					printf ("xrun in %d_did_output (outside:%d, rs:%d != %d, rounds:%ld/%ld)\n",
							i,info->outside, rs, (info->sample_size * nframes), 
							info->inrounds, info->outrounds); */
/*                                      printf("%s->outL, %d/%d/%d frames\n",jack_port_name (info->submixes[i]->port_outL),
							nframes,info->last_nframes,rs);*/
					}

                                        port = info->submixes[i]->port_outR;
                                        rb = info->submixes[i]->rb_outR;
                                        rh = (jack_default_audio_sample_t *) jack_port_get_buffer (port,nframes);
                                        if ((rs = jack_ringbuffer_read (rb,(void*)rh,info->sample_size * nframes))
                                                < nframes * info->sample_size){ info->xruns++;}
                                }

                                if (info->submixes[i]->needs_input){
                                        port = info->submixes[i]->port_inL;
                                        rb = info->submixes[i]->rb_inL;
                                        rh = (jack_default_audio_sample_t *) jack_port_get_buffer (port,nframes);
                                        if (jack_ringbuffer_write_space(rb) >= nframes * info->sample_size){
                                                if (jack_ringbuffer_write (rb,(void*)rh,info->sample_size * nframes)
                                                        < nframes * info->sample_size){info->xruns++;}
                                        }else{info->xruns++;}

                                        port = info->submixes[i]->port_inR;
                                        rb = info->submixes[i]->rb_inR;
                                        rh = (jack_default_audio_sample_t *) jack_port_get_buffer (port,nframes);
                                        if (jack_ringbuffer_write_space(rb) >= nframes * info->sample_size){
                                                if (jack_ringbuffer_write (rb,(void*)rh,info->sample_size * nframes)
                                                        < nframes * info->sample_size){info->xruns++;}
                                        }else{info->xruns++;}
                                }

                                if (info->submixes[i]->did_pointer){
                                        port = info->submixes[i]->port_outP;
                                        rb = info->submixes[i]->rb_outP;
                                        rh = (jack_default_audio_sample_t *) jack_port_get_buffer (port,nframes);
                                        if ((rs = jack_ringbuffer_read (rb,(void*)rh,info->sample_size * nframes))
                                                < nframes * info->sample_size){ info->xruns++;}
                                }
                                if (info->submixes[i]->needs_pointer){
                                        port = info->submixes[i]->port_inP;
                                        rb = info->submixes[i]->rb_inP;
                                        rh = (jack_default_audio_sample_t *) jack_port_get_buffer (port,nframes);
                                        if (jack_ringbuffer_write_space(rb) >= nframes * info->sample_size){
                                                if (jack_ringbuffer_write (rb,(void*)rh,info->sample_size * nframes)
                                                        < nframes * info->sample_size){info->xruns++;}
                                        }else{info->xruns++;}
                                }
				info->inside = 0;
                        }
                }
        }
	/*printf ("processing done\n");*/
	/*pthread_cond_signal (&info->data_ready);*/
	pthread_cond_broadcast (&info->data_ready);
	info->inside = 2;
       	pthread_mutex_unlock (&info->mix_thread_lock);
	info->inside = 3;
        return 0;
}

static void* mix_thread(void *arg){
	thread_info_t *info = (thread_info_t*)arg;
	struct sched_param rtparam;
	jack_nframes_t i,j = 0;
	looper_data_t* data = NULL;
	looper_data_t* data2 = NULL; 
	submix_t* submix;
	char* name = NULL;
	int noutports = 0;
	float mixfactor = .0;
	const char **ports;
/*	float L,R,P;*/
	float *Lbuf = NULL;
	float *Rbuf = NULL;
	float *Pbuf = NULL;
	double oldrecpos = 0.0;
	double newrecpos = 0.0;
	float* mainbufL = (float*) malloc(sizeof(float) * DEFAULT_RB_SIZE);
	float* mainbufR = (float*) malloc(sizeof(float) * DEFAULT_RB_SIZE);
	int cc = 0;
	char *clientname = malloc(jack_client_name_size() * sizeof(char));
	jack_status_t status;
	int retval;

        jack_set_error_function ((void*)errfunc);

        while (!info->jack_info->client && (cc < 99)){
		memset(clientname,0,jack_client_name_size());
                sprintf(clientname,"kluppe%d",++cc);
/*                info->jack_info->client = jack_client_new (clientname);*/
		info->jack_info->client = jack_client_open (clientname, JackUseExactName,&status);
        }


        if (cc > 98){
		fprintf (stderr, "jack_client_open() failed, status = 0x%2.0x\n", status);
                fprintf (stderr, "jack server not running?\n");
                info->jack_info->name = malloc(sizeof(char) * 10);
                strncpy (info->jack_info->name,"nojack!",7);
                return info->jack_info;
        }

        printf ("jack client name: %s\n",clientname);

        info->jack_info->name = calloc(sizeof(char) , (strlen(clientname) + 1));
        strncpy (info->jack_info->name,clientname,strlen(clientname)) ;


        /* launch jack process thread and activate */
	/* bad status for client event handling (type = 8)   ??? */
        jack_set_process_callback (info->jack_info->client, process, info);
        jack_set_freewheel_callback(info->jack_info->client, freewheelfunc, info);
        jack_set_xrun_callback(info->jack_info->client, xrunfunc, info);
        jack_on_shutdown (info->jack_info->client, jack_shutdown, info);

	info->jack_info->samplerate = (long)jack_get_sample_rate(info->jack_info->client);
        info->can_process = 2;

	/* try to reach higher priority */
	memset (&rtparam, 0, sizeof (rtparam));
        rtparam.sched_priority = /*sched_get_priority_max(SCHED_RR) - */10;
	i = pthread_setschedparam(pthread_self(),SCHED_RR,&rtparam);
	/* if (i) printf ("couldn't set priority\n");*/

        pthread_setcancelstate (PTHREAD_CANCEL_ENABLE, NULL);
        pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

	if ((retval=pthread_mutex_lock (&info->mix_thread_lock))){
		printf ("mutex_lock failed!:%d\n",retval);
	}
	/*printf ("mixer thread:%d\n",(int)getpid());*/

	while (1){
/*		if (info->can_process == 2) printf ("status 2\n");*/
		if ((info->can_process == 1) || (info->can_process == -1)){
			info->can_process = 0;
			info->jack_info->load = jack_cpu_load (info->jack_info->client);
			info->jack_info->freewheeling = info->freewheeling;
			info->outside = 300;

			if (!info->submixes){
				info->outside = 301;
				/* no main in/out yet: */
				submix_append (info,submix_new (info,0,"main"),0);
/*				printf ("main outputs created\n");*/
				submix = search_submix_by_id(info,0);
				
				if ((ports = jack_get_ports (
					info->jack_info->client, NULL, NULL, JackPortIsPhysical|JackPortIsInput)) == NULL) {
					fprintf(stderr, "Cannot find any physical playback ports!\n");
				}else{
					info->outside = 302;

					if (jack_connect (info->jack_info->client, jack_port_name (submix->port_outL),ports[0])){
						fprintf (stderr, "cannot connect main port\n");
					}
					if (jack_connect (info->jack_info->client, jack_port_name (submix->port_outR),ports[1])){
						fprintf (stderr, "cannot connect main ports\n");
					}
		
					info->submixes[0]->did_output = 1; /* always play on main out */
	
					free (ports);
					info->outside = 303;
				}
			}

/* xruns even if all the processing is switched off:
#if 0
*/
			info->outside = 304;

			memset (mainbufL,0,info->rb_size/*last_nframes*/ * info->sample_size);
			memset (mainbufR,0,info->rb_size/*last_nframes*/ * info->sample_size);

			info->outside = 305;
	
			noutports = 0;
			while (search_lost_submix(info))
				submix_remove_by_id(info,search_lost_submix(info));

			speciallist_lock(info->looperdatalist);
			data = speciallist_get_first_nolock(info->looperdatalist);
			while (data){
				submix = search_submix_by_id(info,data->id);
				info->outside = 306;
				if (!submix){
					/* new looper: setup submix... */
					name = (char*) malloc (MAX_NAMESIZE * sizeof(char));
					sprintf (name,"act%d",data->id);
					submix = submix_new (info,data->id,name);
					/* connecting takes a while and can cause xruns */
					/* LATER: move connecting to gui thread / looper.c */
					printf ("connecting...\n");
					if ((ports = jack_get_ports (info->jack_info->client, NULL, NULL, 
						JackPortIsPhysical|JackPortIsOutput))){
						if (jack_connect (info->jack_info->client,ports[0], 
									jack_port_name (submix->port_inL))){
							fprintf (stderr, "cannot connect looper input port\n");
						}
						if (jack_connect (info->jack_info->client,ports[1], 
									jack_port_name (submix->port_inR))){
							fprintf (stderr, "cannot connect looper input port\n");
						}
						free (ports);
					}else{
						printf ("can't find physical input ports for looper\n");
					}
					submix_append (info,submix,data->id);
                                       	free(name);
                                       	info->outside = 307;
				}
				info->outside = 309;
				
				if (data->buf && data->id){
					if (data->buf->status == BUFFER_STATUS_READY){
						info->outside = 310;
						if (jack_port_connected(submix->port_outP))
							submix->did_pointer = 1;
						else 	submix->did_pointer = 0;
	
						if (data->isrecording && (data->recend > data->recstart))
							submix->needs_input = 1;
						else 	submix->needs_input = 0;

						if ((data->playmode == LOOP_PLAYMODE_EXTERNALPOS) && 
								jack_port_connected(submix->port_inP))
							submix->needs_pointer = 1;
						else	submix->needs_pointer = 0;
	
						/* LATER calculate the number of samples from 
		   				jacks buffer_size_callback */
						/* for now, it's just the last number of samples
		   				from process() */
						i = j;
						j = info->last_nframes;
						if (jack_ringbuffer_read_space(submix->rb_inL) > j * info->sample_size){
							j = jack_ringbuffer_read_space(submix->rb_inL) / info->sample_size;
						}
						if (i != j){ /* check buffer sizes */
/*							printf ("n_frames change: %ld -> %ld\n",(long)i, (long)j);*/
							if (Lbuf){
								Lbuf = realloc (Lbuf, info->sample_size * j);
								Rbuf = realloc (Rbuf, info->sample_size * j);
								Pbuf = realloc (Pbuf, info->sample_size * j);
							} else {
								Lbuf = calloc (info->sample_size, j);
								Rbuf = calloc (info->sample_size, j);
								Pbuf = calloc (info->sample_size, j);
							}
						}

						oldrecpos = looperdata_get_recpos(data);
                                               	looperdata_lock(data);
                                               	info->outside = 311;

						if (submix->needs_input){
							jack_ringbuffer_read (submix->rb_inL,
								(void *) Lbuf, info->sample_size * j);
							jack_ringbuffer_read (submix->rb_inR,
                                                               	(void *) Rbuf, info->sample_size * j);
/*							looperdata_write_sampleblock(data, j, Lbuf, Rbuf);*/
						}							
						if (submix->needs_pointer){
							jack_ringbuffer_read (submix->rb_inP,
								(void *) Pbuf, info->sample_size * j);
						}
					
						submix->did_output = 0;	

						looperdata_calc_sampleblock(data, j, Lbuf, Rbuf, Pbuf,
							!info->jack_info->suspend_playing, 
							!info->jack_info->suspend_recording);

						if (data->isplaying){
							looperdata_unlock (data);	
							if (submix->did_pointer)
                                                       		jack_ringbuffer_write (submix->rb_outP,
                                                               		(void *)Pbuf, info->sample_size * j);
							noutports++;
							if (!info->jack_info->suspend_playing)
								for (i = 0; i < j;i++){
                                                               		*(mainbufL + i) += *(Lbuf + i);
                                                               		*(mainbufR + i) += *(Rbuf + i);         
                                                       		}
							if ((submix->port_outL) && (submix->port_outR)){
								if (jack_port_connected(submix->port_outL) ||
                                                       			jack_port_connected(submix->port_outR)){	
									jack_ringbuffer_write (submix->rb_outL,
										(void *)Lbuf, info->sample_size * j);
									jack_ringbuffer_write (submix->rb_outR,
                                                               			(void *)Rbuf, info->sample_size * j);
									submix->did_output = 1;
								}
							}
						} else looperdata_unlock (data);

						info->outside = 312;
/*						info->last_nframes = j;*/

						if ((oldrecpos != looperdata_get_recpos(data)) || data->buf->normalized){
							info->outside = 313;
							/* update dirty_blocks_list for all affected loopers */
							data2 = speciallist_get_first_nolock(info->looperdatalist);
							while (data2){
								if (data2->buf == data->buf){
									if (data->buf->normalized){
										looperdata_lock(data2);
										looperdata_write_dirtyblock(
											data2,0.,(double)(
												buffer_get_size(data2->buf)));
										looperdata_unlock(data2);
									}else{
										newrecpos = looperdata_get_recpos(data);
										looperdata_lock(data2);
										if (newrecpos < oldrecpos){
											looperdata_write_dirtyblock(data2, 
												oldrecpos,
												looperdata_get_recend(data));
											if (newrecpos > looperdata_get_recstart(data)){
												looperdata_write_dirtyblock(data2,
													looperdata_get_recstart(data),newrecpos);
											}	
										}else{
											looperdata_write_dirtyblock(data2, oldrecpos, newrecpos);
										}
										looperdata_unlock (data2);
									}
								}
								data2 = speciallist_get_next_nolock(info->looperdatalist, data2);
							}
							if (data->buf->normalized){
								looperdata_lock(data);
								data->buf->normalized = 0;
								looperdata_unlock(data);
							}
						}
						info->outside = 313;
					}
				}
				data = speciallist_get_next_nolock(info->looperdatalist, data);
			}
			speciallist_unlock(info->looperdatalist);
/*			printf ("unlock:%d\n",count);*/
			/* LATER delete unused submixes */
			info->outside = 314;
			if ((noutports > 1) && (!info->jack_info->suspend_playing)){
				/* normalize main out signal */
				mixfactor = 1.0f / sqrt(noutports);
				for (i = 0; i < j/*info->last_nframes*/; i++){
					*(mainbufL + i ) *= mixfactor;
					*(mainbufR + i ) *= mixfactor;
				}
			}
			info->outside = 315;

			/* warn if there were xruns */
			if (info->xruns != info->jack_info->xruns){
				info->jack_info->xruns = info->xruns;
/*				info->xruns = 0;*/
			}
/* end of xrun test 
#endif
*/
			info->outside = 316;
			/* write main out data */
/*			printf ("write:%d bytes\n",info->last_nframes * info->sample_size);*/
			jack_ringbuffer_write (info->submixes[0]->rb_outL,(void *)mainbufL,info->last_nframes * info->sample_size);
		       	jack_ringbuffer_write (info->submixes[0]->rb_outR,(void *)mainbufR,info->last_nframes * info->sample_size);	
			info->outside = 317;
			info->can_process = 1;
		}/* end if can_process == 1 or -1*/
		/* wait until process() signals more data */
		++info->outrounds;
		info->outside = 318;
		/*printf ("trylock\n");
		pthread_mutex_lock(&info->mix_thread_lock);*/
		/*printf ("waiting\n");*/
		pthread_cond_wait (&info->data_ready, &info->mix_thread_lock);
		/*printf ("waiting ended\n");*/
		info->outside = 319;
	}/* end while always*/

	/* this will never happen */	
	pthread_mutex_unlock (&info->mix_thread_lock);

	return NULL;
}

jack_info_t* mixer_launch (speciallist_t *looperdatalist){
	thread_info_t *info;
	long waited = 0;
	int retval;

	/* set up communication data */
	info = (thread_info_t*) malloc(sizeof(thread_info_t));
	memset (info, 0, sizeof (thread_info_t));

	info->looperdatalist = looperdatalist;
	info->can_process = 0;
	info->submixes = NULL;
	info->nmixes = 0;
	info->last_nframes = 0;
	info->freewheeling = 0;
	info->inside = 0;
	info->outside = 0;
	info->inrounds = 0;
	info->outrounds = 0;
	info->sample_size = sizeof(jack_default_audio_sample_t);
	info->jack_info = (jack_info_t*) malloc(sizeof(jack_info_t));
        info->jack_info->client = 0;
        info->jack_info->samplerate = 0;
        info->jack_info->load = 0.0;
        info->jack_info->xruns = 0;
        info->jack_info->name = NULL;
        info->jack_info->freewheeling = 0;
	info->jack_info->suspend_playing = 0;
	 info->jack_info->suspend_recording = 0;
	info->jack_info->alive = 0;


/* 	LATER handle buffersize changes while processing */
/*	info->rb_size = jack_get_buffer_size (info->jack_info->client) + info->sample_size;	*/
	info->rb_size = DEFAULT_RB_SIZE;

	/* launch mix thread */
	if ((retval=pthread_cond_init(&info->data_ready, NULL))){
		printf ("cond_init failed:%d\n",retval);
	}
	if((retval=pthread_mutex_init(&info->mix_thread_lock,NULL))){
		printf ("mutex_init failed:%d\n",retval);
	}
	pthread_create (&info->mix_thread_id, NULL, mix_thread, info);

	while (!info->can_process && (++waited < 50000)){ 
		usleep(10);
	}

	if (info->can_process < 1) return info->jack_info;

	if (jack_activate (info->jack_info->client)) {
                fprintf (stderr, "cannot activate client\n");
        }else{
		info->can_process = 1;
		info->jack_info->alive = 1;
	}
	/* printf ("can process:%d, returning\n",info->can_process);*/ 
	return info->jack_info;
}
