|
|
- // Copyright (c) 2014-2016 Walter Bender
- //
- // This program is free software; you can redistribute it and/or
- // modify it under the terms of the The GNU Affero General Public
- // License as published by the Free Software Foundation; either
- // version 3 of the License, or (at your option) any later version.
- //
- // You should have received a copy of the GNU Affero General Public
- // License along with this library; if not, write to the Free Software
- // Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
-
- // This header is prepended to the Lilypond output.
- const LILYPONDHEADER = '\\version "2.18.2"\n\n% ****************************************************************\n% \n% WHAT IS THIS? -- This is a LilyPond file generated from Music\n% Blocks software (Read about it at www.musicblocks.net).\n% \n% DOWNLOAD LILYPOND -- In order to create notation with this file,\n% you will need to download and install LilyPond software onto your\n% computer (http://lilypond.org/download.html). Frescobaldi\n% software is also handy for editing LilyPond files\n% (http://frescobaldi.org/download).\n% \n% LILYPOND INSTRUCTIONS -- For instructions on how to further\n% manipulate musical notation using LilyPond software, please\n% read the Introduction (http://lilypond.org/text-input.html) and\n% the Manual\n% (http://lilypond.org/doc/v2.18/Documentation/learning/index.html).\n% \n% GLOSSARY -- A glossary with helpful examples may be found here\n% (http://www.lilypond.org/doc/v2.19/Documentation/music-glossary/).\n% \n% MUTOPIA -- You may also benefit from studying scores from the\n% Mutopia Project website, which has freely sharable music notation\n% generated with LilyPond (http://www.mutopiaproject.org/).\n% \n% LILYBIN -- You can explore your Lilypond output in a web browser at\n% (http://lilybin.com/).\n% \n% COMMENTS -- Some of the code below is commented out. You can\n% enable it by deleting the % that precedes the text or, in the\n% case of a commented section, deleting the %{ and %} that surrounds\n% the section.\n% \n% ****************************************************************\n\n% Please add your own name, the title of your musical creation,\n% and the intended copyright below.\n% The copyright is great for sharing (and re-sharing)!\n% Read more about it here (http://creativecommons.org/licenses/by-sa/4.0/).\n% Of course, you can use any copyright you like -- you made it!\n\\header {\n dedication = \\markup {\n \\abs-fontsize #8 \\sans "Made with LilyPond and Music Blocks" \\with-url #"http://walterbender.github.io/musicblocks/" {\n \\abs-fontsize #8 \\sans "(http://walterbender.github.io/musicblocks/)"\n }\n }\n title = "My Music Blocks Creation"\n% subtitle = "Subtitle"\n% instrument = "Instrument"\n composer = "Mr. Mouse"\n% arranger = "Arranger"\n copyright = "Mr. Mouse (c) 2015 -- CC-BY-SA"\n tagline = "Made from Music Blocks v.0.9"\n footer = \\markup {\n \\with-url #"http://walterbender.github.io/musicblocks/" "Made with Music Blocks Software v.0.9." Engraved on \\simple #(strftime "%Y-%m-%d" (localtime (current-time)))\n }\n currentYear = \\markup {\n \\simple #(strftime "%Y" (localtime (current-time)))\n }\n copyTag = " free to distribute, modify, and perform"\n copyType = \\markup {\n \\with-url #"http://creativecommons.org/licenses/by-sa/3.0/" "Creative Commons Attribution ShareAlike 3.0 (Unported) License "\n }\n copyright = \\markup {\n \\override #\'(baseline-skip . 0 ) \\right-column {\n \\sans \\bold \\with-url #"http://musicblocks.net" {\n \\abs-fontsize #9 "Music " \\concat {\n \\abs-fontsize #12 \\with-color #white \\char ##x01C0 \\abs-fontsize #9 "Blocks "\n }\n }\n }\n \\override #\'(baseline-skip . 0 ) \\center-column {\n \\abs-fontsize #11.9 \\with-color #grey \\bold {\n \\char ##x01C0 \\char ##x01C0\n }\n }\n \\override #\'(baseline-skip . 0 ) \\column {\n \\abs-fontsize #8 \\sans \\concat {\n " Typeset using " \\with-url #"http://www.lilypond.org" "LilyPond software " \\char ##x00A9 " " \\currentYear " by " \\composer " " \\char ##x2014 " " \\footer\n }\n \\concat {\n \\concat {\n \\abs-fontsize #8 \\sans {\n " " \\copyType \\char ##x2014 \\copyTag\n }\n }\n \\abs-fontsize #13 \\with-color #white \\char ##x01C0\n }\n }\n }\n tagline = ##f\n}\n\n% To change the meter make adjustments in the following section.\n% You must also delete the % before \\meter e
-
- //.TRANS Animal names used in Lilypond output
- const RODENTS = [_('mouse'), _('brown rat'), _('mole'), _('chipmunk'), _('red squirrel'), _('guinea pig'), _('capybara'), _('coypu'), _('black rat'), _('grey squirrel'), _('flying squirrel'), _('bat')];
- const RODENTSEN = ['mouse', 'brown rat', 'mole', 'chipmunk', 'red squirrel', 'guinea pig', 'capybara', 'coypu', 'black rat', 'grey squirrel', 'flying squirrel', 'bat'];
- //.TRANS Abbreviations for names used in Lilypind output, e.g., m for mouse
- const RODENTSSHORT = [_('m'), _('br'), _('ml'), _('ch'), _('rs'), _('gp'), _('cb'), _('cp'), _('bk'), _('gs'), _('fs'), _('bt')];
- const RODENTSSHORTEN = ['m', 'br', 'ml', 'ch', 'rs', 'gp', 'cb', 'cp', 'bk', 'gs', 'fs', 'bt'];
- const LYNOTE = 0;
- const LYDURATION = 1;
- const LYDOTCOUNT = 2;
- const LYTUPLETVALUE = 3;
- const LYROUNDDOWN = 4;
- const LYINSIDECHORD = 5;
- const LYSTACCATO = 6;
- const CLEFS = ['treble', 'bass', 'bass_8', 'percussion'];
-
-
- getLilypondHeader = function () {
- return LILYPONDHEADER;
- }
-
-
- updateLilypondNotation = function (logo, note, duration, turtle, insideChord) {
- var obj = durationToNoteValue(duration);
- if (!(turtle in logo.lilypondStaging)) {
- logo.lilypondStaging[turtle] = [];
- }
-
- if (logo.turtles.turtleList[turtle].drum) {
- note = "c'";
- }
-
- logo.lilypondStaging[turtle].push([note, obj[0], obj[1], obj[2], obj[3], insideChord, logo.staccato[turtle].length > 0 && last(logo.staccato[turtle]) > 0]);
- };
-
-
- processLilypondNotes = function (logo, turtle) {
- // obj = [instructions] or
- // obj = [note, duration, dotCount, tupletValue, roundDown, insideChord, staccato, tie]
-
- logo.lilypondNotes[turtle] = '';
-
- function __toLilynote (note) {
- // Lilypond notes use is for sharp, es for flat,
- // , and ' for shifts in octave.
- // Also, notes must be lowercase.
- // And the octave bounday is at C, not A.
-
- // Convert frequencies here.
- if (typeof(note) === 'number') {
- var pitchObj = frequencyToPitch(note);
- note = pitchObj[0] + pitchObj[1];
- }
-
- return note.replace(/♯/g, 'is').replace(/♭/g, 'es').replace(/10/g, "''''''''").replace(/1/g, ',,').replace(/2/g, ',').replace(/3/g, '').replace(/4/g, "'").replace(/5/g, "''").replace(/6/g, "'''").replace(/7/g, "''''").replace(/8/g, "''''''").replace(/9/g, "'''''''").toLowerCase();
- };
-
- var counter = 0;
- var queueSlur = false;
- var articulation = false;
- var targetDuration = 0;
- var tupletDuration = 0;
- for (var i = 0; i < logo.lilypondStaging[turtle].length; i++) {
- obj = logo.lilypondStaging[turtle][i];
- if (typeof(obj) === 'string') {
- if (obj === 'break') {
- if (i > 0) {
- logo.lilypondNotes[turtle] += '\n';
- }
- counter = 0;
- } else {
- if (obj === '(') {
- // The ( is added after the first note.
- queueSlur = true;
- } else if (obj === 'begin articulation') {
- articulation = true;
- } else if (obj === 'end articulation') {
- articulation = false;
- } else {
- logo.lilypondNotes[turtle] += obj;
- }
- }
- } else {
- if (counter % 8 === 0 && counter > 0) {
- logo.lilypondNotes[turtle] += '\n';
- }
- counter += 1;
-
- var note = __toLilynote(obj[LYNOTE]);
- var incompleteTuplet = 0; // An incomplete tuplet
-
- // If it is a tuplet, look ahead to see if it is complete.
- // While you are at it, add up the durations.
- if (obj[LYTUPLETVALUE] != null) {
- targetDuration = (1 / logo.lilypondStaging[turtle][i][LYDURATION]);
- tupletDuration = (1 / logo.lilypondStaging[turtle][i][LYROUNDDOWN]);
- var j = 1;
- var k = 1;
- while (k < obj[LYTUPLETVALUE]) {
- if (i + j >= logo.lilypondStaging[turtle].length) {
- incompleteTuplet = j;
- break;
- }
-
- if (logo.lilypondStaging[turtle][i + j][LYINSIDECHORD] > 0 && logo.lilypondStaging[turtle][i + j][LYINSIDECHORD] === logo.lilypondStaging[turtle][i + j - 1][LYINSIDECHORD]) {
- // In a chord, so jump to next note.
- j++;
- } else if (logo.lilypondStaging[turtle][i + j][LYTUPLETVALUE] !== obj[LYTUPLETVALUE]) {
- incompleteTuplet = j;
- break;
- } else {
- targetDuration += (1 / logo.lilypondStaging[turtle][i + j][LYDURATION]);
- tupletDuration += (1 / logo.lilypondStaging[turtle][i + j][LYROUNDDOWN]);
- j++; // Jump to next note.
- k++; // Increment notes in tuplet.
- }
- }
- }
-
- function __processTuplet(logo, turtle, i, count) {
- var j = 0;
- var k = 0;
-
- while (k < count) {
- var tupletDuration = 2 * logo.lilypondStaging[turtle][i + j][LYDURATION];
- // Are we in a chord?
- if (logo.lilypondStaging[turtle][i + j][LYINSIDECHORD] > 0) {
- // Is logo the first note in the chord?
- if ((i === 0 && j === 0) || logo.lilypondStaging[turtle][i + j - 1][LYINSIDECHORD] !== logo.lilypondStaging[turtle][i + j][LYINSIDECHORD]) {
- logo.lilypondNotes[turtle] += '<';
- }
-
- logo.lilypondNotes[turtle] += __toLilynote(logo.lilypondStaging[turtle][i + j][LYNOTE]);
- if (obj[LYSTACCATO]) {
- logo.lilypondNotes[turtle] += '\\staccato';
- }
-
- logo.lilypondNotes[turtle] += ' ';
-
- // Is logo the last note in the chord?
- if (i + j === logo.lilypondStaging[turtle].length - 1 || logo.lilypondStaging[turtle][i + j + 1][LYINSIDECHORD] !== logo.lilypondStaging[turtle][i + j][LYINSIDECHORD]) {
- logo.lilypondNotes[turtle] += '>' + logo.lilypondStaging[turtle][i + j + 1][LYROUNDDOWN] + ' ';
- k++; // Increment notes in tuplet.
- }
- j++;
- } else {
- logo.lilypondNotes[turtle] += __toLilynote(logo.lilypondStaging[turtle][i + j][LYNOTE]) + logo.lilypondStaging[turtle][i + j][LYROUNDDOWN];
- if (obj[LYSTACCATO]) {
- logo.lilypondNotes[turtle] += '\\staccato';
- }
-
- logo.lilypondNotes[turtle] += ' ';
- j++; // Jump to next note.
- k++; // Increment notes in tuplet.
- }
- }
-
- // Workaround to a Lilypond "feature": if a slur
- // ends on a tuplet, the closing ) must be inside
- // the closing } of the tuplet.
- if (i + j - 1 < logo.lilypondStaging[turtle].length - 1) {
- var nextObj = logo.lilypondStaging[turtle][i + j];
- if (typeof(nextObj) === 'string' && nextObj === ')') {
- logo.lilypondNotes[turtle] += ')} ';
- i += 1;
- } else {
- logo.lilypondNotes[turtle] += '} ';
- }
- } else {
- logo.lilypondNotes[turtle] += '} ';
- }
-
- return j;
- };
-
- if (obj[LYTUPLETVALUE] > 0) {
- // lilypond tuplets look like logo: \tuplet 3/2 { f8 g a }
- // multiplier = tupletDuration / targetDuration
- // e.g., (3/8) / (1/4) = (3/8) * 4 = 12/8 = 3/2
- // There may be chords embedded.
-
- if (incompleteTuplet === 0) {
- var tupletFraction = toFraction(tupletDuration / targetDuration);
- logo.lilypondNotes[turtle] += '\\tuplet ' + tupletFraction[0] + '/' + tupletFraction[1] + ' { ';
-
- i += __processTuplet(logo, turtle, i, obj[LYTUPLETVALUE]) - 1;
- } else {
- var tupletFraction = toFraction(obj[LYTUPLETVALUE] / incompleteTuplet);
- logo.lilypondNotes[turtle] += '\\tuplet ' + tupletFraction[0] + '/' + tupletFraction[1] + ' { ';
-
- i += __processTuplet(logo, turtle, i, incompleteTuplet) - 1;
- }
-
- targetDuration = 0;
- tupletDuration = 0;
- } else {
- if (obj[LYINSIDECHORD] > 0) {
- // Is logo the first note in the chord?
- if (i === 0 || logo.lilypondStaging[turtle][i - 1][LYINSIDECHORD] !== obj[LYINSIDECHORD]) {
- // Open the chord.
- logo.lilypondNotes[turtle] += '<';
- }
-
- logo.lilypondNotes[turtle] += (note);
-
- // Is logo the last note in the chord?
- if (i === logo.lilypondStaging[turtle].length - 1 || logo.lilypondStaging[turtle][i + 1][LYINSIDECHORD] !== obj[LYINSIDECHORD]) {
- // Close the chord and add note duration.
- logo.lilypondNotes[turtle] += '>';
- logo.lilypondNotes[turtle] += obj[LYDURATION];
- for (var d = 0; d < obj[LYDOTCOUNT]; d++) {
- logo.lilypondNotes[turtle] += '.';
- }
-
- if (articulation) {
- logo.lilypondNotes[turtle] += '->';
- }
-
- logo.lilypondNotes[turtle] += ' ';
- }
- } else {
- logo.lilypondNotes[turtle] += (note + obj[LYDURATION]);
- for (var d = 0; d < obj[LYDOTCOUNT]; d++) {
- logo.lilypondNotes[turtle] += '.';
- }
-
- if (articulation) {
- logo.lilypondNotes[turtle] += '->';
- }
- }
-
- if (obj[LYSTACCATO]) {
- logo.lilypondNotes[turtle] += '\\staccato';
- }
-
- targetDuration = 0;
- tupletDuration = 0;
- }
-
- logo.lilypondNotes[turtle] += ' ';
-
- if (queueSlur) {
- queueSlur = false;
- logo.lilypondNotes[turtle] += '(';
- }
- }
- }
- };
-
-
- lilypondLineBreak = function(logo, turtle) {
- if (logo.lilypondStaging[turtle] == undefined) {
- logo.lilypondStaging[turtle] = [];
- }
-
- logo.lilypondStaging[turtle].push('break');
- };
-
-
- lilypondBeginArticulation = function(logo, turtle) {
- if (logo.lilypondStaging[turtle] == undefined) {
- logo.lilypondStaging[turtle] = [];
- }
-
- logo.lilypondStaging[turtle].push('begin articulation');
- };
-
-
- lilypondEndArticulation = function(logo, turtle) {
- logo.lilypondStaging[turtle].push('end articulation');
- };
-
-
- lilypondBeginCrescendo = function(logo, turtle, factor) {
- if (logo.lilypondStaging[turtle] == undefined) {
- logo.lilypondStaging[turtle] = [];
- }
-
- if (factor > 0) {
- logo.lilypondStaging[turtle].push('\\< ');
- } else {
- logo.lilypondStaging[turtle].push('\\> ');
- }
- };
-
-
- lilypondEndCrescendo = function(logo, turtle) {
- logo.lilypondStaging[turtle].push('\\! ');
- };
-
-
- lilypondBeginSlur = function(logo, turtle) {
- if (logo.lilypondStaging[turtle] == undefined) {
- logo.lilypondStaging[turtle] = [];
- }
-
- logo.lilypondStaging[turtle].push('(');
- };
-
-
- lilypondEndSlur = function(logo, turtle) {
- logo.lilypondStaging[turtle].push(') ');
- };
-
-
- lilypondInsertTie = function(logo, turtle) {
- if (logo.lilypondStaging[turtle] == undefined) {
- logo.lilypondStaging[turtle] = [];
- }
-
- logo.lilypondStaging[turtle].push('~');
- };
-
-
- lilypondRemoveTie = function(logo, turtle) {
- logo.lilypondStaging[turtle].pop();
- };
-
-
- saveLilypondOutput = function(logo, saveName) {
- var turtleCount = 0;
- var clef = [];
- for (var t in logo.lilypondStaging) {
- turtleCount += 1;
- }
- console.log('saving as lilypond: ' + turtleCount);
-
- logo.lilypondOutput += '% You can change the MIDI instruments below to anything on logo list:\n% (http://lilypond.org/doc/v2.18/documentation/notation/midi-instruments)\n\n';
-
- var c = 0;
- for (var t in logo.lilypondStaging) {
- if (logo.lilypondStaging[t].length > 0) {
- var octaveTotal = 0;
- var noteCount = 0;
- for (var i = 0; i < logo.lilypondStaging[t].length; i++) {
- obj = logo.lilypondStaging[t][i];
- if (typeof(obj) === 'object') {
- if (obj[0].length < 2) {
- // Test for rests
- } else {
- if (typeof(obj[0]) === 'string') {
- octaveTotal += Number(obj[0].substr(-1));
- } else {
- var pitchObj = frequencyToPitch(obj[0]);
- octaveTotal += pitchObj[1];
- }
- noteCount += 1;
- }
- }
- }
- if (logo.turtles.turtleList[t].drum) {
- clef.push('percussion');
- } else if (noteCount > 0) {
- // console.log(t + ': ' + octaveTotal + ' ' + noteCount);
- switch (Math.floor(octaveTotal / noteCount)) {
- case 0:
- case 1:
- case 2:
- clef.push('bass_8');
- break;
- case 3:
- clef.push('bass');
- break;
- default:
- clef.push('treble');
- break;
- }
- } else {
- clef.push('treble');
- }
-
- processLilypondNotes(logo, t);
-
- var instrumentName = logo.turtles.turtleList[t].name;
- if (instrumentName === _('start') || instrumentName === _('start drum')) {
- instrumentName = RODENTS[t % 12];
- } else if (instrumentName === t.toString()) {
- instrumentName = RODENTS[t % 12];
- }
-
- if (instrumentName === "") {
- instrumentName = RODENTSEN[t % 12];
- }
-
- instrumentName = instrumentName.replace(/ /g, '_');
-
- logo.lilypondOutput += instrumentName + ' = {\n';
- logo.lilypondOutput += '% \\meter\n';
- logo.lilypondOutput += logo.lilypondNotes[t];
-
- // Add bar to last turtle's output.
- if (c === turtleCount - 1) {
- logo.lilypondOutput += ' \\bar "|."'
- }
- logo.lilypondOutput += '\n}\n\n';
-
- var shortInstrumentName = RODENTSSHORT[t % 12];
-
- if (shortInstrumentName === "") {
- shortInstrumentName = RODENTSSHORTEN[t % 12];
- }
-
- logo.lilypondOutput += instrumentName.replace(/ /g, '_') + 'Voice = ';
- if (logo.turtles.turtleList[t].drum) {
- logo.lilypondOutput += '\\new DrumStaff \\with {\n';
- // logo.lilypondOutput += ' \\drummode {\n hihat4 hh bassdrum bd\n }\n';
- } else {
- logo.lilypondOutput += '\\new Staff \\with {\n';
- }
- logo.lilypondOutput += ' \\clef "' + last(clef) + '"\n';
- logo.lilypondOutput += ' instrumentName = "' + instrumentName + '"\n';
- logo.lilypondOutput += ' shortInstrumentName = "' + shortInstrumentName + '"\n';
- logo.lilypondOutput += ' midiInstrument = "acoustic grand"\n';
- // Automatic note splitting
- logo.lilypondOutput += '\n \\remove "Note_heads_engraver"\n \\consists "Completion_heads_engraver"\n \\remove "Rest_engraver"\n \\consists "Completion_rest_engraver"\n'
-
- logo.lilypondOutput += '} { \\clef "' + last(clef) + '" \\' + instrumentName.replace(/ /g, '_') + ' }\n\n';
- }
- c += 1;
- }
-
- // Begin the SCORE section.
- logo.lilypondOutput += '\n\\score {\n';
- logo.lilypondOutput += ' <<\n';
-
- // Sort the staffs, treble on top, bass_8 on the bottom.
- for (var c = 0; c < CLEFS.length; c++) {
- var i = 0;
- for (var t in logo.lilypondNotes) {
- if (clef[i] === CLEFS[c]) {
- if (logo.lilypondStaging[t].length > 0) {
- var instrumentName = logo.turtles.turtleList[t].name;
- if (instrumentName === _('start') || instrumentName === _('start drum')) {
- instrumentName = RODENTS[t % 12];
- } else if (instrumentName === t.toString()) {
- instrumentName = RODENTS[t % 12];
- }
- instrumentName = instrumentName.replace(/ /g, '_');
- logo.lilypondOutput += ' \\' + instrumentName + 'Voice\n';
- }
- }
- }
- }
-
- // Add GUITAR TAB in comments.
- logo.lilypondOutput += '\n\n% GUITAR TAB SECTION\n% Delete the %{ and %} below to include guitar tablature output.\n%{\n \\new TabStaff = "guitar tab" \n <<\n \\clef moderntab\n';
- for (var c = 0; c < CLEFS.length; c++) {
- var i = 0;
- for (var t in logo.lilypondNotes) {
- if (clef[i] === CLEFS[c]) {
- if (logo.lilypondStaging[t].length > 0) {
- var instrumentName = logo.turtles.turtleList[t].name;
- if (instrumentName === _('start') || instrumentName === _('start drum')) {
- instrumentName = RODENTS[t % 12]
- } else if (instrumentName === t.toString()) {
- instrumentName = RODENTS[t % 12];
- }
- instrumentName = instrumentName.replace(/ /g, '_');
- logo.lilypondOutput += ' \\context TabVoice = "'+ instrumentName + '" \\' + instrumentName.replace(/ /g, '_') + '\n';
- }
- }
- }
- }
-
- // Close the SCORE sections.
- logo.lilypondOutput += ' >>\n%}\n';
- logo.lilypondOutput += '\n >>\n \\layout {}\n\n';
-
- // Add MIDI OUTPUT in comments.
- logo.lilypondOutput += '% MIDI SECTION\n% Delete the %{ and %} below to include MIDI output.\n%{\n\\midi {\n \\tempo 4=90\n}\n%}\n\n}\n\n';
-
- // ADD TURTLE BLOCKS CODE HERE
- logo.lilypondOutput += '% MUSIC BLOCKS CODE\n';
- logo.lilypondOutput += '% Below is the code for the Music Blocks project that generated logo Lilypond file.\n%{\n\n';
- // prepareExport() returns json-encoded project data.
- var projectData = prepareExport();
- logo.lilypondOutput += projectData.replace(/]],/g, ']],\n');
- logo.lilypondOutput += '\n%}\n\n';
-
- doSaveLilypond(logo, saveName);
- };
|