(
~jaendelTranscribe = {arg colors, version = 0 /*0 is full; 1 is reduced*/, startHarm = 1 /*must be odd*/, fund = 55;
	var dir, basePath, subTiling;

	dir = thisProcess.nowExecutingPath.dirname;
	basePath = dir +/+ ".." +/+ "score" +/+ "lilypond" +/+ "jaendel_rao";

	//runs twice full and reduced version
	2.do({arg version;
		var partRange, amps;

		partRange = if(version == 0, {(0..15)}, {(0..7)});

		//this combines rows to make the note information for each part
		//in the piece, a part are combined subsets of 4 rows spaced 16 rows apart
		//the instrumentalists are all odd harmonics the rest is filled in by the electronics
		amps = partRange.collect({arg r;
			var row, parts, shift;
			row = r;
			parts = 8.collect({arg rep;
				var durUnit, del, eDur, start, stop;

				durUnit = 1 / 6; // 90 bpm
				del = if(version == 0, {(32 * (r % 16)) + (512 * rep)}, {(32 * (r % 8)) + (512 * rep)}) ;
				eDur = if(version == 0, {512 - 64}, {512 - 16});
				//start = del - (row + (rep * if(version == 0, {32}, {16}))); // this skews the tiling
				start = if(version == 0, {(del - r) + (rep * 16)}, {(del - r) + (rep * 8)});
				stop = (start + eDur) - 1;
				shift = 0;
				(del.collect({ 0 }) ++ colors[row + (rep * if(version == 0, {16}, {8}))][start..stop]).extend(if(version == 0, {4608 * 1 - (4 * 16)}, {4608 * 1 - 256}), 0);
			});
			parts[0] + parts[1] + parts[2] + parts[3] + parts[4] + parts[5] + parts[6] + parts[7]
		});

		//generate lilypond parts
		amps.do({arg row, r;
			var harm, modi, lilyFile, lilyString, voices, lastVal;

			//create file
			harm = if(version == 0, {32 - (r * 2 + 1)}, {16 - (r * 2 + 1)});
			modi = if(version == 0, {"full_"}, {"reduced_"});
			lilyFile = File(basePath +/+ "includes" +/+ "jaendel_rao_" ++ modi ++ "part_" ++ harm.asString ++ ".ly".standardizePath,"w");

			//start lilypond directives
			lilyString = "";
			lilyString = lilyString ++ "<<\n";
			lilyString = lilyString ++ "\\numericTimeSignature \n";

			//turn each part into two voices
			voices = row.collect({arg col; col.asDigits(2, 2)}).flop;

			//transcribe each voice
			voices = voices.do({arg voice, v;
				var lilyNote, lilyDur, lilyRest, curTime = 0;

				lastVal = nil;

				//start voice
				lilyString = lilyString ++ "\n{ ";
				lilyString = lilyString ++ if(v == 1, { "\n\\override RehearsalMark.direction = #DOWN \\override RehearsalMark.X-offset = #2 \\override RehearsalMark.Y-offset = #-8"},
					"\n\\override RehearsalMark.X-offset = #1 \\override RehearsalMark.Y-offset = #4");
				lilyString = lilyString ++ "\n\\override Stem.details.beamed-lengths = #'" ++ if(v == 1, {"(5) "}, {"(5) "});
				lilyString = lilyString ++ "\n\\set Score.markFormatter = #format-mark-box-numbers ";
				//lilyString = lilyString + "\\tempo 4 = 90 \n";
				lilyString = lilyString ++ "\n\\tempo \\markup {
       \\concat {
         \\smaller \\general-align #Y #DOWN
         \\note #\"4\" #1 \\normal-text \" ≈ 90\"
}}\n";

				voice.clump(4).do({arg beat, i;
					var gSum = 0;
					beat.separate({arg a, b; a != b}).do({arg group, g; var noteLength, target = 0;
						noteLength = group.size;
						gSum = gSum + noteLength;

						//add ties
						lilyString = lilyString ++ if((group[0] == lastVal) && (group[0] != 0), {"~ "}, {""});
						//add barcheck count
						lilyString = lilyString ++ if((curTime % 4 == 0) && (i != 0), {"| "}, {""});

						//add double bar (not working)
						lilyString = lilyString ++ if(((curTime % 128) == (r * 8)) && (curTime < 1024) && (v == 0), {"\\mark \\default "}, {""});

						//crescendo
						lilyString = lilyString ++ if(((curTime % 128) == (r * 8)) && (curTime < 1024) && (v == 1), {"\\override RehearsalMark.direction = #DOWN \\mark \\markup { \\italic cresc. } "}, {""});

						//decrescendo
						lilyString = lilyString ++ if(((curTime % 128) == ((r * 8 + if(version == 0, {96}, {108})) % 128)) && (curTime >= if(version == 0, {96}, {108})) && (v == 1), {"\\override RehearsalMark.direction = #DOWN \\mark \\markup { \\italic decresc. } "}, {""});

						//note if not
						lilyNote = if(v == 1, {"f'"}, {"e''"});
						//note if rest (for vertical position control)
						if(group[0] == 0, {lilyNote = if(v == 1, {"r"}, {"r"})});
						//duration
						lilyDur = switch(noteLength, 1, {"16 "}, 2, {"8 "}, 3, {"8. "}, 4, {"4 "});
						//append rest directive
						lilyRest = "";
						//total directive
						lilyString = lilyString ++ lilyNote ++ lilyDur ++ lilyRest;

						//beam group
						if((g == 0) && (noteLength != 4), {lilyString = lilyString ++ " [ "});
						if((gSum == 4) && (noteLength != 4), {lilyString = lilyString ++ " ] "});

						lastVal = group[0];
						curTime = curTime + (noteLength / 4);
					});
				});

				//end voice
				lilyString = lilyString ++ " ] \\bar \"|.\" } \n \\\\ \n"
			});

			//consolidate notes
			lilyString.findRegexp("(e''|f')4 ~ (e''|f')4 ~ (e''|f')4 ~ (e''|f')4").clump(5).do({arg match;
				lilyString = lilyString.replace(match[0][1], match[1][1] ++ "1")});

			lilyString.findRegexp("(e''|f')4 ~ (e''|f')4 ~ (e''|f')4").clump(4).do({arg match;
				lilyString = lilyString.replace(match[0][1], match[1][1] ++ "2.")});

			lilyString.findRegexp("(e''|f')4 ~ (e''|f')4").clump(3).do({arg match;
				lilyString = lilyString.replace(match[0][1], match[1][1] ++ "2")});

			//consolidate rests
			lilyString.findRegexp("r4 r4 r4 r4").clump(2).do({arg match;
				lilyString = lilyString.replace(match[0][1], "s1")});

			lilyString.findRegexp("r4 r4 r4").clump(2).do({arg match;
				lilyString = lilyString.replace(match[0][1], "r2.")});

			lilyString.findRegexp("r4 r4").clump(2).do({arg match;
				lilyString = lilyString.replace(match[0][1], "r2")});

			//write file
			lilyString = lilyString ++ ">>";
			lilyFile.write(lilyString);
			lilyFile.close;
		});
	});
};

)
