u16 finger_raw[36]; // raw value back from stm
u16 finger_min[36]; // lowest value seen (zero point)
u16 finger_max[36]; // highest value seen (zero point)

static inline int finger_cap(int sensoridx) {
	return finger_raw[sensoridx];
}
static inline int finger_mincap(int sensoridx) {
	return finger_min[sensoridx];
}
static inline int finger_maxcap(int sensoridx) {
	return finger_max[sensoridx];
}

typedef struct CalibResult {
	u16 pressure[8];
	s16 pos[8];
} CalibResult;
typedef struct CalibProgress {
	float weight[8];
	float pos[8];
	float pressure[8];
} CalibProgress;
static inline CalibProgress* GetCalibProgress(int sensoridx) {
	CalibProgress* p = (CalibProgress*)delaybuf;
	return p + sensoridx;
}
CalibResult calibresults[18];

typedef union Finger {
	s32 x;
	struct {
		s16 pressure;
		s16 pos : 15;
		s16 written : 1;
	};
} Finger;
Finger fingers_ui_time[9][8]; // 8 frames for 9 fingers
Finger fingers_synth_time[8][8]; // 8 frames for 8 fingers
Finger fingers_synth_sorted[8][8];
u8 finger_state;
u8 finger_step;
u16 finger_stepmask;
volatile u8 finger_frame_ui;
volatile u8 finger_frame_synth;
u8 finger_ui_done_this_frame;
u8 waitcounter;
u8 finalwait;
u8 fingerediting = 0; // finger is over some kind of non-musical edit control
u8 prevfingerediting = 0;

typedef struct euclid_state {
	int trigcount;
	bool did_a_retrig;
	bool supress;
	
} euclid_state;

euclid_state arp_rhythm;
euclid_state seq_rhythm;


static inline u8 touch_ui_writingframe(void) { return (finger_frame_ui) & 7; }
static inline u8 touch_ui_frame(void) { return (finger_frame_ui-1)&7; }
static inline u8 touch_ui_prevframe(void) { return (finger_frame_ui -2) & 7; }

static inline u8 touch_synth_writingframe(void) { return (finger_frame_synth) & 7; }
static inline u8 touch_synth_frame(void) { return (finger_frame_synth - 1) & 7; }
static inline u8 touch_synth_prevframe(void) { return (finger_frame_synth - 2) & 7; }


static inline Finger* touch_synth_getlatest(int finger) { return &fingers_synth_time[finger][touch_synth_frame()]; }
static inline Finger* touch_synth_getprev(int finger) { return &fingers_synth_time[finger][touch_synth_prevframe()]; }
static inline Finger* touch_synth_getwriting(int finger) { return &fingers_synth_time[finger][touch_synth_writingframe()]; }
static inline Finger* touch_ui_getwriting(int finger) { return &fingers_ui_time[finger][touch_ui_writingframe()]; }
static inline Finger* touch_ui_getlatest(int finger) { return &fingers_ui_time[finger][touch_ui_frame()]; }
static inline Finger* touch_ui_getprev(int finger) { return &fingers_ui_time[finger][touch_ui_prevframe()]; }

#define SWAP(a,b) if (a>b) { int t=a; a=b; b=t; }
void sort8(int *dst, const int *src) {
	int a0=src[0],a1=src[1],a2=src[2],a3=src[3],a4=src[4],a5=src[5],a6=src[6],a7=src[7];
	SWAP(a0,a1);SWAP(a2,a3);SWAP(a4,a5);SWAP(a6,a7);
	SWAP(a0,a2);SWAP(a1,a3);SWAP(a4,a6);SWAP(a5,a7);
	SWAP(a1,a2);SWAP(a5,a6);SWAP(a0,a4);SWAP(a3,a7);
	SWAP(a1,a5);SWAP(a2,a6);
	SWAP(a1,a4);SWAP(a3,a6);
	SWAP(a2,a4);SWAP(a3,a5);
	SWAP(a3,a4);
	dst[0]=a0; dst[1]=a1; dst[2]=a2; dst[3]=a3; dst[4]=a4; dst[5]=a5; dst[6]=a6; dst[7]=a7;
}
#undef SWAP

void touch_reset_calib(void) {
//	memset(finger_max,0,sizeof(finger_max));
	memset(finger_min,-1, sizeof(finger_min));
	memset(finger_raw,0,sizeof(finger_raw));
	memset(calibresults, 0, sizeof(calibresults));
}



void check_curstep(void) { // enforces invariants 
	if (rampreset.looplen_step <= 0 || rampreset.looplen_step > 64)
		rampreset.looplen_step = 64;
	rampreset.loopstart_step_no_offset &= 63;
	u8 loopstart_step = (rampreset.loopstart_step_no_offset + step_offset) & 63;
	cur_step= (cur_step- loopstart_step) % rampreset.looplen_step;
	if (cur_step < 0)
		cur_step += rampreset.looplen_step;
	cur_step += loopstart_step;
}

void set_cur_step(u8 newcurstep, bool triggerit) {
	//u8 old = cur_step;
	cur_step = newcurstep;
	check_curstep();
	seq_rhythm.did_a_retrig = triggerit; // make the sound play out once
	ticks_since_step = 0;
	seq_divide_counter = 0;
}

void OnLoop(void) {
	if (edit_preset_pending != 255) {
		SetPreset(edit_preset_pending, false);
		edit_preset_pending = 255;
	}
	if (edit_pattern_pending != 255) {
		EditParamQuant(P_SEQPAT, M_BASE, edit_pattern_pending);
		edit_pattern_pending = 255;
	}
	if (pending_loopstart_step !=255) {
		u8 loopstart_step = (rampreset.loopstart_step_no_offset + step_offset) & 63;
		if (loopstart_step != pending_loopstart_step) {
			rampreset.loopstart_step_no_offset = (pending_loopstart_step - step_offset)&63;
			ramtime[GEN_PRESET] = millis();
		}
		set_cur_step(loopstart_step, seq_rhythm.did_a_retrig);
		pending_loopstart_step  = 255;
		
	}
	if (edit_sample1_pending != cur_sample1 && edit_sample1_pending!=255) {
		EditParamQuant(P_SAMPLE, 0, edit_sample1_pending);
		edit_sample1_pending = 255;
	}
	check_curstep();
}


bool touched_main_area;

bool got_ui_reset = false;
int tapcount = 0;
void clearlatch(void);

void reverb_clear(void) {
	memset(reverbbuf, 0, (RVMASK + 1) * 2);
}
void delay_clear(void) {
	memset(delaybuf, 0, (DLMASK + 1) * 2);
}


u16 audioin_holdtime = 0;
s16 audioin_peak = 0;
s16 audioin_hold = 0;
knobsmoother recgain_smooth;
int audiorec_gain_target = 1 << 15;

int recpos = 0; // this cycles around inside the delay buffer (which we use for a recording buffer) while armed...
int recstartpos = 0; // once we start recording, we note the position in the buffer here
int recreadpos = 0; // ...and this is where we are up to in terms of reading that out and writing it to flash
u8 recsliceidx = 0;
const static bool pre_erase = true;
u32 record_flashaddr_base = 0;

static inline SampleInfo* getrecsample(void) { return &ramsample; } 
static inline u8 getwaveform4(SampleInfo* s, int x) { // x is 0-2047
	if (x < 0 || x >= 2048)
		return 0;
	return (s->waveform4_b[x >> 1] >> ((x & 1) * 4)) & 15;
}
static inline u8 getwaveform4halfres(SampleInfo* s, int x) {  // x 0-1023
	u8 b = s->waveform4_b[x & 1023];
	return maxi(b & 15, b >> 4);
}
static inline u16 getwaveform4zoom(SampleInfo* s, int x, int zoom) { // x is 0-2048. returns average and peak!
	if (zoom <= 0)
		return getwaveform4(s, x >> zoom);
	int samplepairs = 1 << (zoom - 1);
	u8* b = &s->waveform4_b[(x>>1) & 1023];
	int avg = 0, peak=0;
	u8* bend = &s->waveform4_b[1024];
	for (int i = 0; i < samplepairs && b < bend; ++i, ++b) {
		int s0 = b[0] & 15;
		int s1 = b[0] >> 4;
		avg += s0 + s1;
		peak = maxi(peak, maxi(s0, s1));
	}
	avg >>= zoom;
	return avg+peak*256;
}

static inline void setwaveform4(SampleInfo* s, int x, int v) {
	v = clampi(v, 0, 15);
	u8* b = &s->waveform4_b[(x >> 1) & 1023];
	if (x & 1) {
		v = maxi(v, (*b) >> 4);
		*b = (*b & 0x0f) | (v << 4);
	}
	else {
		v = maxi(v, (*b) & 15);
		*b = (*b & 0xf0) | v;
	}
}

void DebugSPIPage(int addr);

void recording_stop_really(void) {
	// clear out the raw audio in the delaybuf
	reverb_clear();
	delay_clear();
	ramtime[GEN_SAMPLE] = millis();	// fill in the remaining split points
	SampleInfo* s = getrecsample();
	int startsamp = s->splitpoints[recsliceidx];
	int endsamp = s->samplelen;
	int n = 8 - recsliceidx;
	for (int i = recsliceidx + 1; i < 8; ++i) {
		int samp = startsamp + ((endsamp - startsamp) * (i - recsliceidx)) / n;
		s->splitpoints[i] = samp;
		EmuDebugLog("POST RECORD EVEN SET SPLITPOINT %d to %d\n", i, s->splitpoints[i]);

	}
	recsliceidx = 0;
	ramtime[GEN_SAMPLE] = millis();
	//DebugSPIPage(0);
	//DebugSPIPage(1024*1024*2-65536);

	enable_audio = EA_PLAY;
}

void recording_stop(void) {
	if (enable_audio == EA_PLAY) {
		editmode = EM_PLAY;
	}
	else if (enable_audio == EA_RECORDING) {
		enable_audio = EA_STOPPING1;
	}
	else  if (enable_audio >= EA_STOPPING1) {
		// do nothing
	}
	else
		enable_audio = EA_PLAY;
	
}

void seq_step(int initial);

void recording_trigger(void) {
	recsliceidx = 0;
	SampleInfo* s = getrecsample();
	memset(s, 0, sizeof(SampleInfo));
#define LEADIN 1024
	int leadin = mini(recpos, LEADIN);
	recreadpos = recstartpos = recpos - leadin;
	s->samplelen = 0;
	s->splitpoints[0] = leadin;
	enable_audio = EA_RECORDING;
}


void on_shift_hold(int button) {
	
	if (editmode == EM_PRESET) {
		if (shift_down == SB_CLEAR && shift_down_time > 64+4) {
			// clear it!
			if (last_preset_selection_rotstep>=0 && last_preset_selection_rotstep<64)
				copyrequest = last_preset_selection_rotstep + 128;
		}
	}
	
	if (editmode == EM_SAMPLE) {
		if (enable_audio==EA_PLAY && (shift_down == SB_RECORD || shift_down == SB_PLAY) && shift_down_time > 64) {
			knobsmooth_reset(&recgain_smooth, audiorec_gain_target);
			record_flashaddr_base = (edit_sample0 & 7) * (2 * MAX_SAMPLE_LEN);
			recsliceidx = 0;
			recstartpos = 0;
			recreadpos = 0;
			recpos = 0;
			enable_audio = pre_erase ? EA_PREERASE : EA_MONITOR_LEVEL;
		}
	}
}

void arp_reset(void);
void ShowMessage(EFont fnt, const char* msg, const char *submsg);

u8 prev_editmode;
u8 last_edit_param=255;
bool param_sticky = false;
void on_shift_down(void) {
	prev_editmode = editmode;
	touched_main_area = false;
	if (editmode == EM_SAMPLE) {
		if (shift_down == SB_PREV) {
			
		} 
		else if (shift_down == SB_RECORD || shift_down==SB_RECORD || shift_down==SB_PLAY) {
			if (enable_audio == EA_PLAY) {
				/* long press */
			}
			else if (enable_audio == EA_MONITOR_LEVEL)
				enable_audio = EA_ARMED;
			else if (enable_audio == EA_ARMED) {
				recording_trigger();
			}
			else if (enable_audio == EA_RECORDING) {
				recording_stop();
			}
		}
		return;
	}

	switch (shift_down) {
	case SB_PLAY: // play/pause
		if (playmode == PLAY_WAITING_FOR_CLOCK_STOP) {
			playmode = PLAY_STOPPED;
			OnLoop();
		}
		else if (playmode == PLAY_PREVIEW) {
			//playmode = PLAYING;
			//arp_reset();
			//seq_step(true);
		} else if (playmode == PLAY_STOPPED) {
			playmode = PLAY_PREVIEW;
			seq_step(1);
		}
		else if (playmode == PLAYING) {
			playmode = PLAY_WAITING_FOR_CLOCK_STOP;
		}
		break;
	//case SB_REWIND: // reset
	//	playclock=loopstart+latency_fix;
	//	OnLoop();
	//	break;
	case SB_PREV: // prev
		editmode = EM_START;
		if (isplaying())
			got_ui_reset = true;
		break;
	case SB_NEXT: // next 
		editmode = EM_END;
		break;
	case SB_RECORD:
		knobbase[0] = adc_smooth[4].y2;
		knobbase[1] = adc_smooth[5].y2;
		recordingknobs = 0;
		break;
	case SB_CLEAR: // delete/clear. TODO: hold down in preset mode to clear a pattern/sample/preset. in play mode, supress notes.
		clearlatch();
		break; 
	case SB_PRESET:
		editmode = EM_PRESET;
		last_preset_selection_rotstep = sysparams.curpreset;
		break;
	case SB_PARAMSA:
		if (edit_param >= P_LAST && last_edit_param < P_LAST)
			edit_param = last_edit_param, param_sticky = true;
		else
			param_sticky = false;
		if (edit_param<P_LAST && (edit_param %12)>=6) edit_param -=6;
		editmode = EM_PARAMSA;
		tapcount = 0;
		break;
	case SB_PARAMSB:
		if (edit_param >= P_LAST && last_edit_param < P_LAST)
			edit_param = last_edit_param, param_sticky = true;
		else
			param_sticky = false;
		if (edit_param<P_LAST && (edit_param % 12) < 6) edit_param += 6;
		editmode = EM_PARAMSB;
		break;
	}
}

bool isshortpress(void) {
	return (ticks() - last_time_shift_was_untouched) < 250;
}

void togglearp(void);

void on_shift_up(int release_button) {
	bool shortpress = isshortpress();
	
	if (editmode == EM_SAMPLE) {
		if (shortpress) {
			if (shift_down == SB_PARAMSA) {
				ramsample.pitched = !ramsample.pitched;
				ramtime[GEN_SAMPLE] = millis();
			} else
			if (shift_down == SB_PARAMSB) {
				ramsample.loop = (ramsample.loop+1)&3;
				ramtime[GEN_SAMPLE] = millis();
			}
			else
			if (shift_down != SB_RECORD && shift_down!=SB_PLAY) {
				recording_stop();
			}
		}
		return;
	}
	switch (shift_down) {
	case SB_PARAMSA:
	case SB_PARAMSB:
		editmode = EM_PLAY;
		last_edit_param = edit_param;
		if (!touched_main_area && !param_sticky) {
			edit_param = P_LAST;
			edit_mod = 0;
		}
		// these arent real params, so dont stick on them.
		if (edit_param == P_ARPONOFF || edit_param == P_LATCHONOFF) {
			edit_param = P_LAST;
			edit_mod = 0;
		}
		break;
	case SB_PREV: // prev
		//if (!touched_main_area && shift_down_time > 128 / 4 && prev_editmode == EM_PLAY) {
		//	togglearp();
		//	editmode = EM_PLAY;
		//}
		//else 
		if (!touched_main_area && shortpress) {
			if (isplaying()) {
			//	got_ui_reset = true; did it on note down already
			}
			else
				set_cur_step(cur_step - 1, !isplaying());
			editmode = EM_PLAY;
		}
		if (touched_main_area || prev_editmode==editmode)
			editmode = EM_PLAY;
		break;
	case SB_NEXT: // next
		if (!touched_main_area && shortpress) {
			set_cur_step(cur_step + 1, !isplaying());
			editmode = EM_PLAY;
		}
		if (touched_main_area || prev_editmode == editmode)
			editmode = EM_PLAY;
		break;
	case SB_PRESET: // preset
		if (touched_main_area || prev_editmode == editmode)
			editmode = EM_PLAY;
		break;
	case SB_PLAY:
		if (playmode == PLAY_PREVIEW )
			playmode = shortpress ? PLAYING: PLAY_STOPPED;
	
		break;
	case SB_RECORD:
		if (shortpress && recordingknobs==0)
			recording = !recording;
		recordingknobs = 0;
		break;
	case SB_CLEAR:
		if (!isplaying() && recording && editmode == EM_PLAY) {
			bool dirty = false;
			int q = (cur_step >> 4) & 3;
			FingerRecord* fr = &rampattern[q].steps[cur_step & 15][0];
			for (int fi = 0; fi < 8; ++fi, ++fr) {
				for (int k = 0; k < 8; ++k) {
					if (fr->pressure[k] > 0)
						dirty = true;
					fr->pressure[k] = 0;
					if (fi < 2) {
						s8* d = &rampattern[q].autoknob[(cur_step & 15) * 8 + k][fi];
						if (*d) {
							*d = 0; dirty = true;
						}
					}
				}

			}
			if (dirty)
				ramtime[GEN_PAT0 + ((cur_step >> 4) & 3)] = millis();
			set_cur_step(cur_step + 1, false);
		}
		
		break;
	}
	check_curstep();
}


int prev_prev_total_ui_pressure = 0;
int prev_total_ui_pressure = 0;
int total_ui_pressure = 0;

typedef struct FingerStorage {
	u8 minpos, maxpos;
	u8 avgvel;
} FingerStorage;
FingerStorage latch[8];
bool latch_valid;
void clearlatch(void) {
	memset(latch, 0, sizeof(latch));
	latch_valid = false;
}

	
static inline int randrange(int mn, int mx) {
	return mn + (((rand() & 255) * (mx - mn)) >> 8);
}



int param_eval_finger(u8 paramidx, int fingeridx, Finger* f);
u8 synthfingerdown_nogatelen_internal;
u8 synthfingersupress_nogatelen;
bool read_from_seq = false;


FingerRecord* readpattern(int fi) {
	if (rampattern_idx == cur_pattern && shift_down != SB_CLEAR) {
		return &rampattern[(cur_step >> 4) & 3].steps[cur_step & 15][fi];
	} // pattern is ok?
	return 0;
}

/////////////////// XXXXX MIDI HERE?
	// bitmask of 'midi pitch override' and 'midi pressure override'
	// midipitch
	// here: if real pressure, clear both
	// here: if pressure override, set it as midi channel aftertouch + midi note pressure
	// in plinky.c midi note down: inc next voice index; set both override bits; set voice midi pitch and channel and velocity
	// in plinky.c midi note up: clear pressure override bit
	// in the synth - override pitch if bit is set
u8 midi_pressure_override, midi_pitch_override;
u8 midi_notes[8];
u8 midi_velocities[8];
u8 midi_aftertouch[8];
u8 midi_channels[8] = { 255,255,255,255,255,255,255,255 };
u8 midi_chan_aftertouch[16];
s16 midi_chan_pitchbend[16];
u8 midi_next_finger;
u8 midi_lsb[32];
u8 find_midi_note(u8 chan, u8 note) {
	for (int fi = 0; fi < 8; ++fi) if (midi_notes[fi] == note && midi_channels[fi] == chan)
		return fi;
	return 255;
}
u8 find_midi_free_channel(void) {
	u8 numfingerdown = 0;
	
	for (int attempt = 0; attempt < 16; ++attempt) {
		u8 ch = (midi_next_finger++) & 7;
		bool fingerdown = touch_synth_getlatest(ch)->pressure > 0 && (midi_pressure_override&(1<<ch))==0; // synth_dst_finger>0, and midi_pressure_override bit is not set
		if (fingerdown) {
			if (attempt < 8) {
				numfingerdown++;
				if (numfingerdown == 8)
					return 255; // all fingers using channels! NO ROOM! TOUGH
			}
			continue;
		}
		if (attempt >=8 || midi_channels[ch] == 255)
			return ch;
	}
	// give up
	return (midi_next_finger++) & 7;
}

bool is_finger_an_edit_operation(int fi);

void finger_synth_update(int fi) {
	if (fi == 0) {
		total_ui_pressure = 0;
	}
	int bit = 1 << fi;
	int frame_ui = (finger_frame_ui-1) & 7;
	if (finger_ui_done_this_frame & bit)
		frame_ui = (frame_ui + 1) & 7;
//	int prevframe_ui = (frame_ui - 1) & 7;
	int prevprevframe_ui = (frame_ui - 2) & 7;
//	int prev4frame_ui = (frame_ui - 4) & 7;
	Finger* uif = &fingers_ui_time[fi][frame_ui];
//	Finger* uif_prev = &fingers_ui_time[fi][prevframe_ui];
	Finger* uif_prevprev = &fingers_ui_time[fi][prevprevframe_ui];
//	Finger* uif_prev4= &fingers_ui_time[fi][prev4frame_ui];
	Finger* synth_dst_finger = &fingers_synth_time[fi][finger_frame_synth];
//	Finger* synth_prev_finger = &fingers_synth_time[fi][(finger_frame_synth-1)&7];

	int p = uif->pressure;
//	int prevp = uif_prev->pressure;
	int prevprevp = uif_prevprev->pressure;
//	int prev4p = uif_prev4->pressure;
//	bool fingerup = (p <= 0 && prevprevp <= 0 && prev4p<=0);
	bool pressure_not_rapidly_decreasing = (p > (prevprevp)); // we do this BEFORE compensating for edit mode, so that we dont accidentally see it as increasing after edit ends
	if (editmode != EM_SAMPLE) {
		if ((fingerediting | prevfingerediting) & bit)
			p = -256;
		else if (shift_down == SB_CLEAR)
			p = -256;
		else if (is_finger_an_edit_operation(fi))
			p = -256;
	}


	//if (fi == 0)
	//	DebugLog("process finger0 pressure %d %f\n", p);

	// MIDI HERE

	
	if (p > 0) {
		midi_pressure_override &= ~bit;
		midi_pitch_override &= ~bit;
	}
	else if (midi_pressure_override & bit) {
		u8 chan = midi_channels[fi];
		p = 1+(midi_velocities[fi] + maxi(midi_aftertouch[fi], midi_chan_aftertouch[chan]))*16;
		pressure_not_rapidly_decreasing = (p > (prevprevp) ); // update for midi
	}

	/////////////////// 

	synth_dst_finger->pressure = p;
//	int total_ui_pressure_before_me = total_ui_pressure;
	total_ui_pressure += maxi(0, p);

	//if (uif->written) // written is only set if we are confident to update the position
		synth_dst_finger->pos = uif->pos;
	//else
	//	synth_dst_finger->pos = synth_prev_finger->pos;

	int phase0 = calcseqsubstep(0, 8);
	if (fi==0)
		read_from_seq = false;
	bool any_fingers_down = (total_ui_pressure > 0 || prev_total_ui_pressure > 0);
	if (fi==7 && shift_down!=SB_CLEAR && total_ui_pressure > 0 && total_ui_pressure >= prev_total_ui_pressure) {
		// update latch from ui state
		for (int fi=0;fi<8;++fi) {
			u8 maxpos = 0, minpos = 255, maxpressure = 0;
			Finger* f = fingers_synth_time[fi];
			for (int j = 0; j < 8; ++j, ++f) {
				u8 p = clampi((f->pos + 4) / 8, 0, 255);
				minpos = mini(p, minpos);
				maxpos = maxi(p, maxpos);
				u8 pr = clampi(f->pressure / 12, 0, 255);
				maxpressure = maxi(maxpressure, pr);
			}
			//if (avgvel > latch[fi].avgvel+1 || pressure_not_rapidly_decreasing)
			{
//				if (fi == 4) EmuDebugLog("latch pressure %d, tot %d prev %d\n", maxpressure, total_ui_pressure, prev_total_ui_pressure);
				latch[fi].avgvel = maxpressure;
				latch[fi].minpos = minpos;
				latch[fi].maxpos = maxpos;
			}
		}
		latch_valid = true;
	} // latch fingers down!

	// recording
	static u8 last_rec_step=255;
	static u8 last_rec_phase = 0;
	static u8 step_filled = 0;
	int q = (cur_step >> 4) & 3;
	FingerRecord* fr = &rampattern[q].steps[cur_step & 15][fi];
	bool want_recording_on = any_fingers_down && recording && rampattern_idx == cur_pattern ; // && (isplaying() || pressure_increasing)

	if (recording && rampattern_idx == cur_pattern && fi < 2) { // check for knob recording?
		int k = fi;
		float knob = adc_smooth[4 + k].y2;
		if (shift_down == SB_RECORD && fabsf(knob - knobbase[k]) > 0.01f)
			recordingknobs |= 1 << k;
	}
	if (shift_down == SB_CLEAR && recording)
		want_recording_on = true;
	if (want_recording_on) {
		if (last_rec_step != cur_step) {
			// new step! blank it out
		//	DebugLog("cear step %d (fi=%d)\r\n", cur_step, fi);
			for (int fi = 0; fi < 8; ++fi) {
				FingerRecord* fr = &rampattern[q].steps[cur_step & 15][fi];
				memset(fr, 0, sizeof(FingerRecord));
			}
			for (int k = 0; k < 2; ++k)
				if (recordingknobs&(1<<k)) for (int i = 0; i < 8; ++i)
					rampattern[q].autoknob[(cur_step & 15) * 8 + i][k] = 0;
			if (playmode != PLAYING && phase0 != 0) {
				// force the sequencer step clock back to the start of the step!
				phase0 = 0;
				ticks_since_step = 0;
				seq_divide_counter = 0;
			}
			step_filled = 0;
			last_rec_phase = phase0;
			last_rec_step = cur_step;
		}
		ramtime[GEN_PAT0 + ((cur_step >> 4) & 3)] = millis();
		int pressure = clampi((synth_dst_finger->pressure+512) / 12, 0, 255);
		int pos = clampi(synth_dst_finger->pos / 8, 0, 255);
		if (shift_down == SB_CLEAR) {
			if (fi<2)
				for (int i=0;i<8;++i)
					rampattern[q].autoknob[(cur_step & 15) * 8 + i][fi] = 0;
			pressure = 0;
		}
		if (phase0 < last_rec_phase)
			step_filled = 1;
		if (step_filled) {
			// scroll right! but dont record lift offs
			if (phase0!=last_rec_phase && (isplaying() || pressure_not_rapidly_decreasing)) {
				for (int i = 0; i < 7; ++i) fr->pressure[i] = fr->pressure[i + 1];
				for (int i = 0; i < 3; ++i) fr->pos[i] = fr->pos[i + 1];
				fr->pressure[7] = pressure;
				fr->pos[3] = pos;
			}
		}
		else {
			// fill from left!
			fr->pressure[phase0] = maxi(fr->pressure[phase0], pressure);
			for (int phase = phase0 + 1; phase < 8; ++phase)
				fr->pressure[phase] = 0;
			for (int phase = phase0 / 2; phase < 4; ++phase)
				fr->pos[phase] = pos;

		}
	}
	if (recording && rampattern_idx == cur_pattern && fi < 2 && recordingknobs>0) {
		int k = fi;
		float knob = adc_smooth[4 + k].y2;
		if (recordingknobs & (1 << k)) {
			if (isplaying())
				rampattern[q].autoknob[(cur_step & 15) * 8 + phase0][k] = clampi((int)(knob * 127.f), -127, 127);
			else for (int i = 0; i < 8; ++i)
				rampattern[q].autoknob[(cur_step & 15) * 8 + i][k] = clampi((int)(knob * 127.f), -127, 127);
			ramtime[GEN_PAT0 + ((cur_step >> 4) & 3)] = millis();
		}
	}

	if (fi == 7) {
		last_rec_step = cur_step;
		last_rec_phase = phase0;
		if (!want_recording_on)
			last_rec_step = 255;
	}
	if (!any_fingers_down) {
		
		// read from pattern
		FingerRecord *fr = readpattern(fi);
		if (fr && (isplaying() || seq_rhythm.did_a_retrig)) {
			read_from_seq = true;

			int pr = fr->pressure[phase0] * 12;
			int pos = fr->pos[phase0 / 2];
			synth_dst_finger->pressure = (pr && !seq_rhythm.supress) ? randrange(pr, pr+12)-512 : -1024;
			synth_dst_finger->pos = randrange(pos * 8, pos * 8 + 8);
			synth_dst_finger->written = 1;
		}
		// read from latch
		bool latchon = ((rampreset.flags & FLAGS_LATCH)) ;
		if (latch_valid && latchon && shift_down != SB_CLEAR) {
			int latchvel = latch[fi].avgvel * 12;
			if (latchvel >= synth_dst_finger->pressure) {
				int mnpos = latch[fi].minpos * 8 + 2, mxpos = latch[fi].maxpos * 8 + 6;
				int avgpos = (mnpos + mxpos) / 2;
				int range = (mxpos - mnpos) / 4;
				synth_dst_finger->pos = randrange(avgpos-range,avgpos+range);
				synth_dst_finger->pressure = latchvel ? randrange(latchvel - 12, latchvel) : -1024;
				synth_dst_finger->written = 1;
			}
		}
		
	}

	float gate_cv = adc_smooth[7].y2;
	synth_dst_finger->pressure = (int)((synth_dst_finger->pressure + 256) * gate_cv) - 256;

	//if (fi == 0)
	//	DebugLog("finger0 pressure %d %f\n", synth_dst_finger->pressure, gate_cv);


	//if (fi==7)
	//	DebugLog("frame_ui %d frame_synth %d uif %d synth %d\n", finger_frame_ui, finger_frame_synth, uif->pressure, synth_dst_finger->pressure);

	
	if (synth_dst_finger->pressure > 0) {
		synthfingerdown_nogatelen_internal |= bit;
	}
	else {
		synthfingerdown_nogatelen_internal &= ~bit;
		synthfingersupress_nogatelen &= ~bit;
	}
	// /////////////////////////////////////////
	int gatelen = param_eval_finger(P_GATE_LENGTH, fi, synth_dst_finger) >> 8;
	if (gatelen < 256 && !isgrainpreview()) {
		int phase = (rampreset.flags & FLAGS_ARP) ? calcarpsubstep(0, 256) : calcseqsubstep(0, 256);
		int suppress_seq=false;
		if (read_from_seq)
			suppress_seq = (!seq_rhythm.did_a_retrig && !isplaying());
		else
			suppress_seq = (synthfingersupress_nogatelen & bit);
		if (rampreset.flags & FLAGS_ARP)
			suppress_seq |= !arp_rhythm.did_a_retrig;
		//suppress_seq = !arp_rhythm.did_a_retrig;
		if (phase > gatelen || suppress_seq) {
			synth_dst_finger->pressure = -1024;
			if (!read_from_seq && !(rampreset.flags & FLAGS_ARP)) {
				if (synthfingerdown_nogatelen & bit)
					synthfingersupress_nogatelen |=bit; // once we've supressed, we keep supressing!
			}
		}
	}
	else {
		if (!isplaying() && read_from_seq && !seq_rhythm.did_a_retrig) {
			synth_dst_finger->pressure = -1024;
		}
	}
	// /////////////////////////////////////////

	/* test hold fingers down 
	if (fi > 0 && fi < 5) {
		synth_dst_finger->pos = 128;
		synth_dst_finger->pressure = (fi==1)?2047:1024;
	}
	*/

#define ENABLE_TEST_TONE 0
	if (ENABLE_TEST_TONE) if (fi == 5) {
		synth_dst_finger->pressure = 1500;
		synth_dst_finger->pos = 128;
	}
	/*
	static int log3=0;
	if (log3>0 && fi==3) {
		DebugLog("%d %d (%d)\r\n",synth_dst_finger->pressure, synth_dst_finger->pos, written);
		if (synth_dst_finger->pressure<=0)
			log3--;
	}*/

	static s16 prevpressure[8];

	if (prevpressure[fi]<= 0 && synth_dst_finger->pressure > 0) {
		// the finger has just gone down! lets go fix a bunch of positions in the history
		Finger* of = fingers_synth_time[fi];
		int newp = synth_dst_finger->pos;
		int numfix=0;
		for (int h = 0; h < 8; ++h, of++) if (h != finger_frame_synth) {
			if (of->pressure <= 0) {
				of->pos = (of->pos & 3) ^ newp;
				numfix++;
			}
		}
		/*
		DebugLog("fi %d fixed %d vol=%d -> %d pos = %d\r\n", fi, numfix, synth_prev_finger->pressure, synth_dst_finger->pressure, newp);
		sort8((int*)fingers_synth_sorted[fi], (int*)fingers_synth_time[fi]);
		for (int i=0;i<8;++i) DebugLog("%d ",fingers_synth_sorted[fi][i].pos);
		DebugLog("\r\n");
		log3=10;
		*/
	}
	prevpressure[fi] = synth_dst_finger->pressure;

	sort8((int*)fingers_synth_sorted[fi], (int*)fingers_synth_time[fi]);

	

}

void finger_editing(int fi, int frame);


#ifdef EMU
int htsc;
typedef struct TSC_IOConfigTypeDef {
	u32 ChannelIOs;
	u32 SamplingIOs;
}TSC_IOConfigTypeDef;
typedef int TSC_GroupStatusTypeDef;
#define TSC_GROUP1_IO1 (1<<0)
#define TSC_GROUP1_IO2 (1<<1)
#define TSC_GROUP1_IO3 (1<<2)
#define TSC_GROUP1_IO4 (1<<3)
#define TSC_GROUP2_IO1 (1<<4)
#define TSC_GROUP2_IO2 (1<<5)
#define TSC_GROUP2_IO3 (1<<6)
#define TSC_GROUP2_IO4 (1<<7)
#define TSC_GROUP3_IO1 (1<<8)
#define TSC_GROUP3_IO2 (1<<9)
#define TSC_GROUP3_IO3 (1<<10)
#define TSC_GROUP3_IO4 (1<<11)
#define TSC_GROUP4_IO1 (1<<12)
#define TSC_GROUP4_IO2 (1<<13)
#define TSC_GROUP4_IO3 (1<<14)
#define TSC_GROUP4_IO4 (1<<15)
#define TSC_GROUP5_IO1 (1<<16)
#define TSC_GROUP5_IO2 (1<<17)
#define TSC_GROUP5_IO3 (1<<18)
#define TSC_GROUP5_IO4 (1<<19)
#define TSC_GROUP6_IO1 (1<<20)
#define TSC_GROUP6_IO2 (1<<21)
#define TSC_GROUP6_IO3 (1<<22)
#define TSC_GROUP6_IO4 (1<<23)
#define TSC_GROUP7_IO1 (1<<24)
#define TSC_GROUP7_IO2 (1<<25)
#define TSC_GROUP7_IO3 (1<<26)
#define TSC_GROUP7_IO4 (1<<27)

#define ENABLE 1
#define TSC_GROUP_COMPLETED 1
u32 _chanios;
void HAL_TSC_IOConfig(int* htsc, TSC_IOConfigTypeDef* config) { _chanios = config->ChannelIOs; }
void HAL_TSC_IODischarge(int *htsc, int enable) {}
void HAL_TSC_Start(int* htsc) {}
void HAL_TSC_Stop(int* htsc) {}
TSC_GroupStatusTypeDef HAL_TSC_GroupGetStatus(int* htsc, int groupidx) {
	return TSC_GROUP_COMPLETED;
}
short HAL_TSC_GroupGetValue(int* htsc, int groupidx) {
	// hacked so groupidx is actually 0-35 sensor idx
	groupidx %= 18;
	int fingeridx = groupidx / 2;
	extern int emutouch[9][2];
	int pos = emutouch[fingeridx][1];
	int pressure = emutouch[fingeridx][0];
	int a = pressure * (2048 - pos);
	int b = pressure * (pos);
	if (fingeridx == 8) {
		int t = a; a = b; b = t; // oops I swapped the pins
	}
	if (groupidx&1) a = b;
	a >>= 10;
	a += 2048;
//	if (groupidx == 0)
//		printf("hello finger 0 %d %d = %d\n", pos, pressure, (2048 * 2048) / a);
	a += rand() & 31;
	return (2048 * 2048) / a;
}
#endif

#define FF0 TSC_GROUP1_IO2+TSC_GROUP4_IO2
#define FF1 TSC_GROUP2_IO2+TSC_GROUP5_IO2
#define FF2 TSC_GROUP3_IO3+TSC_GROUP6_IO2
#define FF3 TSC_GROUP1_IO3+TSC_GROUP4_IO3
#define FF4 TSC_GROUP2_IO3+TSC_GROUP5_IO3
#define FF5 TSC_GROUP3_IO4+TSC_GROUP6_IO3
#define FF6 TSC_GROUP1_IO4+TSC_GROUP4_IO4
#define FF7 TSC_GROUP2_IO4+TSC_GROUP5_IO4
#define FF8a TSC_GROUP7_IO2
#define FF8b TSC_GROUP7_IO3
#define SS0 TSC_GROUP1_IO1+TSC_GROUP4_IO1
#define SS1 TSC_GROUP2_IO1+TSC_GROUP5_IO1
#define SS2 TSC_GROUP3_IO2+TSC_GROUP6_IO1
#define SS3 TSC_GROUP1_IO1+TSC_GROUP4_IO1
#define SS4 TSC_GROUP2_IO1+TSC_GROUP5_IO1
#define SS5 TSC_GROUP3_IO2+TSC_GROUP6_IO1
#define SS6 TSC_GROUP1_IO1+TSC_GROUP4_IO1
#define SS7 TSC_GROUP2_IO1+TSC_GROUP5_IO1
#define SS8a TSC_GROUP7_IO1
#define SS8b TSC_GROUP7_IO1

int raw_isdown(int srcidx) {
	int a = finger_cap(srcidx * 2) - finger_mincap(srcidx*2);
	int b = finger_cap(srcidx * 2 + 1) - finger_mincap(srcidx * 2 + 1);
	return (a + b > 1000) ? 1 : 0;
}

static inline int finger_rawpos(int a, int b) {
	return clampi(((b - a) << 12) / (a+b+ 1), -32767, 32767);
}

static inline int finger_rawpressure(int a, int b) {
	return clampi(a + b, 0, 32767);
}

void update_finger(int srcidx) {
	int dstidx = srcidx;
	if (dstidx >= 9) dstidx -= 9;
	int a = finger_cap(srcidx*2);
	int b = finger_cap(srcidx*2+1);
	int amin = finger_mincap(srcidx * 2);
	int bmin = finger_mincap(srcidx * 2 + 1);
	int rawpressure = finger_rawpressure(a-amin,b-bmin);
	int rawpos = finger_rawpos(a, b);
	int pos = rawpos, pressure = rawpressure;

//	if (pressure>500) DebugLog("%d %d\r\n", pressure, pos);

	// scale pos and pressure by calibration
	const CalibResult* c = &calibresults[srcidx];
	if (c->pressure[0] != 0) { // if we dont have any calibration data, we just pass thru the raw values
		int prev = c->pos[0] - (c->pos[1] - c->pos[0]); // extrapolate below 0
		int reversed = c->pos[7] < c->pos[0];
		int maxp;
		if ((rawpos < prev) ^ reversed) {
			pos = -128;
			maxp = c->pressure[0];
		}
		else {
			pos = 8 * 256 + 128;
			maxp = c->pressure[7];
			for (int x = 0; x <= 8; ++x) {
				int next;
				if (x == 8) next = c->pos[7] + (c->pos[7] - c->pos[6]); // extrapolate
				else next = c->pos[x];
				if ((rawpos < next) ^ reversed) {
					int diff = next-prev;

					int t = diff ? ((rawpos - prev) * 256) / diff : 0;
					pos = x * 256 - 128 + t;
					int prevp = c->pressure[maxi(0, x - 1)];
					int nextp = c->pressure[mini(7, x)];
					maxp = prevp + (((nextp - prevp) * t) >> 8);
					break;
				}
				prev = next;
			}
		}
		if (maxp < 1000) maxp = 1000;
		pressure = (rawpressure * 4096) / maxp - 2048;
	//	if (pressure>100) DebugLog("%d %d\r\n", pressure, pos);
	}
	
	int frame = finger_frame_ui;
	ASSERT(frame >= 0 && frame < 8);
	int prevframe = (frame - 1) & 7;
	int fi = dstidx;
	Finger* uif = &fingers_ui_time[fi][frame];
	Finger* prev_uif = &fingers_ui_time[fi][prevframe];
	uif->pressure = pressure;
	//bool written = 0;
	if (pressure > 0 && pressure > prev_uif->pressure - 128) { // not significantly lifting finger off
		uif->pos = clampi(pos, 0, 2047);
		//written = 1;
	}
	else
		uif->pos = prev_uif->pos;
	uif->written = 1;//written;
	// else dont even write pos! let it be old random values



	if (enable_audio <= EA_OFF) {
		return;
	}
	
	if (fi == 8) {
		Finger* oldf = &fingers_ui_time[fi][(frame-2)&7];
		int button = uif->pos>>8;
		if (shift_down != SB_NOT_PRESSED) {
			int oldpos = shift_down * 256 + 128;
			if (abs(oldpos - uif->pos) < 192) // a bit of hysteresis
				button = shift_down;
		}
		int midbutton = prev_uif->pos>>8;
		int oldbutton = oldf->pos>>8;
		bool valid=oldf->pressure>700 && uif->pressure>700 && abs(uif->pos-oldf->pos)<60;
		if (shift_down == SB_NOT_PRESSED)
			valid&= oldbutton==midbutton && button==oldbutton;

		if (valid) {
			if (shift_down != button) {
				// check for ghosted presses
				Finger* f0 = touch_ui_getwriting(button);
				Finger* fl = touch_ui_getwriting(maxi(0, button - 1));
				Finger* fr = touch_ui_getwriting(mini(7, button + 1));
				if (
					(f0->pressure > 256 && f0->pos >= 6 * 256) ||
					(fl->pressure > 256 && fl->pos >= 7 * 256) ||
					(fr->pressure > 256 && fr->pos >= 7 * 256))
					shift_down = SB_GHOSTED; // ghosted press! they have a finger on a nearby strip, maybe it was an accident.
				else {
					shift_down = button;
					shift_down_time = 0;
					on_shift_down();
				}
			}
		} else if (uif->pressure<=0) {
			if (shift_down >= 0)
				on_shift_up(button);
			shift_down = SB_NOT_PRESSED;
		}
		if (shift_down >= 0) {
			shift_down_time++;
			on_shift_hold(button);
		}
		if (uif->pressure <= 0)
			last_time_shift_was_untouched = ticks();

		//DebugLog("%d %d %02x\r\n", maxi(0,pressure), valid ? button*256 : -256, finger_stepmask);
		
	} 
	else {
		finger_editing(fi, finger_frame_ui);
		finger_ui_done_this_frame |= 1 << fi;
	}
}

u32 fingererror=0;

void touch_update(void) {
again:
	waitcounter++;
#define MAX_STEPS 13 // 13 steps that generate 36 sensor readings.
	static u8 const sensororder[MAX_STEPS][8] = {
			{ 0, 2, 4, 1, 3, 5, 16}, // first step does a group of first three touch + half of strip
			{ 6, 8,10, 7, 9,11, 17}, // second step does next 3 touch in a group + other half of step
			{12,14,    13,15},			// third step does last 2 touch in a group
			{18,19},	// remaining steps do single fingers at a time
			{20,21},
			{22,23},
			{24,25},
			{26,27},
			{28,29},
			{30,31},
			{32,33},
			{34},{35} }; // this is actually two steps, doh pin wiring
	// the bitmask version of the above table:
	static u32 const chansio[MAX_STEPS] = {FF0 + FF1 + FF2 + FF8a,FF3 + FF4 + FF5 + FF8b, FF6 + FF7,	FF0,FF1,FF2,FF3,FF4,FF5,FF6,FF7,FF8a,FF8b};
	static u32 const sampio[MAX_STEPS] =  {SS0 + SS1 + SS2 + SS8a,SS3 + SS4 + SS5 + SS8b, SS6 + SS7,	SS0,SS1,SS2,SS3,SS4,SS5,SS6,SS7,SS8a,SS8b };
	if (finger_state == 0) {
		TSC_IOConfigTypeDef config={0};
		config.ChannelIOs= chansio[finger_step];
		config.SamplingIOs=sampio[finger_step];
		HAL_TSC_IOConfig(&htsc, &config);
		HAL_TSC_IODischarge(&htsc, ENABLE);
		finger_state++;
		return;
	} 
	if (finger_state == 1) {
		finger_state++;
		//return; // more discharge time !
	}
	if (finger_state == 2) {
		HAL_TSC_Start(&htsc);
		finger_state++;
		return;
	}
	u32 chans = chansio[finger_step];
	int subchan = 0;
	bool errorstate=false;
#ifndef EMU
	if (HAL_TSC_GetState(&htsc)==HAL_TSC_STATE_ERROR) {
		errorstate=true;
	}
#endif
	for (int grp = 0; grp < 7; ++grp) {
		if (!(chans & (0xf << (grp * 4))))
			continue;
		TSC_GroupStatusTypeDef status = HAL_TSC_GroupGetStatus(&htsc, grp);
		int oidx=sensororder[finger_step][subchan++];
		if (status != TSC_GROUP_COMPLETED) {
			if (errorstate) {
				fingererror|=(1<<oidx);
			}
			else
				return;// not done yet
		}
		else {
			fingererror&=~(1<<oidx);
		}
		short v = finger_raw[oidx] = (1<<23)/maxi(129,
#ifdef EMU
			HAL_TSC_GroupGetValue(&htsc, oidx));
#else
			HAL_TSC_GroupGetValue(&htsc, grp));
#endif
//		if (oidx == 3 * 2 + 1) v = finger_min[1]; FAKE AN ERROR
//		if (oidx == 4) v = finger_min[3]; // FAKE AN ERROR
		if (v > finger_max[oidx])
			finger_max[oidx] = v;
		if (v < finger_min[oidx])
			finger_min[oidx] = v;
		//if (oidx == 34 || oidx == 35 || oidx == 16 || oidx == 17) {
		//	DebugLog("%d = %d\n", oidx, v);
		//}
	}
	prevfingerediting = fingerediting;
	HAL_TSC_Stop(&htsc);
	// update fingers and decide next step
	bool calib = (enable_audio == EA_OFF);
	switch (finger_step) {
	case 0: { // group of fingers 0,1,2 plus half of strip
		finger_stepmask = 7; // always do the first 3 steps, doh
		bool d0 = raw_isdown(0), d1 = raw_isdown(1), d2 = raw_isdown(2), d8 = raw_isdown(8);
		int numdown = d0 + d1 + d2 + d8*2;
		if (numdown <= 1 || calib) {
			update_finger(0);
			update_finger(1);
			update_finger(2);
			// do the shift finger in the next one
		}
		else {
			if (d0) finger_stepmask |= 1 << (3 + 0); else update_finger(0);
			if (d1) finger_stepmask |= 1 << (3 + 1); else update_finger(1);
			if (d2) finger_stepmask |= 1 << (3 + 2); else update_finger(2);
		}
	}
	break;
	case 1: { // group of fingers 3,4,5 plus half of strip
		bool d3 = raw_isdown(3), d4 = raw_isdown(4), d5 = raw_isdown(5), d8 = raw_isdown(8);
		int numdown = d3 + d4 + d5 + d8 * 2;
		if (numdown <= 1 || calib) {
			update_finger(3);
			update_finger(4);
			update_finger(5);
			update_finger(8);
		}
		else {
			if (d3) finger_stepmask |= 1 << (3 + 3); else update_finger(3);
			if (d4) finger_stepmask |= 1 << (3 + 4); else update_finger(4);
			if (d5) finger_stepmask |= 1 << (3 + 5); else update_finger(5);
			if (d8) finger_stepmask |= (1 << (3 + 8)) + (1 << (3 + 9)); else update_finger(8); // strip is two steps alas
		}
	}
	break;
	case 2: { // group of fingers 6,7
		bool d6 = raw_isdown(6), d7 = raw_isdown(7);
		int numdown = d6 + d7;
		if (numdown <= 1 || calib) {
			update_finger(6);
			update_finger(7);
		}
		else {
			if (d6) finger_stepmask |= 1 << (3 + 6); else update_finger(6);
			if (d7) finger_stepmask |= 1 << (3 + 7); else update_finger(7);
		}
	}
	break;
	default: // 3...10: individual fingers
		update_finger(9 + finger_step - 3);
		break;
	case 11: // half of strip. do nothing :(
		break;
	case 12: // second half of strip! yay!
		update_finger(9 + 8);
		break;
	}
	finger_state = 0;
	// find next step that is active
	while (finger_step < MAX_STEPS) {
		finger_step++;
		if (calib)
			break;
		if (finger_stepmask & (1 << finger_step))
			break;
	}

	// move on to next step!
	if (finger_step == MAX_STEPS) {
		finalwait = waitcounter;
		waitcounter = 0;
		finger_step = 0;
		finger_frame_ui = (finger_frame_ui + 1) & 7;
		finger_ui_done_this_frame=0;
	}
	goto again;
}

