(
var createTransportPattern;

~hdpBusArray = 16.collect({Bus.audio(s, 1)});

Event.addEventType(\osc, {
	if (~addr.postln.notNil) {
		~msg.postln;
		~addr.sendMsg(~path, *~msg);
	};
});


//create synth defs
~bergerCreateSynthsForLive = {var b;
	// this creates a different waveform for each sonification of each part
	Buffer.freeAll(s);
	b = 8.collect({var buf = Buffer.alloc(s, 512, 1); buf.sine1(1.0 / 5.collect({arg i; pow(i + 1, 5.0.rand + 1)}), true, true, true)});

	SynthDef(\berger, {arg freq, amp = 0.2, del = 5, gate = 1, sustain = 1, buf = 0, out = 0;
		Out.ar(out, Osc.ar(Select.kr(buf, b), freq, 0, amp) * EnvGen.kr(Env.adsr(0.05, sustain, 0.25, 0.1), gate, 1, 0, 1, doneAction: 2));
	}).add;
};

~robinsonCreateSynthsForLive = {var b;
	Buffer.freeAll(s);
	b = 8.collect({var buf = Buffer.alloc(s, 512, 1); buf.sine1(1.0 / 5.collect({arg i; pow(i + 1, 5.0.rand + 1)}), true, true, true)});

	SynthDef(\robinson, {arg freq, amp = 0.2, del = 5, gate = 1, sustain = 1, buf = 0, out = 0;
		Out.ar(out, Osc.ar(Select.kr(buf, b), freq, 0, amp) * EnvGen.kr(Env.sine(sustain + 0.01), gate, 1, 0, 1, doneAction: 2));
	}).add;
};

~penroseCreateSynthsForLive = {var b;
	Buffer.freeAll(s);
	b = 8.collect({var buf = Buffer.alloc(s, 512, 1); buf.sine1(1.0 / 5.collect({arg i; pow(i + 1, 5.0.rand + 1)}), true, true, true)});

	SynthDef(\penrose_ins, {arg freq, amp = 0.2, del = 5, gate = 1, sustain = 1, buf = 0, out = 0;
		Out.ar(out /*10 + buf*/, Osc.ar(Select.kr(buf, b), freq, 0, amp) * EnvGen.kr(Env.adsr(1, sustain - 0.3, 0.7, 0.5), gate, 1, 0, 1, doneAction: 2));
	}).add;

	SynthDef(\penrose_fades, {arg subSeqLength, gate = 1, ins = 0, out = 0;
		Out.ar(out, In.ar(10 + ins) * EnvGen.kr(Env.new([0.4, 0.8, 0.8, 0.4, 0], [3, subSeqLength - 6, 3, 0.1], \sine), gate, 1, 0, 1, doneAction: 2));
	}).add;
};

~ammannCreateSynthsForLive = {var b;
	Buffer.freeAll(s);
	b = 6.collect({var buf = Buffer.alloc(s, 512, 1); buf.sine1(1.0 / 5.collect({arg i; pow(i + 1, 5.0.rand + 1)}), true, true, true)});

	SynthDef(\ammann, {arg freq, amp, del = 5, gate = 1, sustain = 1, buf = 0, out = 0;
		Out.ar(out, Osc.ar(Select.kr(buf, b), freq, 0, amp) * EnvGen.kr(Env.adsr(0.05, sustain, 0.5, 0.1), gate, 1, 0, 1, doneAction: 2));
	}).add;
};

~kariCreateSynthsForLive = {
	SynthDef(\kariEnsemble, {arg freq = 110, amp = 1, gate = 1, sustain = 1, dur = 1, out = 0;
		Out.ar(out, SinOsc.ar(freq, 0, 0.1) *
			pow(EnvGen.kr(Env.sine(sustain, amp / 5), gate, 1, 0, 1, doneAction: 2), 0.5) * 0.25);
	}).add;

	SynthDef(\kariBass, {arg freq = 110, amp = 1, gate = 1, sustain = 1, dur = 1, out = 0;
		Out.ar(out, SinOsc.ar(freq, 0, 0.1) *
			EnvGen.kr(Env.adsr(1, dur - 1.3, 0.25, 0.3, 1), gate, doneAction: 2) * 1);
	}).add;

	SynthDef(\kariNoise, {arg snd = 0, amp = 1, gate = 1, sustain = 1, dur = 1, out = 0;
		Out.ar(out, Select.ar(snd, [WhiteNoise.ar(0.5), DC.ar(0), BrownNoise.ar(0.7)]) *
			EnvGen.kr(Env.asr(dur, amp / 5, 3), gate, doneAction: 2) * 0.3);
	}).add;
};

//sine tones for sonification
~jaendelCreateSynthsForLive = {
	SynthDef(\jaendel, {arg freq, col, attack, sustain, decay, del = 5, gate = 1, pan = 0, pIndex = 0;
		var env;
		env = EnvGen.kr(Env.new([0, 1, 1, 0], [attack, sustain, decay], \sin), 1, 1, 0, 1, doneAction: 2);
		//color is mapped to panning similar to the score which maps to different ways of playing the same note
		Out.ar(pIndex * 2, SinOsc.ar(freq, 0, (((col >= 1) * (col <= 1)) + (col >= 3)).lag(0.3) * env * 0.05));
		Out.ar(pIndex * 2 + 1, SinOsc.ar(freq, 0, (col >= 2).lag(0.3) * env * 0.05));
	}).add;
};


//create patterns
~bergerCreatePatternsForLive = {arg seqs, group;
	var pBinds, cleanSeqs;
	cleanSeqs = [seqs[5], seqs[6], seqs[7], seqs[0], seqs[1], seqs[2]];
	pBinds = seqs.collect({arg r, i;
		Pbind(\instrument, \berger,
			//\buf, Pseq((i - ((0..(r.slice(nil, 2).flat.size - 1)) / 10).trunc.asInteger) % 8),
			\group, group,
			\buf, i,
			\out, ~hdpBusArray[i],
			//\freq, Pseq(55 * r[1]),
			\freq, Pseq((55 * r[1]).cpsmidi.round(0.5).midicps),
			\dur, Pseq(r[0]),
			\sustain, Pseq(r[0]),
			\amp, Pseq(r[2] / 1) //4)
		)
	});
};

~robinsonCreatePatternsForLive = {arg seqs, group;
	var pBinds;
	seqs = File.readAllString((~dir +/+ "music_data" +/+ "robinson.txt").standardizePath).interpret;
	pBinds = seqs[0].collect({arg r, i;
		Pbind(\instrument, \robinson,
			\buf, i,
			\group, group,
			\out, ~hdpBusArray[i],
			\freq, Pseq(12.midicps * r.slice(nil, 1)),
			\dur, Pseq(r.slice(nil, 0) * 0.25 /* * (3/4)*/),
			\sustain, Pseq(r.slice(nil, 0) * 0.25 /* * (3/4)*/),
			\amp, Pseq(r.slice(nil, 2) * 2)
		)
	});
};

~penroseCreatePatternsForLive = {arg seqs, group;
	var finSeqs, finFades;
	finSeqs = seqs[0].collect({arg r, i;
		Pbind(\instrument, \penrose_ins,
			\group, group,
			\buf, i,
			\out, ~hdpBusArray[i],
			//\freq, Pseq((24.midicps * r[1]).cpsmidi.round(0.5).midicps),
			\freq, Pseq((24.midicps * r[1])),
			\dur, Pseq(r[0] * 0.25 /* * 0.125*/),
			\sustain, Pseq(r[0] * 0.25 /* * 0.125*/),
			\amp, Pseq(((r[2] * 0.25) / pow(1.5, ((i % 6) + 1))))
		)
	});

	/*
	finFades = seqs[2].collect({ arg r, i;
		Pbind(\instrument, \penrose_fades,
			\group, group,
			\out, ~hdpBusArray[i],
			\dur, Pseq(r * 0.125),
			\subSeqLength, Pseq(r * 0.125),
			\ins, i
		)
	});
	*/

	finSeqs /*++ finFades*/;
};

~ammannCreatePatternsForLive = {arg seqs, group;
	var finSeqs;
	finSeqs = seqs.collect({arg r, i;
		Pbind(\instrument, \ammann,
			//\buf, Pseq((i - ((0..(r.slice(nil, 2).flat.size - 1)) / 10).trunc.asInteger) % 8),
			\group, group,
			\buf, i,
			\out, ~hdpBusArray[i],
			\freq, Pseq((55 * r.slice(nil, 2).flatten).cpsmidi.round(0.5).midicps),
			\dur, Pseq(r.slice(nil, 0).flat),

			//this is a bit tricky it makes it so that each note goes to the next
			//(ignoring the old sustain value)
			//but the rests are kept to make it clear where the note falls in each measure
			//for the transcriber

			\sustain, Pseq(
				[r.slice(nil, 0).flat.drop(1).clump(2).size.collect({Rest(1)}),
					r.slice(nil, 0).flat.drop(1).clump(2).collect({arg item; item.sum})].flop.flat
			),

			//\sustain, Pseq(r.slice(nil, 1).flat  ),
			\amp, Pseq(r.slice(nil, 3).flat * 0.5)
		)
	});

	finSeqs;
};

~kariCreatePatternsForLive = {arg dirs, group;
	[
		Pbind(\instrument, \kariNoise,
			\group, group,
			\out, 5,
			\dur,  Pseq(dirs[0].slice(nil, 1) * 1.2),
			\snd, Pseq(dirs[0].slice(nil, 0))),
		Pbind(\instrument, \kariBass,
			\group, group,
			\out, 6,
			\dur,  Pseq(dirs[1].slice(nil, 1) * 1.2),
			\freq, Pseq((dirs[1].slice(nil, 0).collect({arg elem; [Rest(0), 36.midicps, 43.midicps][elem]})))),
	] ++

	dirs.drop(2).collect({arg row, i;
		Pbind(\instrument, \kariEnsemble,
			\group, group,
			\out, i,
			\dur, Pseq(row.slice(nil, 1) * 1.2),
			//\sustain, Pseq(row.slice(nil, 1) * 1.2),
			\freq, Pseq(row.slice(nil, 0).collect({arg val; if(val == 5, {Rest(0)}, {(60 + (val.trunc * 7)).midicps})})),
			\amp, 1)})
};

~jaendelCreatePatternsForLive = {arg colors, version = 0 /*0 is full; 1 is reduced*/, startHarm = 1 /*must be odd*/, fund = 55, group;
	//generate patterns
	colors.collect({arg row, r;
		var durUnit, del, eDur, start, stop, harms, fade, pIndex;
		durUnit = 1 / 6; // 90 bpm
		// 2 measures for full version, 1 for reduced version
		del = if(version == 0, {(32 * (r % 16)) + (512 * (r / 16).asInteger)}, {(32 * (r % 8)) + (512 * (r / 8).asInteger)});
		pIndex = if(version == 0, {(r % 16)}, {(r % 8)});
		eDur = if(version == 0, {512 - 64}, {512 - 16}); // 32 - 4 measures for full version, 16 - 1 for reduced version
		start = del - r; // this skews the tiling
		stop = (start + eDur) - 1;
		harms =  ((0..(if(version == 0, {15}, {7}))) * 2).reverse + startHarm;
		fade = if(version == 0, {64}, {64});
		Pmono(\jaendel,
			\group, group,
			\pIndex, pIndex,
			\dur, Pseq([Rest(del * durUnit), Pseq([durUnit], inf)]),
			\freq, fund * harms[r % if(version == 0, {16}, {8})],
			\col, Pseq([0, Pseq(row[start..stop])]),
			\attack, fade * durUnit,
			\sustain, (eDur - (fade * 2)) * durUnit,
			\decay, fade * durUnit,
		)
	});
};


SynthDef(\mixer, {arg freq, gate = 1, sustain, amp, dur, out;
	var nameSpaces, sigs;

	sigs = [~hdpBusArray].collect({arg busArray, i;
		var nameSpace, sig;
		nameSpace = ['hdp'][i];
		sig = busArray.collect({arg bus, c; In.ar(bus, 1) * NamedControl.kr(\ ++ nameSpace ++ '_volume_' ++ c, 1, 0.1)});
		sig = sig.collect({arg channel, c; Pan2.ar(channel, NamedControl.kr(\ ++ nameSpace ++ '_pan_' ++ c, i / (busArray.size - 1), 0.1) * 2 - 1)});
		sig = sig.collect({arg channel, c; channel * NamedControl.kr(\ ++ nameSpace ++ '_mute_' ++ c, 1, 0.1)});
		sig = Mix.ar(sig) * pow(NamedControl.kr(\ ++ nameSpace ++ '_volume_master', 1, 0.5), 2);
	});

	sigs = Mix.ar(sigs / 6);
	Out.ar(0, sigs)
}).add;

)

(

var createTransportData, createTransportPattern, group, berger, mixer, measureLengths, seqs, transportData, playbackData, tempoClock, player;

~dir = thisProcess.nowExecutingPath.dirname;

createTransportData = {arg measureLengths;
	measureLengths.collect({arg dur, measure;
		var beats;
		if(dur.round(0.5) % 1 == 0, {
			dur.asInteger.collect({arg beat;
				[measure + 1, beat + 1, 1]
			})
		}, {
			var eigths, beatDurs;
			eigths = (dur / 0.5).round(0.5).asInteger;
			beatDurs = case
			{eigths <= 3} {[dur]}
			{eigths == 5} {[1, 1.5]}
			{eigths == 7} {[1, 1, 1.5]}
			{eigths == 9} {[1, 1, 1, 1.5]}
			{eigths == 11} {[1, 1, 1, 1, 1.5]}
			{eigths == 13} {[1, 1, 1, 1, 1, 1.5]}
			{eigths == 15} {[1, 1, 1, 1, 1, 1, 1.5]};

			beatDurs.collect({arg bDur, beat;
				[measure + 1, beat + 1, bDur]
			})
		});
	}).flatten;
};

createTransportPattern = {arg addr, transportData;
	Pbind(
		\type, \osc,
		\addr, addr,
		\path, "/playing",
		\msg, Pseq(transportData.collect({arg item; [item[0], item[1]]}), 1),
		\dur, Pseq(transportData.collect({arg item; item[2]}), 1),
	);
};

~bergerCreateSynthsForLive.value;
~robinsonCreateSynthsForLive.value;
~penroseCreateSynthsForLive.value;
~ammannCreateSynthsForLive.value;
~kariCreateSynthsForLive.value;
~jaendelCreateSynthsForLive.value;

group = Group.new;
berger = Synth.tail(group, \berger);
mixer = Synth.tail(group, \mixer);

playbackData = Dictionary.new(n: 6);

seqs = File.readAllString((~dir +/+ "music_data" +/+ "berger.txt").standardizePath).interpret;
measureLengths = seqs[0][6];
playbackData.add(\berger -> [~bergerCreatePatternsForLive.value(seqs, group), createTransportData.value(measureLengths), measureLengths]);

seqs = File.readAllString((~dir +/+ "music_data" +/+ "robinson.txt").standardizePath).interpret;
measureLengths = seqs[1].sum.collect({4});
playbackData.add(\robinson -> [~robinsonCreatePatternsForLive.value(seqs, group), createTransportData.value(measureLengths), measureLengths]);

seqs = File.readAllString((~dir +/+ "music_data" +/+ "penrose.txt").standardizePath).interpret;
~seqs = seqs;
measureLengths = seqs[1].sum.collect({4});
playbackData.add(\penrose -> [~penroseCreatePatternsForLive.value(seqs, group), createTransportData.value(measureLengths), measureLengths]);

seqs = File.readAllString((~dir +/+ "music_data" +/+ "ammann.txt").standardizePath).interpret;
measureLengths = seqs[0].collect({arg item; item[0].sum});
playbackData.add(\ammann -> [~ammannCreatePatternsForLive.value(seqs, group), createTransportData.value(measureLengths), measureLengths]);

seqs = File.readAllString((~dir +/+ "music_data" +/+ "kari.txt").standardizePath).interpret;
measureLengths = seqs[0].collect({arg item; item.last}).flatten;
playbackData.add(\kari -> [~kariCreatePatternsForLive.value(seqs, group), createTransportData.value(measureLengths), measureLengths]);

seqs = File.readAllString((~dir +/+ "music_data" +/+ "jaendel.txt").standardizePath).interpret;
measureLengths = 284.collect({4});
playbackData.add(\jaendel -> [~jaendelCreatePatternsForLive.value(seqs, 0, 1, 55, group), createTransportData.value(measureLengths), measureLengths]);


OSCdef(\mixer, {arg msg, time, addr, port;
	msg.postln;
	mixer.set((msg[1] ++ '_' ++ msg[2] ++ '_' ++ msg[3]), msg[4])
}, \mixer);

OSCdef(\transport, {arg msg, time, addr, port;
	msg.postln;
	if(msg[1] == 0, {
		//need some work here to make sure all synths are getting cutoff correctly
		group.set(\release, 2);
		group.set(\gate, 0);
		player.stop;
	}, {
		var pbinds, transportData, measureLengths, patterns, stream, offset, offsetStream, terminationStream;
		# pbinds, transportData, measureLengths = playbackData[msg[2]];
		patterns = Ppar(pbinds.postln.add(createTransportPattern.value(addr, transportData).postln));

		stream = patterns.asStream;
		offset = stream.fastForward(measureLengths.keep(msg[3].asInteger - 1).sum);

		offsetStream = Routine({offset.wait});
		terminationStream = Routine({addr.sendMsg("/transport", 0)});

		player = EventStreamPlayer(offsetStream ++ stream ++ terminationStream);
		tempoClock = TempoClock(msg[5]/60.0);
		player.play(tempoClock);
	});
}, \transport);

)

/*
TODOs:
- get synths to stop/cutoff correctly
- check and fix part ordering for each piece
- check and fix tempos for each piece
- make tempo variable
- set levels
*/

/*
~bergerCreateSynthsForLive.value;
~patterns = ~bergerCreatePatternsForLive.value(~bergerMusic);
~patterns.play
~bergerMusic[0][6].collect({arg beats, measure; beats.asInteger.collect({arg beat; [measure + 1, beat + 1]})}).flatten;
*/

~seqs[0][1][0]
[seqs[5], seqs[6], seqs[7], seqs[0], seqs[1], seqs[2]]

0.25 * (3/4) * 16

0.25 * 16

4/0.25

9 * 0.125