// 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 everywhere it appears below.\nmeter = {\n \\time 3/4\n \\key c \\minor\n \\numericTimeSignature\n \\partial 4 \n \\tempo "Andante" 4=90\n}\n\n'; //.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); };