You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

254 lines
8.1 KiB
Plaintext

(
var formatMusicData, spellingDict, lyNoteNameStr, lyOctStr, lyFinalizeMusic, lyMeasureDef,
lyRelMark, lyRelMarkNote, lyHBracket, lyStaffDef, lyTie,
lyNoteName, lyCentDev, lyDimDiff, lyDur, lyNote, lyBeamOpen, lyBeamClosed,
consolidateNotes, consolidateRests;
// formats the data for the transcriber
formatMusicData = {arg seq;
var maxSize, musicData;
maxSize = 0;
musicData = seq.collect({arg partData, p;
var freqs, durs, attacks, delays, sustains, rels, amps, refs, res;
# freqs, durs, attacks, delays, sustains, rels, amps, refs = partData.flop;
durs = (durs / 0.5).round.asInteger;
sustains = ((sustains + delays + rels) / 0.5).round.asInteger;
//sustains = (sustains / 0.5).round.asInteger;
//[durs, sustains, delays, rels].flop.do({arg item; if(item[0] < item[1], {item.postln})});
res = [freqs, durs, sustains, refs].flop.collect({arg data, i;
var freq, dur, sus, ref;
# freq, dur, sus, ref = data;
sus.collect({[freq, ref, i]}) ++ (dur - sus).collect({[-1, ref, i]})
}).flatten;
if(res.size > maxSize, {maxSize = res.size});
res
});
//make them all the same length
//maxSize = maxSize.trunc(16);// + 16;
maxSize = maxSize.trunc(8) + 8;
musicData = musicData.collect({arg partData, p; partData.extend(maxSize, partData.last)});
musicData
};
// constants (spelling dictionary note names and octaves)
spellingDict = Dictionary.with(*
[
\major -> Dictionary.with(*
[0, 7, 2, 9, 4, 11].collect({arg pc; pc->\sharps}) ++
[5, 10, 3, 8, 1, 6].collect({arg pc; pc->\flats})
),
\minor -> Dictionary.with(*
[9, 4, 11, 6, 1, 8].collect({arg pc; pc->\sharps}) ++
[2, 7, 0, 5, 10, 3].collect({arg pc; pc->\flats})
)
]
);
//define staff
lyStaffDef = {arg name, nameShort, nameMidi;
"\\new Staff = \"" ++ name ++ "\" \\with { \n" ++
"instrumentName = \"" ++ name ++ "\" \n" ++
"shortInstrumentName = \"" ++ nameShort ++ "\" \n" ++
"midiInstrument = #\"" ++ nameMidi ++ "\" \n" ++
"\n}\n"
};
// add music preamble
lyFinalizeMusic = {arg lyStr, part, name, nameShort, nameMidi, clef;
"\\new StaffGroup \\with {\\remove \"System_start_delimiter_engraver\"}\n<<\n" ++
lyStaffDef.value(name, nameShort, nameMidi) ++
"<<\n\n{ " +
"\n\\set Score.rehearsalMarkFormatter = #format-mark-box-numbers " +
"\\tempo 4 = 60\n" +
"\\numericTimeSignature \\time 4/4\n" +
"\\clef " ++ clef ++ "\n" ++ lyStr + "\\fermata" +
" }>> \\bar \"|.\" \n} \n\n>>" ++
"\n>>"
};
// barline and ossia definition
lyMeasureDef = {arg insName, part, beat;
var barline = "|", break = "";
barline = "\\bar \"|\"";
//if((beat % 24) == 0, {break = "\\break"});
////if((beat % 16) == 0, {break = "\\break \\noPageBreak"});
////if((beat % (16 * 3)) == 0, {break = "\\pageBreak"});
////if(beat != 0, {"}\n>>\n" + barline + break}, {""}) + "\n<<\n" /*++ ossia*/ + "{";
if(beat != 0, {"}\n" + barline + break}, {""}) + "\n" /*++ ossia*/ + "{"
};
lyNoteNameStr = Dictionary.with(*
[
\sharps -> ["c", "cis", "d", "dis","e", "f", "fis", "g", "gis", "a", "ais", "b"],
\flats -> ["c", "des", "d", "ees","e", "f", "ges", "g", "aes", "a", "bes", "b"],
]
);
lyOctStr = [",,", ",", "", "'", "''", "'''", "''''"];
lyTie = {"~"};
lyNoteName = {arg freq, spellingPref = \sharps;
if(freq != -1, {
lyNoteNameStr[spellingPref][((freq.cpsmidi).round(1) % 12)] ++
lyOctStr[(((freq).cpsmidi).round(1) / 12).asInteger - 2];
},{"r"});
};
lyDur = {arg noteLength;
switch(noteLength, 1, {"8"}, 2, {"4"}, 3, {"4."}, 4, {"2"});
};
lyBeamOpen = {"["};
lyBeamClosed = {"]"};
lyCentDev = {arg freq, padding = true;
var centDev;
centDev = ((freq.cpsmidi - (freq.cpsmidi).round(1)) * 100).round(1).asInteger;
"^\\markup { " ++ if(padding, {"\\pad-markup #0.2 \""}, {"\""}) ++
if(centDev >= 0, {"+"}, {""}) ++ centDev.asString ++ "\"}"
};
lyDimDiff = {arg dimDiff, ref, padding = true, lower = 3, attachedToNote = true;
var res, num, den, diff;
res = "\\markup {" + if(attachedToNote, {""}, {"\\normalsize"}) +
"\\lower #" ++ lower + if(padding, {"\\pad-markup #0.2 "}, {" "});
diff = dimDiff.abs.asString;
if(dimDiff > 1, {diff = diff ++ "↑"});
if(dimDiff < 0, {diff = diff ++ "↓"});
diff = "\" " ++ diff ++ "\" }";
res = if(ref != -1,
{
res ++ "\\concat{ \"" ++ ["III", "II", "I"][ref] ++ "\"\\normal-size-super " ++ diff ++ "}"
}, {
res ++ diff
}
);
if(attachedToNote, {"_" ++ res}, {res})
};
lyNote = {arg freq, noteLength, dimDiff, ref, spellingPref = \sharps, frHide = false, centHide = false, padding = true;
lyNoteName.value(freq, spellingPref) ++
lyDur.value(noteLength) ++
if(frHide.not || centHide.not, {
"<MARKUP" ++
lyCentDev.value(freq, padding) ++
if(frHide, {""}, {lyDimDiff.value(dimDiff, ref, padding)}) ++
"MARKUP>"
}, {""})
};
consolidateNotes = {arg lyStr, part;
var noteRegex, markupRegex, fullNoteRegex, restRegex, fullRestRegex, res;
noteRegex = "(?<n>[a-g](?:es|is)?(?:[,']*?)?4)";
//markupRegex = if(part != 0, {"(<MARKUP.{15,155}MARKUP>)?"}, {"(<MARKUP.{15,155}MARKUP>)?"});
markupRegex = "(<MARKUP.{15,155}MARKUP>)?";
fullNoteRegex = noteRegex ++ markupRegex ++ "(?:\\h+~\\h+\\k<n>)";
restRegex = "(?<r>r4)";
fullRestRegex = "(?<r>r4)(?:(\\h+)\\k<r>)";
res = lyStr;
[6, 4, 3, 2].do({arg len;
[fullNoteRegex, fullRestRegex].do({arg regex;
res.findRegexp(regex ++ "{" ++ (len-1) ++ "}").clump(3).do({arg match;
var word, note, markup, lyDur;
//[match, len].postln;
word = match[0][1];
note = match[1][1];
markup = match[2][1];
lyDur = switch(len, 6, {"1."}, 4, {"1"}, 3, {"2."}, 2, {"2"});
res = res.replace(word, note.replace("4", lyDur) ++ markup)});
});
});
res.replace("<MARKUP", "").replace("MARKUP>", "");
};
~transcribe = {arg rawMusicData;
var basePath, scoreFile, musicData, insData, insNames, insNamesShort, insMidi, insClef;
basePath = thisProcess.nowExecutingPath.dirname +/+ "lilypond";
musicData = formatMusicData.value(rawMusicData);
//musicData.postln;
insData = [
["III", "III", "clarinet", "bass"],
["II", "II", "clarinet", "alto"],
["I", "I", "clarinet", "treble"]
];
insNames = insData.slice(nil, 0);
insNamesShort = insData.slice(nil, 1);
insMidi = insData.slice(nil, 2);
insClef = insData.slice(nil, 3);
musicData.do({arg part, p;
var lyFile, lyStr, lastMusAtom, measureCount, spellingPref,
tmpSectionData, pcRoot, partLookup, quality;
//create file
lyFile = File((basePath +/+ "includes" +/+ "part_" ++ ["III", "II", "I"][p] ++ ".ly").standardizePath,"w");
//start lypond directives
lyStr = "";
lastMusAtom = nil;
measureCount = 0;
spellingPref = \sharps;
tmpSectionData = nil;
part.clump(2).do({arg beat, i;
var gSum;
gSum = 0;
beat.separate({arg a, b;
((a[0] != -1) || (b[0] != -1)) && (a != b)}).do({arg group, g;
var noteLength, curMusAtom, freq, dimDiff, ref, isSame, isRest, isFirst, isLast,
isTied, isMeasureBound, isBeamStart, isBeamEnd;
noteLength = group.size;
gSum = gSum + noteLength;
curMusAtom = group[0];
freq = curMusAtom[0];
ref = curMusAtom[1][0];
dimDiff = curMusAtom[1][1];
# isSame, isRest, isFirst, isLast = [curMusAtom == lastMusAtom, freq == -1, g == 0, gSum == 2];
# isTied, isMeasureBound = [isSame && isRest.not, isFirst && ((i % 4) == 0)];
# isBeamStart, isBeamEnd = [(noteLength != 2) && isFirst, (noteLength != 2) && isLast];
//add ties
if(isTied, {lyStr = lyStr + lyTie.value});
//add barline and ossia definition
//if(isMeasureBound, {lyStr = lyStr + "\\bar \"|.|\""}); //lyMeasureDef.value(sectionData[i], insNames[p], p, i)});
if(isMeasureBound, {lyStr = lyStr + lyMeasureDef.value(insNames[p], p, i)});
//add note data
lyStr = lyStr + lyNote.value(freq, noteLength, dimDiff, ref, \sharps, isSame || isRest || (ref < 0), isSame || isRest);
//beam group
if(isBeamStart, {lyStr = lyStr ++ lyBeamOpen.value});
if(isBeamEnd, {lyStr = lyStr ++ lyBeamClosed.value});
lastMusAtom = curMusAtom;
});
});
//wrap music and add staff definitions
lyStr = "{" ++ lyStr ++ "}\n\\bar \"|.\"\n}";
//consolidate notes and rests
lyStr = consolidateNotes.value(lyStr, p);
//write file
lyFile.write(lyStr);
lyFile.close;
});
};
~transcribe.value(~musicData);
)
~transcribe.value(~musicData);