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.
324 lines
10 KiB
Plaintext
324 lines
10 KiB
Plaintext
2 years ago
|
(
|
||
|
var formatMusicData, spellingDict, lyNoteNameStr, lyOctStr, lyFinalizeMusic, lyMeasureDef,
|
||
|
lyRelMark, lyRelMarkNote, lyHBracket, lyStaffDef, lyTie,
|
||
|
lyNoteName, lyCentDev, lyFreqRatio, lyDur, lyNote, lyBeamOpen, lyBeamClosed,
|
||
|
consolidateNotes, consolidateRests,
|
||
|
primes, hsArrayToFreq, hsArraysToFreqRatio;
|
||
|
|
||
|
primes = [[2, 1], [3, 2], [5, 4], [7, 4], [11, 8], [13, 8]];
|
||
|
|
||
|
hsArrayToFreq = {
|
||
|
arg array;
|
||
|
array.collect({arg dim, d; pow(primes[d][0]/primes[d][1], dim)}).product
|
||
|
};
|
||
|
|
||
|
hsArraysToFreqRatio = {
|
||
|
arg array1, array2;
|
||
|
var fArray, num, den, gcd;
|
||
|
fArray = array2 - array1;
|
||
|
num = 1;
|
||
|
den = 1;
|
||
|
fArray.do({arg dim, d;
|
||
|
if(dim > 0, {
|
||
|
num = num * pow(primes[d][0], dim.abs);
|
||
|
den = den * pow(primes[d][1], dim.abs);
|
||
|
});
|
||
|
if(dim < 0, {
|
||
|
num = num * pow(primes[d][1], dim.abs);
|
||
|
den = den * pow(primes[d][0], dim.abs);
|
||
|
});
|
||
|
});
|
||
|
gcd = gcd(num.asInteger, den.asInteger);
|
||
|
[num / gcd, den / gcd].asInteger
|
||
|
};
|
||
|
|
||
|
// formats the data for the transcriber
|
||
|
formatMusicData = {arg seq;
|
||
|
var maxSize, voices, durs, baseData, musicData;
|
||
|
maxSize = 0;
|
||
|
# voices, durs = seq.flatten2(seq.maxDepth - 5).flop;
|
||
|
|
||
|
baseData = voices.flop.collect({arg voice, v;
|
||
|
var clumps, hdScores, freqs, fDurs, refs;
|
||
|
|
||
|
//this gets the reference instrument and is another way to check things
|
||
|
refs = voice.collect({arg item, i;
|
||
|
var ref = [-1, [1, 1]];
|
||
|
if((i > 0), {
|
||
|
if((item != voice[i - 1]) && (item != ["Rest"]), {
|
||
|
var ins;
|
||
|
ins = voices[i].minIndex({arg hsArray, h;
|
||
|
var res = 100000;
|
||
|
if((h != v) && (hsArray != ["Rest"]), {res = (hsArray.drop(1) - item.drop(1)).abs.sum});
|
||
|
res
|
||
|
});
|
||
|
if(voices[i][ins] != ["Rest"], {
|
||
|
ref = [ins, hsArraysToFreqRatio.value(voices[i][ins], item)]
|
||
|
}, {
|
||
|
ref = [ins, [100, 100]]
|
||
|
})
|
||
|
});
|
||
|
});
|
||
|
ref
|
||
|
});
|
||
|
|
||
|
clumps = voice.separate({arg a, b; a != b });
|
||
|
freqs = clumps.collect({arg clump; if(clump[0] != ["Rest"], {(60.midicps * hsArrayToFreq.value(clump[0]))/*.cpsmidi.round(0.25).midicps*/}, {-1})});
|
||
|
fDurs = durs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump.sum});
|
||
|
refs = refs.clumps(clumps.collect({arg clump; clump.size})).collect({arg clump; clump[0]});
|
||
|
[freqs, (fDurs / 0.125).round, refs].flop;
|
||
|
});
|
||
|
|
||
|
musicData = baseData.collect({arg partData, p;
|
||
|
var res;
|
||
|
res = partData.collect({arg item, i;
|
||
|
var freq, dur, ref, amp, sus, note;
|
||
|
//# freq, dur, amp, mult, insRef = item;
|
||
|
# freq, dur, ref = item;
|
||
|
sus = dur.asInteger;
|
||
|
note = sus.collect({[freq, ref, i]});
|
||
|
//rest = if(p < rawMusicData.size, {(dur - sus).collect({[-1, -1, -1, i]})}, {[]});
|
||
|
//note ++ rest
|
||
|
note
|
||
|
}).flatten;
|
||
|
if(res.size > maxSize, {maxSize = res.size});
|
||
|
res
|
||
|
});
|
||
|
|
||
|
//make them all the same length
|
||
|
maxSize = maxSize.trunc(64) + 64;
|
||
|
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.markFormatter = #format-mark-box-numbers " +
|
||
|
"\\tempo 2 = 60\n" +
|
||
|
"\\numericTimeSignature \\time 2/2\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 % 16) == 0, {break = "\\break \\noPageBreak"});
|
||
|
if((beat % (16 * 3)) == 0, {break = "\\pageBreak"});
|
||
|
if(beat != 0, {"}\n>>\n" + barline + break}, {""}) + "\n<<\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, {"16"}, 2, {"8"}, 3, {"8."}, 4, {"4"});
|
||
|
};
|
||
|
|
||
|
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 ++ "\"}"
|
||
|
};
|
||
|
|
||
|
lyFreqRatio = {arg freqRatioMult, ref, padding = true, lower = 3, attachedToNote = true;
|
||
|
var res, ratio;
|
||
|
res = "\\markup {" + if(attachedToNote, {""}, {"\\normalsize"}) +
|
||
|
"\\lower #" ++ lower + if(padding, {"\\pad-markup #0.2 "}, {" "});
|
||
|
ratio = "\"" ++ freqRatioMult[0].asInteger ++ "/" ++ freqRatioMult[1].asInteger ++ "\" }";
|
||
|
res = if(ref != nil,
|
||
|
{
|
||
|
res ++ "\\concat{ \"" ++ ["IV", "III", "II", "I"][ref] ++ "\"\\normal-size-super " ++ ratio ++ "}"
|
||
|
}, {
|
||
|
res ++ ratio
|
||
|
}
|
||
|
);
|
||
|
if(attachedToNote, {"_" ++ res}, {res})
|
||
|
};
|
||
|
|
||
|
lyNote = {arg freq, noteLength, freqRatioMult, ref, spellingPref = \sharps, addMarkup = true, frHide = false, padding = true;
|
||
|
lyNoteName.value(freq, spellingPref) ++
|
||
|
lyDur.value(noteLength) ++
|
||
|
if(addMarkup, {
|
||
|
"<MARKUP" ++
|
||
|
lyCentDev.value(freq, padding) ++
|
||
|
if(frHide, {""}, {lyFreqRatio.value(freqRatioMult, 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.{75,85}MARKUP>)?"}, {"(<MARKUP.{75,115}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;
|
||
|
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";
|
||
|
basePath.mkdir;
|
||
|
(basePath +/+ "includes").mkdir;
|
||
|
|
||
|
//scoreFile = File(basePath +/+ "tkam_score.ly".standardizePath,"w");
|
||
|
//scoreFile.write(File.readAllString(basePath +/+ ".." +/+ "template" +/+ "tkam_score_template.ly").replace("seed: xxx", "seed: " ++ seed));
|
||
|
//scoreFile.close;
|
||
|
|
||
|
musicData = formatMusicData.value(rawMusicData);
|
||
|
|
||
|
insData = [
|
||
|
["IV", "IV", "clarinet", "bass"],
|
||
|
["III", "III", "clarinet", "alto"],
|
||
|
["II", "II", "clarinet", "treble"],
|
||
|
["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_" ++ ["IV", "III", "II", "I"][p] ++ ".ly").standardizePath,"w");
|
||
|
|
||
|
//start lypond directives
|
||
|
lyStr = "";
|
||
|
lastMusAtom = nil;
|
||
|
measureCount = 0;
|
||
|
spellingPref = \sharps;
|
||
|
tmpSectionData = nil;
|
||
|
part.clump(4).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, freqRatioMult, ref, isSame, isRest, isFirst, isLast,
|
||
|
isTied, isMeasureBound, isBeamStart, isBeamEnd;
|
||
|
|
||
|
noteLength = group.size;
|
||
|
gSum = gSum + noteLength;
|
||
|
curMusAtom = group[0];
|
||
|
freq = curMusAtom[0];
|
||
|
//freqRatioMult = curMusAtom[1];
|
||
|
ref = curMusAtom[1][0];
|
||
|
freqRatioMult = curMusAtom[1][1];
|
||
|
# isSame, isRest, isFirst, isLast = [curMusAtom == lastMusAtom, freq == -1, g == 0, gSum == 4];
|
||
|
# isTied, isMeasureBound = [isSame && isRest.not, isFirst && ((i % 4) == 0)];
|
||
|
# isBeamStart, isBeamEnd = [(noteLength != 4) && isFirst, (noteLength != 4) && 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
|
||
|
/*
|
||
|
if(sectionData[i] != nil, {
|
||
|
tmpSectionData = sectionData[i];
|
||
|
});
|
||
|
if(isTied.not, {
|
||
|
partLookup = if((p != 0) || [1, 2, 3].includes(ref).not , {p}, {ref});
|
||
|
pcRoot = ((tmpSectionData[0][partLookup][3].cpsmidi).round(1) % 12).asInteger;
|
||
|
quality = if(tmpSectionData[0][partLookup][1][2] == [[ 1, 5 ], [ 1, 2, 2 ]], {\major}, {\minor});
|
||
|
spellingPref = spellingDict[quality][pcRoot];
|
||
|
if(p == 0, {[(i / 4).asInteger, partLookup, pcRoot, quality]});
|
||
|
});
|
||
|
*/
|
||
|
|
||
|
//lyStr = lyStr + lyNote.value(freq, noteLength, freqRatioMult, ref, spellingPref, isSame.not && isRest.not);
|
||
|
lyStr = lyStr + lyNote.value(freq, noteLength, freqRatioMult, ref, \sharps, isSame.not && isRest.not);
|
||
|
|
||
|
//beam group
|
||
|
if(isBeamStart, {lyStr = lyStr ++ lyBeamOpen.value});
|
||
|
if(isBeamEnd, {lyStr = lyStr ++ lyBeamClosed.value});
|
||
|
|
||
|
lastMusAtom = curMusAtom;
|
||
|
});
|
||
|
});
|
||
|
|
||
|
//wrap music and add staff definitions
|
||
|
lyStr = lyFinalizeMusic.value(lyStr, p, insNames[p], insNamesShort[p], insMidi[p], insClef[p]);
|
||
|
|
||
|
//consolidate notes and rests
|
||
|
lyStr = consolidateNotes.value(lyStr, p);
|
||
|
|
||
|
//write file
|
||
|
lyFile.write(lyStr);
|
||
|
lyFile.close;
|
||
|
});
|
||
|
};
|
||
|
|
||
|
|
||
|
~transcribe.value(~seq);
|
||
|
|
||
|
)
|