(

// MAIN LAUNCH (loads necessary files and definitions)

var appEnvironment;

//push new environment
appEnvironment = Environment.make;
appEnvironment.push;

s.waitForBoot({

	var hdpBusArray, clickBus, clickBufPrimary, clickBufSecondary,
	bergerCreateSynthsForLive, robinsonCreateSynthsForLive, penroseCreateSynthsForLive, ammannCreateSynthsForLive, kariCreateSynthsForLive, jaendelCreateSynthsForLive, clickCreateSynthForLive,
	bergerCreatePatternsForLive, robinsonCreatePatternsForLive, penroseCreatePatternsForLive, ammannCreatePatternsForLive, kariCreatePatternsForLive, jaendelCreatePatternsForLive,
	createTransportData, createTransportPattern, createClickPattern, dir, group, berger, mixer, measureLengths, seqs, transportData, playbackData, tempoClock, player;

	Buffer.freeAll(s);

	c = Condition.new;

	dir = thisProcess.nowExecutingPath.dirname;

	hdpBusArray = 32.collect({Bus.audio(s, 1)});
	clickBus = Bus.audio(s, 1);

	Event.addEventType(\osc, {
		if (~addr.postln.notNil) {
			~msg.postln;
			//~addr.sendMsg(~path, *~msg);
			~addr.sendBundle(1, [~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(3, sustain - 4, 1, 1), gate, 1, 0, 1, doneAction: 2));
			//Out.ar(out, Osc.ar(Select.kr(buf, b), freq, 0, amp) * EnvGen.kr(Env.adsr(0.25, sustain, 0.85, 0.2), 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, out1 = 0, out2 = 1;
			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(out1, SinOsc.ar(freq, 0, (((col >= 1) * (col <= 1)) + (col >= 3)).lag(0.3) * env * 0.05));
			Out.ar(out2, SinOsc.ar(freq, 0, (col >= 2).lag(0.3) * env * 0.05));
		}).add;
	};

	//sampler for click track
	clickCreateSynthForLive = {
		clickBufPrimary = Buffer.read(s, dir +/+ "music_data" +/+ "primary_click.wav");
		clickBufSecondary = Buffer.read(s, dir +/+ "music_data" +/+ "secondary_click.wav");

		SynthDef(\click, {arg measure = 0, beat = 0, dur = 1, sustain = 1, gate = 1, out = 0, primaryBufNum = 0, secondaryBufNum = 1;
			var primaryClick, secondaryClick, sig;
			primaryClick = PlayBuf.ar(1, primaryBufNum, BufRateScale.kr(primaryBufNum), 1, SampleRate.ir * BufRateScale.kr(primaryBufNum) * (measure % 100));
			//primaryClick = PlayBuf.ar(1, primaryBufNum, BufRateScale.kr(primaryBufNum));
			secondaryClick = PlayBuf.ar(1, secondaryBufNum, BufRateScale.kr(secondaryBufNum), 1);
			sig = Select.ar(beat > 0, [primaryClick, secondaryClick]);//* EnvGen.kr(Env.cutoff(0.001, 1), doneAction: 2);
			EnvGen.kr(Env.sine(sustain), gate, doneAction: 2);
			Out.ar(out, sig);
		}).add;
	};


	//create patterns
	bergerCreatePatternsForLive = {arg seqs, group;
		var pBinds, cleanSeqs;
		cleanSeqs = [seqs[5], seqs[6], seqs[7], seqs[1], seqs[2], seqs[3]];
		pBinds = cleanSeqs.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;
		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.reverse.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),
				//\freq, Pseq((55 * r.slice(nil, 2).flatten)) / 8 * pow(2, i),
				\freq, Pseq(r.slice(nil, 2).flatten.collect({arg harm; (55 * (32/27) * harm) / 8 * pow(2, i) * pow(2, if(harm < 8, {0}, {0}))})),
				\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, hdpBusArray[0],
				\dur,  Pseq(dirs[0].slice(nil, 1) /* * 1.2*/),
				\snd, Pseq(dirs[0].slice(nil, 0))),
			Pbind(\instrument, \kariBass,
				\group, group,
				\out, hdpBusArray[1],
				\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).reverse.collect({arg row, i;
			Pbind(\instrument, \kariEnsemble,
				\group, group,
				\out, hdpBusArray[i + 2],
				\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 * (32/27) * 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,
				\out1, hdpBusArray[pIndex * 2],
				\out2, hdpBusArray[pIndex * 2 + 1],
			)
		});
	};

	createClickPattern = {arg transportData, group;
		Pbind(\instrument, \click,
			\group, group,
			//\msg, Pseq(8.collect({arg i; ["-", (i % 4) + 1]}) ++ transportData.collect({arg item; [item[0], item[1]]}), 1),
			//\dur, Pseq(8.collect({arg i; 1}) ++ transportData.collect({arg item; item[2]}), 1),
			\measure, Pseq(8.collect({arg i; 2}) ++ transportData.collect({arg item; item[0]}) - 1, 1),
			\beat, Pseq(8.collect({arg i; 2}) ++ transportData.collect({arg item; item[1]}) - 1, 1),
			\dur, Pseq(8.collect({arg i; 1}) ++ transportData.collect({arg item; item[2]}), 1),
			\sustain, 0.75,
			\primaryBufNum, clickBufPrimary,
			\secondaryBufNum, clickBufSecondary,
			\out, clickBus
		);
	};


	SynthDef(\mixer, {
		var sig, click;

		sig = hdpBusArray.collect({arg bus, index; In.ar(bus, 1) * NamedControl.kr(\volume_ ++ index, 0.75, 0.1)});
		sig = sig.collect({arg channel, index; Pan2.ar(channel, NamedControl.kr(\pan_ ++ index, 0.5, 0.1) * 2 - 1)});
		sig = sig.collect({arg channel, index; channel * NamedControl.kr(\mute_ ++ index, 1, 0.1)});
		sig = Mix.ar(sig) * pow(NamedControl.kr(\volume_master, 0.75, 0.5), 2);

		click = In.ar(clickBus, 1) * NamedControl.kr(\volume_click, 0.75, 0.1);

		Out.ar(NamedControl.kr(\out_audio, 0, 0), sig);
		Out.ar(NamedControl.kr(\out_click, 0, 0), click);
	}).add;


	s.sync(c);

	createTransportData = {arg measureLengths;
		measureLengths.collect({arg dur, measure;
			var beats;
			if((dur.round(0.5) % 1 == 0) && (dur.round(0.5) != 1), {
				dur.round(0.5).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 = eigths.collect({0.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(8.collect({arg i; ["-", (i % 4) + 1]}) ++ transportData.collect({arg item; [item[0], item[1]]}), 1),
			\dur, Pseq(8.collect({arg i; 1}) ++ transportData.collect({arg item; item[2]}), 1),
		);
	};

	bergerCreateSynthsForLive.value;
	robinsonCreateSynthsForLive.value;
	penroseCreateSynthsForLive.value;
	ammannCreateSynthsForLive.value;
	kariCreateSynthsForLive.value;
	jaendelCreateSynthsForLive.value;
	clickCreateSynthForLive.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;
	seqs.size.postln;
	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;
	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, time, addr, port].postln;
		if((msg[1].asString == "volume_master") || (msg[1].asString == "volume_click") || (msg[1].asString[..2] == "out"), {
			var val = msg[2];
			if(msg[1].asString[..2] == "out", {val = val.round - 1});
			mixer.set(msg[1], val)
		}, {
			mixer.set((msg[1] ++ '_' ++ msg[2]), msg[3]);
		});
	}, \mixer);

	OSCdef(\transport, {arg msg, time, addr, port;
		[msg, time, addr, port].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);
			//group.release;
			player.stop;
		}, {
			var pbinds, transportData, measureLengths, patterns, stream, offset, offsetStream, terminationStream;
			# pbinds, transportData, measureLengths = playbackData[msg[2]];
			//patterns = Ptpar([0, createTransportPattern.value(addr, transportData), 1 * msg[5]/60.0, createClickPattern.value(transportData, group), 8 + (msg[5]/60.0), Ppar(pbinds)]);

			if((msg[6].postln != -1) && (msg[2].postln != "jaendel"), {
				var lastItem;
				"this still ran".postln;
				lastItem = transportData.detectIndex({arg item; item[0] > msg[6]});
				lastItem.postln;
				transportData = transportData.keep(lastItem + 1);
			});
			patterns = Pfpar([
				createTransportPattern.value(addr, transportData),
				Ptpar([1 * msg[5]/60.0, createClickPattern.value(transportData, group), 8 + (msg[5]/60.0), Ppar(pbinds)])
			]);

			stream = patterns.asStream;
			if(msg[3] == 1, {
				offset = stream.fastForward(measureLengths.keep(msg[3].asInteger - 1).sum);
			}, {
				offset = stream.fastForward(measureLengths.keep(msg[3].asInteger - 1).sum + 8);
			});

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

			player = EventStreamPlayer(offsetStream ++ stream ++ terminationStream);
			//player = EventStreamPlayer(offsetStream ++ stream);
			tempoClock = TempoClock(msg[5]/60.0);
			player.play(tempoClock);
			//player.play(tempoClock, quant: Quant.new(0, 0, -2));
		});
	}, \transport);

	"ready".postln;
});
appEnvironment.pop;
)

/*
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;
*/

