#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
/*#include <gdk/gdk.h>*/
#include "looperdata.h"
#include "interpol.h"

const float PI = 3.14159265358979323846f;
const float S2M_FACTOR = 0.7071;

typedef struct _dirty_block {
	double start;
	double end;
} dirty_block_t;

/* predefine:*/
static void looperdata_reset_grain (looper_data_t* data, int grain);
static void inform_buffer(looper_data_t* data);

looper_data_t* looperdata_new(buffer_info_t* buf, long samplerate,int id){
	int i;
	looper_data_t *data = (looper_data_t*) malloc (sizeof(looper_data_t));

	data->buf = buf;
	data->looper_id = -1;
	data->clickmode_buf = NULL;
	data->reverb = buffer_new(id,samplerate);
	buffer_set_channels(data->reverb,2);
	data->reverblength = 0;
	data->feedback = 0.;
	data->revpos = 0;
	data->id = id;
	data->buffer_group = 0;
	data->samplerate = samplerate;
	data->speed = 1.;
	data->modespeed = 1.;
	data->vol = 1.;
	data->pan = 0.;
	data->panswing = 0.0;
	data->panpos = 0.;
	data->pandir = 1.;
	data->Lfact = 1.;
	data->Rfact = 1.;
	data->inmaxL = .0;
	data->inmaxR = .0;
	data->outmaxL = .0;
	data->outmaxR = .0;
	data->recmix = 1.;
	data->recmixnew = 1.;
	data->recmixorig = 0.;
	data->playpos = 0.;
	data->playindex = 0.;
	data->pointer = 0.;
	data->loopstart = 0;
	data->loopend = 0;
	data->recstart = 0;
	data->recend = 0;
	if (data->buf){
/*		printf ("looperdata, get group\n");*/
		data->buffer_group = buffer_get_group(data->buf,DS_PRIORITY_NORMAL);
		data->loopend = buffer_get_size(data->buf);
		data->recend = buffer_get_size(data->buf);
	}
	data->recpos = 0;
	data->isplaying = 0;
	data->isrecording = 0;
	data->linkrec2play = 1;
	data->keepabsolute = 0;
	data->limiter = 1;
	data->limit = 1.;
	data->limknee = .5;
	data->limconst = .25;
	data->thresholdstate = THRESHOLDSTATE_ANY;
	data->low_threshold = .01;
	data->high_threshold = .015;
	data->thresholdmode = 0;
	data->threshringptr = 0;
	data->threshringsize = 22050; /* half a second at regular speed should be a good start */
	data->threshring = malloc (sizeof(float) * data->threshringsize);
	data->playvuL = .0;
	data->playvuR = .0;
	data->recvuL = .0;
	data->recvuR = .0;
	data->playmode = LOOP_PLAYMODE_LOOP;
	data->recmode = LOOP_RECMODE_LOOP;
	data->cm_size = 1024;
	data->cm_min = 0.001;
	data->cm_max = 5.;
	data->customplaymode 
		= malloc (data->cm_size * sizeof(float));

	for (i=0;i < data->cm_size; i++){
		*(data->customplaymode + i) = 1.;
	}

	data->midichannel = 0; 
	data->ngrains = 0;
	data->grainmixfactor = 1.;
	data->mingrainspeed = 1.;
	data->maxgrainspeed = 1.;
	data->mingrainlength = 1024;
	data->maxgrainlength = 1024;
	data->grainpitchmode = GRAINPITCH_AUTO;
	data->grainpitchbend = 1.;
	data->graindensity = 100;
	data->stereodiversity = 0.;
	data->grainindexes = NULL;
	data->grainoffsets = NULL;
	data->grainsizes = NULL;
	data->grainfadelengths = NULL;
	data->grainspeeds = NULL;
	data->grainsactive = NULL;
	data->grainpitchtable = NULL;
	data->grainpitchvolumes = NULL;
	data->grainpansL = NULL;
	data->grainpansR = NULL;

	data->dirtylist = speciallist_new();
	pthread_mutex_init (&data->looperdatamutex, NULL);
	return data;
}

void looperdata_free(looper_data_t* data){
	/* LATER free dirty block list */

	speciallist_destroy(data->dirtylist);
	buffer_delete(data->reverb);
	if (data->buf){
		buffer_lock(data->buf);
		buffer_delete_group(data->buf, data->buffer_group);
		buffer_unlock(data->buf);
	}
	free (data->customplaymode);
	free (data->grainindexes);
	free (data->grainoffsets);
	free (data->grainsizes);
	free (data->grainfadelengths);
	free (data->grainspeeds);
	free (data->grainsactive);
	free (data->grainpitchtable);
	free (data->grainpitchvolumes);
	free (data->grainpansL);
	free (data->grainpansR);

	free (data);
	data = NULL;
}

void looperdata_lock(looper_data_t* data){
	pthread_mutex_lock (&data->looperdatamutex);
}

void looperdata_unlock(looper_data_t* data){
	pthread_mutex_unlock (&data->looperdatamutex);
}

void looperdata_copy_settings (looper_data_t* dst, looper_data_t* src){
	int i;

	if (dst && src){
/* ???		dst->samplerate = src->samplerate; */
		dst->speed = src->speed;
		dst->playmode = src->playmode;
		dst->modespeed = src->modespeed;
		dst->vol = src->vol;
		dst->pan = src->pan;
		dst->panswing = src->panswing;
		dst->panpos = src->panpos;
		dst->pandir = src->pandir;
		dst->Lfact = src->Lfact;
		dst->Rfact = src->Rfact;
		dst->inmaxL = src->inmaxL;
		dst->inmaxR = src->inmaxR;
		dst->outmaxL = src->outmaxL;
		dst->outmaxR = src->outmaxR;
		dst->recmix = src->recmix;
		dst->recmixnew = src->recmixnew;
		dst->recmixorig = src->recmixorig;
		dst->linkrec2play = src->linkrec2play;
		dst->keepabsolute = src->keepabsolute;
		dst->limiter = src->limiter;
		dst->limit = src->limit;
		dst->limknee = src->limknee;
		dst->limconst = src->limconst;
		dst->midichannel = src->midichannel;
		dst->graindensity = src->graindensity;
		dst->grainpitchmode = src->grainpitchmode;
		dst->stereodiversity = src->stereodiversity;
		if (dst->buf && src->buf){
			dst->isplaying = src->isplaying;
			dst->isrecording = src->isrecording;
			dst->playpos = src->playpos;
			dst->playindex = src->playindex;
			if (dst->playpos > buffer_get_size(dst->buf)){
				dst->playpos = buffer_get_size(dst->buf);
			}
			if (dst->playindex > buffer_get_size(dst->buf)){
				dst->playindex = buffer_get_size(dst->buf);
			}
			dst->recpos = src->recpos;
			dst->pointer = src->pointer;
			if (dst->recpos > buffer_get_size(dst->buf)){
				dst->recpos = buffer_get_size(dst->buf);
			}
			dst->loopstart = src->loopstart;
			if (dst->loopstart > buffer_get_size(dst->buf)){
				dst->loopstart = 0; 
				/* LATER find a more intelligent solution */
			}
			dst->loopend = src->loopend;	
			if (dst->loopend > buffer_get_size(dst->buf)){
				dst->loopend = buffer_get_size(dst->buf);
			}
			dst->recstart = src->recstart;
			if (dst->recstart > buffer_get_size(dst->buf)){
				dst->recstart = 0;
				/* LATER find a more intelligent solution */
			}
			dst->recend = src->recend;
			if (dst->recend > buffer_get_size(dst->buf)){
				dst->recend = buffer_get_size(dst->buf);
			}
			inform_buffer(dst);
		}	

		if (dst->cm_size != src->cm_size){
			dst->customplaymode = realloc(dst->customplaymode,
				src->cm_size * sizeof(float));
			dst->cm_size = src->cm_size;
		}
		for (i = 0; i < dst->cm_size; i++){
			*(dst->customplaymode + i) = *(src->customplaymode + i);
		}
		dst->mingrainlength = src->mingrainlength;
		dst->maxgrainlength = src->maxgrainlength;
		dst->mingrainspeed = src->mingrainspeed;
		dst->maxgrainspeed = src->maxgrainspeed;
		looperdata_set_ngrains(dst,src->ngrains);

/*		printf ("size:%ld\n",buffer_get_size(src->reverb));*/
		buffer_resize(dst->reverb,buffer_get_size(src->reverb));
		dst->reverblength = src->reverblength;
		dst->feedback = src->feedback;

		for (i = 0; i < src->ngrains; i++){
			looperdata_reset_grain(dst,i);	
			dst->grainpitchtable[i] = src->grainpitchtable[i];
			dst->grainpitchvolumes[i] = src->grainpitchvolumes[i];
			dst->grainpansL[i] = src->grainpansL[i];
			dst->grainpansR[i] = src->grainpansR[i];
		}
	}
}

char* looperdata_cm2str (looper_data_t* data, char* str){
	int i;
	long s = 0;
	char *num = malloc(sizeof(char) * 32);

	if (str){
		free(str);
		str = NULL;
	}

	for (i = 0; i < data->cm_size; i++){
		sprintf (num,"%f ",*(data->customplaymode + i));
		s += strlen(num);
/*		printf ("s:%ld,num:%s-\n",(s + 1),num);*/
		str = (char*)realloc (str,sizeof(char) * (s + 1));
		memset((void*)(str + s  - strlen(num)),0,strlen(num));
		str = strcat(str, num);
	}
	free(num);
	return str;
}

void looperdata_str2cm (looper_data_t* data, char* str, int cm_size){
	char *a = str;
	char *b;
	char *num = malloc(sizeof(char) * 32);
	int i;

	data->cm_size = cm_size;	
	data->customplaymode = realloc(data->customplaymode,
				data->cm_size * sizeof(float));

	for (i = 0;i < data->cm_size; i++){
		b = strstr((char*)(a + 1)," ");
		if (b){
			memset((void*)num,0,31);
			num = strncpy (num,(a),(b-a));
			*(data->customplaymode + i) = (float)atof (num);
			a = b;
		}
	}
}

void looperdata_set_clickmode_buf(looper_data_t* data, buffer_info_t* buf){
	if (data) data->clickmode_buf = buf;
}

buffer_info_t* looperdata_get_clickmode_buf(looper_data_t* data){
	if (data) return data->clickmode_buf;
	else return NULL;
}

void looperdata_write_dirtyblock(looper_data_t* data, double start, double end){
	/* return number of blocks including this one */
	dirty_block_t* block;
	int overlap = 0;

	block = speciallist_get_first(data->dirtylist);

	while (block){
/*
		if (((block->start <= start) && (block->end >= start)) ||
			((block->start <= end) && (block->end >= end)) ){
*/
		if (((block->start <= (start + 1.)) && (block->end >= start)) ||
                        ((block->start <= end) && (block->end >= (end - 1.))) ){
				if (block->start > start) block->start = start;
				if (block->end < end) block->end = end;
				overlap = 1;
				break;
		}
		block = speciallist_get_next(data->dirtylist,(void*)block);
	}

	if (!overlap){	
		block = malloc (sizeof(dirty_block_t));
		block->start = start;
		block->end = end;
		speciallist_append(data->dirtylist,(void*)block);
	}
}

int looperdata_read_once_dirtyblock(looper_data_t* data, double* start, double* end){
	/* return 1 if a block exists otherwise 0 */
	/* set start and  end */
	/* delete after read */
	dirty_block_t* block;
	
	block = speciallist_get_first(data->dirtylist);
	
	if (block){
		*start = block->start;
		*end = block->end;
		looperdata_lock(data);
		speciallist_delete(data->dirtylist, (void*)block);
		looperdata_unlock(data);
		/* LATER: is this right? */
		free(block); 
		return 1;
	}		

	*start = 0.0;
	*end = 0.0;
	return 0;
}

void looperdata_set_playing(looper_data_t* data, int isplaying){
	if (data) data->isplaying = isplaying;
}
void looperdata_set_recording(looper_data_t* data, int isrecording){
	if (!data) return;
	if (buffer_get_type(data->buf) != BUFFER_TYPE_DISCSTREAM)
		data->isrecording = isrecording;
}

int looperdata_get_playing(looper_data_t* data){
	if (data) return data->isplaying;
	else return 0;
}
int looperdata_get_recording(looper_data_t* data){
	if (data) return data->isrecording;
	else return 0;
}


void looperdata_calc_pan (looper_data_t* data){
	if (data->panswing > 0.){
		data->panpos += data->pandir 
			* data->panswing  * 2 / data->samplerate;
			
		if (data->panpos > 1.){
			data->panpos = 1.;
			data->pandir = -1.;
		}else if (data->panpos < -1.){
			data->panpos = -1.;
			data->pandir = 1.;
		}

		if (data->panpos > .0){
			data->Lfact = 1. - data->panpos;
			data->Rfact = 1.;
		}else{
			data->Lfact = 1.;
			data->Rfact = 1. + data->panpos;
		}

		if (data->pan > .0){
			
		}
	}else{
		if (data->pan > .0){
			data->Lfact = 1. - data->pan;
			data->Rfact = 1.;
		}else{
			data->Lfact = 1.;
			data->Rfact = 1. + data->pan;
		}
	}
	data->Lfact *= data->vol;
	data->Rfact *= data->vol;
}

void looperdata_set_pan(looper_data_t* data, float pan){
	if (pan < -1.) pan = -1.;
	if (pan > 1.)  pan = 1.;
	data->pan = pan;
	looperdata_calc_pan(data);
}
	
void looperdata_set_panswing(looper_data_t* data, float panswing){
	data->panswing = panswing;
	looperdata_calc_pan(data);
}     

void looperdata_set_vol(looper_data_t* data, float vol){
	if (vol < .0){vol = 0.;}
	data->vol = vol;

/*	if (data->buf) printf ("data:%s, vol:%f\n",data->buf->shortname, vol);*/
	/* recalculate Lfact + Rfact */
	looperdata_calc_pan(data);
}

float looperdata_get_vol(looper_data_t* data){
	return data->vol;
}

void looperdata_set_recmix(looper_data_t* data, float recmix){
/*	if (recmix < -1.){recmix = -1.;}
	if (recmix > 1.){recmix = 1.;}*/
	data->recmix = recmix;

	if (data->recmix > 1.){
		data->recmixorig = 0.;
		data->recmixnew = data->recmix;
	}else if (data->recmix < -1.){
		data->recmixorig = data->recmix;
		data->recmixnew = 0.;
	}else if (data->recmix > .0){
		data->recmixorig = 1. - data->recmix;
		data->recmixnew = 1.;
	}else{
		data->recmixorig = 1.;
		data->recmixnew = 1. + data->recmix;
	}
/*	printf ("recmixnew:%.2f, recmixorig:%.2f\n",data->recmixnew,data->recmixorig);*/
}	

float looperdata_get_recmix(looper_data_t* data){
	return data->recmix;
}

int looperdata_get_midichannel (looper_data_t* data){
	/* muse and my keyboard send 0 when they say 1 ?!? */
	return (data->midichannel - 1);
}

void looperdata_set_midichannel (looper_data_t* data, int channel){
	if ((channel > -1) && (channel < 16))
		data->midichannel = channel + 1;
	else	data->midichannel = -1;
}

void looperdata_set_mingrainspeed(looper_data_t* data,double speed){
	if (data){
		data->mingrainspeed = speed;
	}
	inform_buffer(data);
}

void looperdata_set_maxgrainspeed(looper_data_t* data,double speed){
	if (data){
		data->maxgrainspeed = speed;
	}
	inform_buffer(data);
}

void looperdata_set_mingrainlength(looper_data_t* data,long length){
/*	if ((data) && (length > 10)){*/
	if (data && (length > 1)){
		data->mingrainlength = length;
		if (data->maxgrainlength < data->mingrainlength)
			data->maxgrainlength = data->mingrainlength;
	}
	inform_buffer(data);
}

void looperdata_set_maxgrainlength(looper_data_t* data,long length){
	if (data && (length > 1)){
		data->maxgrainlength = length;
		if (data->maxgrainlength < data->mingrainlength)
			data->mingrainlength = data->maxgrainlength;
	}
	inform_buffer(data);
}

void looperdata_set_graindensity(looper_data_t* data,int density){
	if (data) data->graindensity = density;
}

void looperdata_set_stereodiversity(looper_data_t* data, double diversity){
	if (diversity < 0.) diversity = 0.;
	if (diversity > 1.) diversity = 1.;
	if (data) data->stereodiversity = diversity;
}

double looperdata_get_stereodiversity(looper_data_t* data){
	if (data) return data->stereodiversity;
	else return 0.;
}



double looperdata_get_mingrainspeed(looper_data_t* data){
	if (data){
		return data->mingrainspeed;
	}else{
		return 0.;
	}
}

double looperdata_get_maxgrainspeed(looper_data_t* data){
	if (data){
		return data->maxgrainspeed;
	}else{
		return 0.;
	}
}

long looperdata_get_mingrainlength(looper_data_t* data){
	if (data){
		return data->mingrainlength;
	}else{
		return 0;
	}
}

long looperdata_get_maxgrainlength(looper_data_t* data){
	if (data){
		return data->maxgrainlength;
	}else{
		return 0;
	}
}

int looperdata_get_graindensity(looper_data_t* data){
	if (data)  return data->graindensity;
	else return 0;
}

int looperdata_get_cm_size(looper_data_t* data){
	return data->cm_size;
}

void looperdata_get_playmeters(looper_data_t* data,float* L,float* R){
	*L = data->outmaxL;
	*R = data->outmaxR;	
	data->outmaxL = 0.;
	data->outmaxR = 0.;
}

void looperdata_get_recmeters(looper_data_t* data,float* L,float* R){
	*L = data->inmaxL;
	*R = data->inmaxR;
	data->inmaxL = 0.;
	data->inmaxR = 0.;
}

static void looperdata_reset_grain (looper_data_t* data, int grain){
	int i,a = 0;
	double d = 1.;

	*(data->grainindexes + grain) = 0;

	if (data->ngrains > 1)
		*(data->grainoffsets + grain) = data->playpos + 
			rand()%(data->maxgrainlength / 15 * (data->ngrains + 1) + 1);
	else *(data->grainoffsets + grain) = data->playpos;
	*(data->grainsizes  + grain) = data->mingrainlength + 
		rand()%(data->maxgrainlength - data->mingrainlength + 1);

	*(data->grainfadelengths + grain) = *(data->grainsizes + grain) / 10 + 2;

	/* random stereo factor: */
	if (data->stereodiversity){
		d = data->stereodiversity * rand()/(RAND_MAX+1.0);
		if ((int)rand()%(2)) d*= -1.;
		if (d > .0){
			*(data->grainpansL + grain) = 1. - d;
			*(data->grainpansR + grain) = 1.;
		}else{
			*(data->grainpansL + grain) = 1.;
			*(data->grainpansR + grain) = 1. + d;
		}
	} else{
		*(data->grainpansL + grain) = 1.;
		*(data->grainpansR + grain) = 1.;
	}

	/* is it correct to multiply with speeds ? */
	if ((*(data->grainfadelengths + grain) * *(data->grainspeeds + grain)) > 400) 
						*(data->grainfadelengths + grain) = 400;
	if (*(data->grainfadelengths + grain) < 5) *(data->grainfadelengths + grain) = 5;

	/* pitch according to pitchmode: */
	if (data->grainpitchmode == GRAINPITCH_CHORD){
		if (*(data->grainpitchtable + grain) != 0.){
			/* the next line to include the min-max range correctly: */
			*(data->grainspeeds + grain) = data->mingrainspeed +  
				(double)(rand()%100001) / 100000. * (data->maxgrainspeed - data->mingrainspeed);
			*(data->grainspeeds + grain) *= *(data->grainpitchtable + grain);

			if (data->grainpitchbend) 
				*(data->grainspeeds + grain) *= data->grainpitchbend;
			*(data->grainsactive + grain) = 1;
/*			printf ("active grain %d\n",grain);*/
		}else{
/*			printf ("resetting grain %d\n",grain);*/
			*(data->grainspeeds  + grain) = 1;
			*(data->grainsactive + grain) = 0;	
			*(data->grainpitchvolumes + grain) = 0.0;
		}
	} else {
		/* GRAINPITCH_AUTO and GRAINPITCH_RANGE are based on the predefined pitch range */
		*(data->grainspeeds + grain) = data->mingrainspeed + 
			(double)(rand()%100001) / 100000. * (data->maxgrainspeed - data->mingrainspeed);

		if (data->grainpitchmode == GRAINPITCH_AUTO){
			if (*(data->grainpitchtable + grain) != 0.){
				*(data->grainspeeds + grain) *= *(data->grainpitchtable + grain);
				if (data->grainpitchbend) 
					*(data->grainspeeds + grain) *= data->grainpitchbend;
/*
				*(data->grainsactive + grain) = 1;
*/
				if (data->graindensity > (int)(100.0*rand()/(RAND_MAX+1.0)) )
					*(data->grainsactive + grain) = 1;
				else *(data->grainsactive + grain) = 0;
			}else{
				*(data->grainsactive + grain) = 0;
			}
		}else{
			/* this should be GRAINPITCH_RANGE */
			if (data->graindensity > (int)(100.0*rand()/(RAND_MAX+1.0)) )
				*(data->grainsactive + grain) = 1;
			else *(data->grainsactive + grain) = 0;
		}
	}

	/* LATER: this might wrap wrong !?!*/
	if (*(data->grainoffsets + grain) > data->loopend){
		*(data->grainoffsets + grain) -= data->loopend - data->loopstart;
	}

	if (data->grainpitchmode == GRAINPITCH_CHORD){
		/* avoid stuttering volumechanges */
		data->grainmixfactor =  1.0f / sqrt((float)data->ngrains);
	
	} else {
		/*calculate mix factor for volume: */
		for (i = 0; i < data->ngrains;i++){
			if (*(data->grainsactive + grain)) a++;
		}
		if (a < 1) a = 1;
		data->grainmixfactor =  1.0f / sqrt((float)a);
	/*	printf ("mixfactor:%lf\n", data->grainmixfactor);	*/
	}
}

void looperdata_set_grainpitchmode ( looper_data_t* data, int mode){
	int i;

	if (mode == GRAINPITCH_AUTO){
		data->grainpitchmode = GRAINPITCH_AUTO;
		for (i = 0; i <  data->ngrains; i++){
			*(data->grainpitchtable + i) = 1.;
			*(data->grainpitchvolumes + i) = 1.;
		}
		data->grainpitchbend = 1.;
	}else if (mode == GRAINPITCH_CHORD){
		data->grainpitchmode = GRAINPITCH_CHORD;
		 for (i = 0; i <  data->ngrains; i++){
			*(data->grainpitchtable + i) = 0.;
			*(data->grainpitchvolumes + i) = 0.;
		}
		data->grainpitchbend = 1.;
	}else{
		data->grainpitchmode = GRAINPITCH_RANGE;
		for (i = 0; i <  data->ngrains; i++){
			*(data->grainpitchtable + i) = 1.;
			*(data->grainpitchvolumes + i) = 1.;
		}
		data->grainpitchbend = 1.;
	}
	inform_buffer(data);
}

int looperdata_get_grainpitchmode (looper_data_t* data){
	return data->grainpitchmode;
}

void looperdata_set_grainpitchtablevalue ( looper_data_t* data, double value, double vol){
	int i = 0;
	int found = 0;

/*	printf ("set grainpitch:%lf - %lf\n",value,vol);*/
	if (vol == 0.){
		while (!found && (i < data->ngrains)){
			if (*(data->grainpitchtable + i) == value){
				if (data->grainpitchmode == GRAINPITCH_CHORD){
					*(data->grainpitchtable + i) = 0.;
/*					*(data->grainpitchvolumes + i) = 0.; */  /* mute  without fade !!!*/
				}else{
				 	*(data->grainpitchtable + i) = 1.; /* fall back to regular volume */
					*(data->grainpitchvolumes + i) = 0;
				}
				found++;
			}
			i++;
		}		
		if ((i == data->ngrains) && !found)printf ("off note not found!\n");
	}else{
		while (!found && (i < data->ngrains)){
			if (data->grainpitchmode == GRAINPITCH_CHORD){
				if (*(data->grainsactive + i) == 0){
/*				if (*(data->grainpitchtable + i) == 0.){*/
					*(data->grainpitchtable + i) = value;
					*(data->grainpitchvolumes + i) = vol;
					*(data->grainsactive + i) = 1;
					found++;
				}
			}else{
				if (*(data->grainpitchtable + i) == 1.){
					*(data->grainpitchtable + i) = value;
					*(data->grainpitchvolumes + i) = vol;
					found++;
				}
			}
			i++;
		}
		if ((i == data->ngrains) && !found)printf ("graintable full!\n");
	}
}

static void inform_buffer(looper_data_t* data){
	long bw = 0;
	if (!data->buf) return;
	if (buffer_get_type(data->buf) != BUFFER_TYPE_DISCSTREAM) return;

	if (data->ngrains > 1){
		/* calculate the maximum distance between two grain-playheads:*/
		long slow, fast, cursor;
		long tmp, offset;
		offset = data->maxgrainlength * (data->ngrains + 1);
		offset /= 15;
		slow = (long)((float)data->maxgrainlength * data->mingrainspeed);
		fast = (long)((float)data->maxgrainlength * data->maxgrainspeed);
		cursor = (long)((float)data->maxgrainlength * data->speed);
		bw = abs(cursor - slow) + offset;
		tmp = abs(fast - slow) + offset;
		if (tmp > bw) bw = tmp;
/*		printf ("max distance between two grains:%ld\n",bw);*/
	} else bw = 16384;

	buffer_set_area(data->buf,data->buffer_group,
			data->loopstart,data->loopend,bw);
}

void looperdata_set_grainpitchbend (looper_data_t* data, double bend){
/*	printf ("set grainpitchbend\n");*/
	if ((bend > -1.) && (bend < 2.))
		data->grainpitchbend = bend;
	inform_buffer(data);
}

void looperdata_set_loopstart(looper_data_t* data, long start){
	if (!data) return;
	if (data->buf){
		if (start > buffer_get_size(data->buf)) 
			start = buffer_get_size(data->buf) - 1;
		if (start < 1) start = 1;
		data->loopstart = start;
		if (data->loopstart >= data->loopend)
                        data->loopend  = data->loopstart + 1;
	}
	inform_buffer(data);
}

void looperdata_set_loopend(looper_data_t* data, long end){
	if (!data) return;
	if (data->buf){
		if (end > buffer_get_size(data->buf))
                        end = buffer_get_size(data->buf) - 1;
		if (end < 2) end = 2;
		data->loopend = end;
		if (data->loopstart >= data->loopend)
                        data->loopend  = data->loopstart + 1;
	}
	inform_buffer(data);	
}

void looperdata_set_playmode(looper_data_t* data,int mode){
	data->playmode = mode;
	if (data->playmode == LOOP_PLAYMODE_EXTERNALPOS)
		data->modespeed = 0.;
	else 	data->modespeed = 1.;
}

void looperdata_set_recmode(looper_data_t* data,int mode){
	data->recmode = mode;
}

long looperdata_get_loopstart(looper_data_t* data){
	return data->loopstart;
}    

long looperdata_get_loopend(looper_data_t* data){
	return data->loopend;
}

void looperdata_set_recstart(looper_data_t* data, long start){
	if (!data) return;
        if (data->buf){
                if (start > buffer_get_size(data->buf))
                        start = buffer_get_size(data->buf) - 2;
                if (start < 1) start = 1;
                data->recstart = start;
                if (data->recstart >= data->recend)
                        data->recend = data->loopstart + 1;
        }
        inform_buffer(data);
}

void looperdata_set_recend(looper_data_t* data, long end){
	if (!data) return;
        if (data->buf){
                if (end > buffer_get_size(data->buf))
                        end = buffer_get_size(data->buf) - 1;
                if (end < 2) end = 2;
                data->recend = end;
                if (data->recstart >= data->recend)
                        data->recend  = data->recstart + 1;
        }
        inform_buffer(data);
}

void looperdata_set_playpos(looper_data_t* data, double playpos){
	data->playpos = playpos;
	data->playindex = playpos;
	if (data->buf) 
		buffer_readsample_interpolated(data->buf, 
			1, (long)data->playpos, 0);
}

void looperdata_set_recpos(looper_data_t* data, long recpos){
	data->recpos = recpos;
}

long looperdata_get_recstart(looper_data_t* data){
	return data->recstart;
}

long looperdata_get_recend(looper_data_t* data){
	return data->recend;
}

void looperdata_set_speed(looper_data_t* data, double speed){
	data->speed = speed;
}

double looperdata_get_speed(looper_data_t* data){
	return data->speed;
}

double looperdata_get_playpos(looper_data_t* data){
	return data->playpos;
}

long looperdata_get_recpos(looper_data_t* data){
	return data->recpos;
}

static void calc_limconst (looper_data_t* data){
	data->limconst = (data->limit - data->limknee) *  data->limknee;
/*	printf ("knee:%f, limit:%f, const:%f\n",data->limknee,data->limit,data->limconst);*/
}

int looperdata_get_limiter(looper_data_t* data){
	return data->limiter;
}

void looperdata_set_limiter(looper_data_t* data, int limiter){
	if (limiter) data->limiter = 1;
	else data->limiter = 0;
}

float looperdata_get_limknee(looper_data_t* data){
	return  data->limknee;	
}

void looperdata_set_limknee(looper_data_t* data, float limknee){
	data->limknee = limknee;
	calc_limconst(data);
}

float looperdata_get_limit(looper_data_t* data){
	return data->limit;
}

void looperdata_set_limit(looper_data_t* data, float limit){
	data->limit = limit;
	calc_limconst(data);
}


int looperdata_get_thresholdmode(looper_data_t* data){
	return data->thresholdmode;
}

void   looperdata_set_thresholdmode(looper_data_t* data, int mode){
	if (mode){ 
		data->thresholdmode = 1;	
		data->thresholdstate = THRESHOLDSTATE_LOW;
	} else {
		data->thresholdmode = 0;
		data->thresholdstate = THRESHOLDSTATE_ANY;
	}
}

double looperdata_get_low_threshold(looper_data_t* data){ 
	return data->low_threshold;;
}

void   looperdata_set_low_threshold(looper_data_t* data, double low){
	if (low >= 0.)
		data->low_threshold = low;
}

double looperdata_get_high_threshold(looper_data_t* data){
	return data->high_threshold;	
}

void   looperdata_set_high_threshold(looper_data_t* data, double high){
	if (high >= 0.)
		data->high_threshold = high;
}

int    looperdata_get_thresholdbuffersize(looper_data_t* data){
	return data->threshringsize;
}

void   looperdata_set_thresholdbuffersize(looper_data_t* data, int size){
	if ((size > 64) && (size < 882000)){ /* could be more sophisticated */
		data->threshringsize = size;
		if (data->threshringptr > data->threshringsize)
			data->threshringptr = 0;
		data->threshring = (float*) realloc (data->threshring, 
					sizeof(float) * data->threshringsize);
	}
}


/*
void looperdata_set_grainlength (looper_data_t* data, long length){
	if (length < 256){length = 256;}
	data->grainlength = length;
}
*/

void looperdata_set_pointer (looper_data_t* data, float *pointer){
	buffer_lock(data->buf);
	data->pointer = *pointer;
	buffer_unlock(data->buf);
}

void looperdata_get_pointer (looper_data_t* data, float *pointer){
	float f = data->playindex - data->loopstart;

	if (f == 0.0) 	*pointer = 0.;
	else 	*pointer = f / (float)(data->loopend - data->loopstart);

//	if (f)
//		printf ("diff:%ld ,end:%ld pointer:%f\n",(data->loopend - data->loopstart),data->loopend,*pointer);
		
}

int looperdata_get_ngrains (looper_data_t* data){
	return data->ngrains;
}

void looperdata_set_ngrains (looper_data_t* data, int ngrains){
	int i;
/*	int a = 0;*/
	if (ngrains == data->ngrains) return;
	if (ngrains < 0) ngrains = 0;
	if (ngrains > 15) ngrains = 15;
	
/*	printf ("ngrains:%d\n",ngrains);*/

	if (ngrains){
		data->grainindexes = realloc (data->grainindexes, sizeof(long) * ngrains);
		data->grainoffsets = realloc (data->grainoffsets, sizeof(long) * ngrains);
		data->grainsizes   = realloc (data->grainsizes, sizeof(long) * ngrains);
		data->grainfadelengths = realloc (data->grainfadelengths, sizeof(long) * ngrains);
		data->grainspeeds  = realloc (data->grainspeeds, sizeof(double) * ngrains);
		data->grainsactive = realloc (data->grainsactive, sizeof(int) * ngrains);
		data->grainpitchtable = realloc (data->grainpitchtable, sizeof(double) * ngrains);
		data->grainpitchvolumes = realloc (data->grainpitchvolumes, sizeof(double) * ngrains);
		data->grainpansL = realloc (data->grainpansL, sizeof(double) * ngrains);
		data->grainpansR = realloc (data->grainpansR, sizeof(double) * ngrains);

		if (data->buf){
			for (i = data->ngrains; i < ngrains; i++){
/*				printf ("initialising grain nr. %d\n",i);*/
				looperdata_reset_grain(data,i);
				if (data->grainpitchmode == GRAINPITCH_CHORD){
					data->grainpitchtable[i] = 0.;
					data->grainpitchvolumes[i] = 0.;
				}else{
				 	data->grainpitchtable[i] = 1.;
					data->grainpitchvolumes[i] = 1.;
				}
/*				if (data->grainsactive[i]) a++;*/
			}
		}
/*		data->grainmixfactor =  1.0f / (1. + log10f(sqrt(ngrains)) * 3.333);*/
		data->grainmixfactor =  1.0f / sqrt(ngrains);
/*		printf ("mixfactor:%lf\n",data->grainmixfactor);*/
		/* change = 20 * log10(sqrt(number)) db; 6db = factor 2 */
	}
	data->ngrains = ngrains;
	inform_buffer(data);
}

/* reverb : */

void looperdata_set_reverblength(looper_data_t* data, long length){
        if (length < 0) length = 0;
        if (length > 441000) length = 441000; /* any upper border for now */
        if (data){
                buffer_resize(data->reverb,length);
                data->reverblength = buffer_get_size(data->reverb);
        }
}

long looperdata_get_reverblength(looper_data_t* data){
        if (data)
                return data->reverblength;
        else return 0;
}

void looperdata_set_feedback(looper_data_t* data, double feedback){
        if (feedback < 0.) feedback = 0.;
        if (feedback > 1.) feedback = 1.;
/*      printf ("set feedback:%lf\n",feedback);*/
        if (data)
                data->feedback = feedback;
}

double looperdata_get_feedback(looper_data_t* data){
        if (data)
                return data->feedback;
        else return 0;
}

void looperdata_write_sample(looper_data_t* data,float* L, float* R){
	if (!data->buf) return;
	if (!buffer_get_size(data->buf))  return;

	if (buffer_get_channels(data->buf) > 1){
		if (data->inmaxL < fabs(*L)){data->inmaxL = fabs(*L);}
		if (data->inmaxR < fabs(*R)){data->inmaxR = fabs(*R);}

		buffer_lock(data->buf);	
		*L = *L * data->recmixnew + 
			buffer_readsample_interpolated(data->buf, 1,data->recpos, data->buffer_group) *
			data->recmixorig;
		*R = *R * data->recmixnew + 
                        buffer_readsample_interpolated(data->buf, 2,data->recpos, data->buffer_group) *
                        data->recmixorig;
		if (data->limiter){
			if (*L > data->limknee) *L = data->limit - data->limconst / *L;
			else if (*L < -data->limknee) *L = -data->limit - data->limconst / *L;
			if (*R > data->limknee) *R = data->limit - data->limconst / *R;
			else if (*R < -data->limknee) *R = -data->limit - data->limconst / *R;
		}
		buffer_writesample(data->buf, 1, data->recpos, 0, *L);
		buffer_writesample(data->buf, 2, data->recpos, 0, *R);
		buffer_unlock(data->buf);
	}else{
		if (data->inmaxL < fabs(*L)){data->inmaxL = fabs(*L);}

		buffer_lock(data->buf);
		*L = *L * data->recmixnew +
                        buffer_readsample_interpolated(data->buf, 1,data->recpos, data->buffer_group) *
                        data->recmixorig;
		if (data->limiter){
			if (*L > data->limknee) *L = data->limit - data->limconst / *L;
			else if (*L < -data->limknee) *L = -data->limit - data->limconst / *L;
		}
		buffer_writesample(data->buf, 1, data->recpos, 0, *L);
		buffer_unlock(data->buf);
	}
	if (++data->recpos > data->recend){
		data->recpos = data->recstart;
		if (data->recmode == LOOP_RECMODE_ONCE)
			data->isrecording = 0;
	}
	if (data->recpos < data->recstart){
		data->recpos = data->recstart;
	}
}

void looperdata_write_sampleblock(looper_data_t* data, long size, float* Lbuf, float* Rbuf){
	long l, part;
	float *L;
	float *R;

	if (!data->buf) return;
	if (!buffer_get_size(data->buf))  return;

	if (data->isrecording){
		if (buffer_get_channels(data->buf) > 1){
			for (l = 0; l < size; l++){
				L = (Lbuf + l);
				R = (Rbuf + l);
				if (data->inmaxL < fabs(*L)) data->inmaxL = fabs(*L);
				if (data->inmaxR < fabs(*R)) data->inmaxR = fabs(*R);
	
				*L *= data->recmixnew;	
				*R *= data->recmixnew;
				if (data->recmixorig){
					*L += buffer_readsample_interpolated(data->buf, 1, 
								data->recpos, data->buffer_group)
						* data->recmixorig;
					*R += buffer_readsample_interpolated(data->buf, 2,
								data->recpos, data->buffer_group)
						* data->recmixorig;
				}
	
				if (data->limiter){
					if (*L > data->limknee) *L = data->limit - data->limconst / *L;
					else if (*L < -data->limknee) *L = -data->limit - data->limconst / *L;
					if (*R > data->limknee) *R = data->limit - data->limconst / *R;
					else if (*R < -data->limknee) *R = -data->limit - data->limconst / *R;
				}
			}
		}else{
			for (l = 0; l < size; l++){
				L = (Lbuf + l);
				R = (Rbuf + l);
				if (data->inmaxL < fabs(*(Lbuf + l))) data->inmaxL = fabs(*(Lbuf + l));

				*L *= data->recmixnew;
				if (data->recmixorig)
					*L += buffer_readsample_interpolated(data->buf, 1,
								data->recpos, data->buffer_group)
						* data->recmixorig;
	
				if (data->limiter){
					if (*L > data->limknee) *L = data->limit - data->limconst / *L;
					else if (*L < -data->limknee) *L = -data->limit - data->limconst / *L;
				}
			}
		}

		if (data->recpos < data->recstart) data->recpos = data->recstart;
		if (data->recpos >= data->recend) data->recpos = data->recstart;

		while (size > 0){
			part = size;
			if ((part + data->recpos) > data->recend) /* avoid writing behind the end */
				part = data->recend - data->recpos;

			buffer_lock(data->buf);
			buffer_write_sampleblock(data->buf, data->recpos, part, 0, Lbuf, Rbuf);
			/* write sampleblock = ok */
			buffer_unlock(data->buf);

			size -= part;
			data->recpos += part;

			if (data->recpos >= data->recend){ /* wrap around */
				data->recpos = data->recstart;
				if (data->recmode == LOOP_RECMODE_ONCE){
					size = 0;
					data->isrecording = 0;
				}
			}
		}
	}
}

static double grain_inc(looper_data_t* data, int i){
	double pos = data->playpos;

	*(data->grainindexes + i) += 1;

	if (*(data->grainindexes + i) >= *(data->grainsizes + i)){
/*		printf ("index:%ld, size:%ld\n",*(data->grainindexes + i), *(data->grainsizes + i));*/
		looperdata_reset_grain (data,i);
	} else {
		pos = (float)*(data->grainoffsets + i) + *(data->grainindexes + i) 
							* *(data->grainspeeds + i);
		/* check if still inside the loop: */
		if (pos > (float)data->loopend){
			*(data->grainoffsets + i) -= (data->loopend - data->loopstart)/* * *(data->grainspeeds + i)*/;
/*			printf ("loopend:%ld, recend:%ld, pos:%.2lf\n",data->loopend, data->recend,pos);*/
			pos = (float)*(data->grainoffsets + i) + *(data->grainindexes + i) * *(data->grainspeeds + i);
		} else if (pos < (float)data->loopstart){
			*(data->grainoffsets + i) += (data->loopend - data->loopstart)/* * *(data->grainspeeds + i)*/;
			pos = (float)*(data->grainoffsets + i) + *(data->grainindexes + i) * *(data->grainspeeds + i);
		}
	}
	return pos;
}

void looperdata_calc_sample_stereo(looper_data_t* data, float* L, float* R){
	int i;
	float vol = 1.;
	double pos, ipos;

	if (data->ngrains < 1){
		/* single playhead */
		pos = data->playpos;
		*L = buffer_readsample_interpolated(data->buf,1,pos, data->buffer_group);
		*R = buffer_readsample_interpolated(data->buf,2,pos, data->buffer_group);
	}else{
		/* granular playhead(s) */
		*L = .0;
		*R = .0;
		for (i = 0; i < data->ngrains; i++){
			pos = grain_inc(data,i);
			if (*(data->grainsactive + i)){
				if (*(data->grainindexes + i) < *(data->grainfadelengths + i)){
					vol = (float)*(data->grainindexes + i) / (float)*(data->grainfadelengths + i);
				} else if ( (ipos = (*(data->grainsizes + i) - *(data->grainindexes + i)))
						< *(data->grainfadelengths + i)){
					vol = ipos / (float)*(data->grainfadelengths + i);
				} else vol = 1.;

				vol *= data->grainmixfactor * *(data->grainpitchvolumes + i);

				*L += buffer_readsample_interpolated(data->buf,1,pos, data->buffer_group) 
					* vol * *(data->grainpansL);
				*R += buffer_readsample_interpolated(data->buf,2,pos, data->buffer_group) 
					* vol * *(data->grainpansR);
			}
		}	
	}

	/* clickmode ?: */
	if (data->clickmode_buf){
		pos = (float)(data->playpos - data->loopstart) / data->speed;
		if (pos < buffer_get_size(data->clickmode_buf)){
			*L += buffer_readsample_interpolated(data->clickmode_buf,1,pos,0);
			*R += buffer_readsample_interpolated(data->clickmode_buf,2,pos,0);
		}
	}

	if (data->playmode == LOOP_PLAYMODE_SINE){
		data->modespeed = 
			sin((data->playpos - data->loopstart) * PI / (data->loopend - data->loopstart)) * 1.5 + .5;
	}else if (data->playmode == LOOP_PLAYMODE_CUSTOMSPEED){
		data->modespeed = 
			inter4pol_float(data->customplaymode,
			(float)(data->playpos - data->loopstart) * (float)data->cm_size / 
			(float)(data->loopend - data->loopstart),data->cm_size);
	}else if (data->playmode == LOOP_PLAYMODE_EXTERNALPOS){
		data->playindex = data->loopstart +
			(int)(data->pointer * (data->loopend - data->loopstart));
	}

	data->playindex += data->speed * data->modespeed;

	if (data->playmode == LOOP_PLAYMODE_EXTERNALPOS){
		if (data->playindex > data->loopend)
			data->playindex =  data->loopstart
					+ ((long)(data->playindex - data->loopstart)
					% (long)(data->loopend - data->loopstart));
		if (data->playindex < data->loopstart)
			data->playindex = data->loopend 
					- ((long)(data->loopend - data->playindex) 
					% (long)(data->loopend - data->loopstart));
	} else if (data->playindex > data->loopend){
		if (data->playmode == LOOP_PLAYMODE_BACKFORTH){
/*			printf ("bounce dir\n");*/
			data->modespeed *= -1;
		}else if (data->playmode == LOOP_PLAYMODE_LOOP){
			data->modespeed = 1;
		}
		if ((data->speed * data->modespeed) > .0){
			if (data->playmode == LOOP_PLAYMODE_ONCE){
				data->playindex = data->loopstart;
				looperdata_set_playing(data,0);
			} else data->playindex += data->loopstart - data->loopend;
		}else{
			data->playindex = data->loopend;
		}
/*		printf ("wrap around forward\n");*/
	} else if (data->playindex < data->loopstart){
		if (data->playmode == LOOP_PLAYMODE_BACKFORTH){
			data->modespeed *= -1;
		}else if ((data->playmode == LOOP_PLAYMODE_LOOP) && (data->modespeed < 0)){
			data->modespeed = 1;
		}

		if ((data->speed  * data->modespeed) > .0){
			data->playindex = data->loopstart;
		}else{
			if (data->playmode == LOOP_PLAYMODE_ONCE){
				data->playindex = data->loopend;
				looperdata_set_playing(data,0);
			} else data->playindex += data->loopend - data->loopstart;
		}
	}
	if (data->playmode == LOOP_PLAYMODE_POSITION){
		data->playpos = data->loopstart + (data->loopend - data->loopstart) / 
			(data->cm_max - data->cm_min) * (inter4pol_float (data->customplaymode,
			(data->playindex - data->loopstart) * data->cm_size /
			(data->loopend - data->loopstart),data->cm_size) - data->cm_min);
 	}else{
		data->playpos = data->playindex;
	}
}


void looperdata_calc_sample_mono(looper_data_t* data, float* L){
	int i;
	float vol = 1.;
	double pos, ipos;

	if (data->ngrains < 1){
		/* single playhead */
		pos = data->playpos;
		*L = buffer_readsample_interpolated(data->buf,1,pos, data->buffer_group);
	}else{
		/* granular playhead(s) */
		*L = .0;
		for (i = 0; i < data->ngrains; i++){
			pos = grain_inc(data,i);
			if (*(data->grainsactive + i)){
				if (*(data->grainindexes + i) < *(data->grainfadelengths + i))
					vol = (float)*(data->grainindexes + i) / (float)*(data->grainfadelengths + i);
				else if ( (ipos = (*(data->grainsizes + i) 
					- *(data->grainindexes + i))) 
					< *(data->grainfadelengths + i))
					vol = ipos / (float)*(data->grainfadelengths + i);
				else vol = 1.;

				vol *= data->grainmixfactor * *(data->grainpitchvolumes + i);

				*L += buffer_readsample_interpolated(data->buf,1,pos, data->buffer_group) 
					* vol * *(data->grainpansL);
			}
		}	
	}

	/* clickmode ?: */
	if (data->clickmode_buf){
		pos = (float)(data->playpos - data->loopstart) / data->speed;
		if (pos < buffer_get_size(data->clickmode_buf)){
			*L += buffer_readsample_interpolated(data->clickmode_buf,1,pos,0);
		}
	}

	if (data->playmode == LOOP_PLAYMODE_SINE){
		data->modespeed = 
			sin((data->playpos - data->loopstart) * PI / (data->loopend - data->loopstart)) * 1.5 + .5;
	}else if (data->playmode == LOOP_PLAYMODE_CUSTOMSPEED){
		data->modespeed = 
			inter4pol_float(data->customplaymode,
			(float)(data->playpos - data->loopstart) * (float)data->cm_size / 
			(float)(data->loopend - data->loopstart),data->cm_size);
	}else if (data->playmode == LOOP_PLAYMODE_EXTERNALPOS){
		data->playindex = data->loopstart +
			(int)(data->pointer * (data->loopend - data->loopstart));
	}

	data->playindex += data->speed * data->modespeed;

	if (data->playmode == LOOP_PLAYMODE_EXTERNALPOS){
		if (data->playindex > data->loopend)
			data->playindex =  data->loopstart
					+ ((long)(data->playindex - data->loopstart)
					% (long)(data->loopend - data->loopstart));
		if (data->playindex < data->loopstart)
			data->playindex = data->loopend 
					- ((long)(data->loopend - data->playindex) 
					% (long)(data->loopend - data->loopstart));
	} else if (data->playindex > data->loopend){
		if (data->playmode == LOOP_PLAYMODE_BACKFORTH){
/*			printf ("bounce dir\n");*/
			data->modespeed *= -1;
		}else if (data->playmode == LOOP_PLAYMODE_LOOP){
			data->modespeed = 1;
		}
		if ((data->speed * data->modespeed) > .0){
			if (data->playmode == LOOP_PLAYMODE_ONCE){
				data->playindex = data->loopstart;
				looperdata_set_playing(data,0);
			} else data->playindex += data->loopstart - data->loopend;
		}else{
			data->playindex = data->loopend;
		}
/*		printf ("wrap around forward\n");*/
	} else if (data->playindex < data->loopstart){
		if (data->playmode == LOOP_PLAYMODE_BACKFORTH){
			data->modespeed *= -1;
		}else if ((data->playmode == LOOP_PLAYMODE_LOOP) && (data->modespeed < 0)){
			data->modespeed = 1;
		}

		if ((data->speed  * data->modespeed) > .0){
			data->playindex = data->loopstart;
		}else{
			if (data->playmode == LOOP_PLAYMODE_ONCE){
				data->playindex = data->loopend;
				looperdata_set_playing(data,0);
			} else data->playindex += data->loopend - data->loopstart;
		}
	}
	if (data->playmode == LOOP_PLAYMODE_POSITION){
		data->playpos = data->loopstart + (data->loopend - data->loopstart) / 
			(data->cm_max - data->cm_min) * (inter4pol_float (data->customplaymode,
			(data->playindex - data->loopstart) * data->cm_size /
			(data->loopend - data->loopstart),data->cm_size) - data->cm_min);
 	}else{
		data->playpos = data->playindex;
	}
}

void looperdata_calc_sampleblock (looper_data_t* data, long size, float* Lbuf, float* Rbuf, float* Pbuf, int do_play, int do_rec){
	long l;
	/*long rp;*/
	/*double pp;*/
	if (!data->buf) return;

	/*pp = data->playpos;
	rp = data->recpos;	*/

	if ((size <= (data->recend - data->recstart) || !data->isrecording) && 
		0 && /* this part is broken, use the single sample copy mode instead */
		(buffer_get_sampleratefactor(data->buf, data->samplerate) == 1.0)){
		/* LATER: include rec-threshold here when reactivating blockwise operation */
		if (data->isrecording && do_rec){ /* same result for l++ mode */
			looperdata_write_sampleblock(data, size, Lbuf, Rbuf);
		}
		if (data->isplaying && do_play){
			if (buffer_get_channels(data->buf) > 1){
	        		if (data->panswing){
					for (l = 0; (l < size) && (data->isplaying); l++){
						data->pointer = *(Pbuf + l);
                               			looperdata_calc_sample_stereo (data, (Lbuf + l), (Rbuf + l));
                               			*(Pbuf + l)  = (data->playpos - (float)data->loopstart) /
                                               		(float)(data->loopend - (float)data->loopstart);
	
               					looperdata_calc_pan(data);
       						*(Lbuf + l) *= data->Lfact;
       						*(Rbuf + l) *= data->Rfact;
					}
				} else { /* no panswing: */
					for (l = 0; (l < size) && (data->isplaying); l++){
                                       		data->pointer = *(Pbuf + l);
                                       		looperdata_calc_sample_stereo (data, (Lbuf + l), (Rbuf + l));
                                       		*(Pbuf + l)  = (data->playpos - (float)data->loopstart) /
                                               		(float)(data->loopend - (float)data->loopstart);
                                       		*(Lbuf + l) *= data->Lfact;
                                       		*(Rbuf + l) *= data->Rfact;
                               		}
				} /* end if panswing */
			} else { /* mono: */
				for (l = 0; (l < size) && (data->isplaying); l++){
                               		data->pointer = *(Pbuf + l);
                               		looperdata_calc_sample_mono (data, (Lbuf + l));
                               		*(Pbuf + l)  = (data->playpos - (float)data->loopstart) /
                                               		(float)(data->loopend - (float)data->loopstart);
                       		}
       				if (data->panswing){
					for (l = 0;l < size;l++){
               					looperdata_calc_pan(data);
						*(Rbuf + l) = *(Lbuf + l) * data->Rfact;
       						*(Lbuf + l) *= data->Lfact;
					}
				} else {
					for (l = 0;l < size;l++){
						*(Lbuf + l) *= data->Lfact;
						*(Rbuf + l) = *(Lbuf + l);
					}
				}
			} /* end if stereo/mono */
		}
	} else { /* else have to work sample by sample */
		if (data->thresholdmode){ /* check recording thresholds: */
			double max = 0.;
			/* write new data to ringbuffer first:*/
			for (l = 0; l < size ; l++){
				if (buffer_get_channels(data->buf) > 1){
					if (fabs(*(Lbuf + l)) > fabs(*(Rbuf + l)))
						*(data->threshring + data->threshringptr) = fabs(*(Lbuf + l));
					else	*(data->threshring + data->threshringptr) = fabs(*(Rbuf + l));
				} else 	*(data->threshring + data->threshringptr) = fabs(*(Lbuf + l));
				if (++data->threshringptr >= data->threshringsize) data->threshringptr = 0;
			}
			/* check ringbuffer for maximum: */
			for (l = 0; l < data->threshringsize ; l++){
                                if (*(data->threshring + l) > max) max = *(data->threshring + l);
                        }

			if (max < data->low_threshold){
				data->thresholdstate = THRESHOLDSTATE_LOW;
			} else  if (max > data->high_threshold) {
				data->thresholdstate = THRESHOLDSTATE_HIGH;
			}
		}
		if ((buffer_get_sampleratefactor(data->buf, data->samplerate) == 1.0) && 
			data->isrecording && !data->isplaying  && do_rec && data->thresholdstate){	
			/* copy at once anyway */
			looperdata_write_sampleblock(data, size, Lbuf, Rbuf);	
		} else {
			if (buffer_get_channels(data->buf) > 1){	
				for (l = 0; l < size ; l++){
					if (data->isrecording && do_rec && data->thresholdstate)
						looperdata_write_sample (data, (Lbuf + l), (Rbuf + l));
					if (data->isplaying && do_play){
						if (data->panswing){
							data->pointer = *(Pbuf + l);
                                                	looperdata_calc_sample_stereo (data, (Lbuf + l), (Rbuf + l));
                                                	*(Pbuf + l)  = (data->playpos - (float)data->loopstart) /
                                                        	(float)(data->loopend - (float)data->loopstart);
	
                                                	looperdata_calc_pan(data);
                                                	*(Lbuf + l) *= data->Lfact;
                                                	*(Rbuf + l) *= data->Rfact;
						} else { /* no panswing: */
							data->pointer = *(Pbuf + l);
                                                        looperdata_calc_sample_stereo (data, (Lbuf + l), (Rbuf + l));
                                                        *(Pbuf + l)  = (data->playpos - (float)data->loopstart) /
                                                                (float)(data->loopend - (float)data->loopstart);
                                                        *(Lbuf + l) *= data->Lfact;
                                                        *(Rbuf + l) *= data->Rfact;
						} /* end if panswing */
					} /* end if recording */
				}
			} else { /* mono: */
				for (l = 0; l < size; l++){
					if (data->isrecording && do_rec && data->thresholdstate)
						looperdata_write_sample (data, (Lbuf + l), NULL);
					if (data->isplaying && do_play){
                                                if (data->panswing){
                                        		data->pointer = *(Pbuf + l);
                                        		looperdata_calc_sample_mono (data, (Lbuf + l));
                                        		*(Pbuf + l)  = (data->playpos - (float)data->loopstart) /
                                                        		(float)(data->loopend - (float)data->loopstart);
							*(Rbuf + l) = *(Lbuf + l);
							looperdata_calc_pan(data);
							*(Lbuf + l) *= data->Lfact;
                                                        *(Rbuf + l) *= data->Rfact;
						} else {
                                                        data->pointer = *(Pbuf + l);
                                                        looperdata_calc_sample_mono (data, (Lbuf + l));
                                                        *(Pbuf + l)  = (data->playpos - (float)data->loopstart) /
                                                                        (float)(data->loopend - (float)data->loopstart);
                                                        *(Rbuf + l) = *(Lbuf + l);
                                                        *(Lbuf + l) *= data->Lfact;
                                                        *(Rbuf + l) *= data->Rfact;
                                                 }/* end if panswing */
					} /* end if playing */
                                }                      
			} /* end if mono */
		} /* end if playing */
	} /* end if size < loopsize */

	if (data->isplaying && do_play){
		/* reverb?: */
                if (data->reverblength > 0){
                        for (l = 0;l < size;l++){
                                if (++data->revpos >= data->reverblength) data->revpos = 0;
                                *(Lbuf + l) += buffer_readsample_interpolated(data->reverb, 1, data->revpos, 0);
                                buffer_writesample(data->reverb, 1, data->revpos, 0, *(Lbuf + l) * data->feedback);
                                *(Rbuf + l) += buffer_readsample_interpolated(data->reverb, 1, data->revpos, 0);
                                buffer_writesample(data->reverb, 1, data->revpos, 0, *(Rbuf + l) * data->feedback);
                        }
                }
                if (data->limiter){
                        for (l = 0;l < size;l++){
                                if (*(Lbuf + l) > data->limknee)
                                        *(Lbuf + l) = data->limit - data->limconst / *(Lbuf + l);
                                else if (*(Lbuf + l) < -data->limknee)
                                        *(Lbuf + l) = -data->limit - data->limconst / *(Lbuf + l);
                                if (*(Rbuf + l) > data->limknee)
                                        *(Rbuf + l) = data->limit - data->limconst / *(Rbuf + l);
                                else if (*(Rbuf + l) < -data->limknee)
                                        *(Rbuf + l) = -data->limit - data->limconst / *(Rbuf + l);
                        }
                }/* end limiter */

                for (l = 0;l < size;l++){
                        if (data->outmaxL < *(Lbuf + l)){data->outmaxL = *(Lbuf + l);}
                        if (data->outmaxR < *(Rbuf + l)){data->outmaxR = *(Rbuf + l);}
                } /* end max */
/*
		printf ("j:%ld, %ld after: rcpos:%ld(%ld, %ld), playpos: %.2lf(%.2lf, %.2lf)\n",size, data->recend,
			data->recpos, rp, (data->recpos - rp), 
			data->playpos, pp, (data->playpos - pp));
*/
	} else {
		for (l = 0;l < size;l++){
			*(Lbuf + l) = 0.;
			*(Rbuf + l) = 0.;
		}
	} /* end if active */
}
