//example EEG-led concatenation; this depends on a brain interface to OSC sending program provided by Chris Kiefer, Matthew Yee-King, Mick Grierson

//HOW MANY FEATURES?
//1 channel

//WHAT IS FRAME RATE? 
//100 Hz approx

//receiving continuous stream of EEG values; need to store along with position in track playing back 

//doesn't work; too noisy, no reliability of recall  :  (   

//unsurprising that can't read your mind from one channel!!!

(

SynthDef(\eegplayback, {|buffer=0, rate=10|
	var position; 
	var sound; 
	
	sound = PlayBuf.ar(2,buffer,BufRateScale.kr(buffer),loop:0); 
	
	//keep language updated about current position at granularity of 10 times a second
	SendTrig.kr(Impulse.kr(rate), 0, Sweep.ar(Impulse.ar(0),1.0));
	
	Out.ar(0,sound);
}).add;

SynthDef(\eegrecall, {|buffer=0, pos=0, t_trig=0|
	var position; 
	var sound; 
	
	sound = PlayBuf.ar(2,buffer,BufRateScale.kr(buffer),t_trig,pos*BufSampleRate.ir(buffer),loop:1); 

	Out.ar(0,sound);
}).add;

) 

//two modes: recording initial tags of EEG data from passive listening to track

(
b= Buffer.read(s,"/Users/nickcollins/Desktop/Beethoven8mvt4.wav");

//b.numFrames/44100
//b.duration

//size of eeg feature vector; will need to alter code below if multiple features per frame
~lastn = 50; 

)

//stage 1

(
var eeglist; 
var first= true; 

//list of times and associated EEG data
~list= List[]; 

eeglist = List[]; 

o.remove;
o = OSCresponder(s.addr,'/tr',{ arg time,responder,msg;
	
	//[\trig, time,responder,msg].postln;
	
	if(not(first),{
			
	//time in sound file
	//~list = [msg[3],eeglist]
	
	if(eeglist.size>~lastn,{
		
		//may discard intermediate values; depends on frame rate of EEG
		eeglist = eeglist[0..(~lastn-1)];
		
		~list.add([msg[3],eeglist.copy]); 
		
	}); 
	
	},{
		
	first = false; 	
	});
	
	//eeglist.clear; 
	
}).add; 

m.remove;
m = OSCresponder(nil,'/brainCOM1',{ arg time,responder,msg;
	
	//[\eeg, time,responder,msg].postln;
	
	//what format data in?
	//most recent at front 
	eeglist.addFirst(msg[5]);
	
}).add; 
)

//go
(
Synth(\eegplayback,[\buffer, b]);

)

o.remove;
m.remove;

~list

///////////////////////////////////
//stage 2

(
var eeglist; 
var update; 
var comparisonfunction; 
var maxsearchsize = 1000; //only check first 100 for now for efficiency 

//larger the database, more time this will take; may need to reserve size for now until have more efficient architecture
comparisonfunction = {|test, database|
var closesttime=0.0, minscore= 9999999.9; 
var searchsize = min(database.size, maxsearchsize);

//could just search a proportion of these? 
searchsize.do{|i|  
	var item = database[i];  
	var timenow = item[0]; 
	var data = item[1]; 
	var diff; 
	
	//timenow.postln;
	//data.postln;
	
	//Manhattan metric for speed
	diff = (test-data).abs.sum; 
	
	//[\diff, diff].postln;
	
	if (diff<minscore,{minscore = diff; closesttime= timenow;});
};	

closesttime

};

eeglist = List[]; 

a = Synth(\eegrecall,[\buffer, b]);

o.remove; 
o = OSCresponder(nil,'/brainCOM1',{ arg time,responder,msg;
	
	//[\eeg, time,responder,msg].postln;
	
	//what format data in? 
	eeglist.addFirst(msg[5]);
	
	//once have X values, test against database, finding closest match; set synth to play from matching position
	
}).add; 

update = {
	
	inf.do{
	
	var timepos; 
	
	if(eeglist.size>~lastn) {
		
		eeglist = eeglist[0..(~lastn-1)];
	
		timepos = comparisonfunction.(eeglist,~list); 
		
		a.set(\pos, timepos, \t_trig, 1); 
	};

	//update synth
	
	//
	0.5.wait;
	}
}.fork(SystemClock); 

)

o.remove; 

//basic tests 

(

o.remove;
o = OSCresponder(nil,'/brainCOM1',{ arg time,responder,msg;
	
	//[\eeg, time,responder,msg].postln;
	
	msg[5].postln;
	
	//Neurosky
	//3 = concentration
	//4 = meditation
	//5 = time domain 
	//6 spec offset
	//7 spectral data 
	
	//what format data in?
	//most recent at front 
	//eeglist.addFirst(msg);
	
}).add; 

)