UNPKG

adventure-engine

Version:

Simple text based adventure game library.

152 lines (139 loc) 5.15 kB
//========================================================= // song-converter.js: Primary module for adventure-engine song file converter. //========================================================= // ______ _ ______ // (____ \ | | (_____ \ // ____) )| | _____) ) // | __ ( | | | ____/ // | |__) )| |_____ _| | // |______(_)_______|_)_| // // Description: // Convert adventure-engine song files into freq/duration format // for playing in the adventure terminal. // // Format is as follows: // ! Line comment // ! Note Duration, Note Duration, Note Duration, Note Duration... // C#4 4, Eb4 1, D3 16, R 4, // ! Where a note is: // ! [note-letter][note-sharp-flat]?[note-octave]?[ ][note-duration][,] // ! Where R is a rest/pause // // Note durations are as follows: // 0 = triplet // 1 = sixteenth // 2 = eighth // 3 = dotted eighth // 4 = quarter = 1 beat = 500 ms // 5 = // 6 = dotted quarter // 7 = dotted dotted quarter // 8 = half note = 2 beats = 1000 ms // 9 = // 10 = // 11 = // 12 = dotted half note // 13 = // 14 = // 15 = // 16 = whole note // // Author: Brian Piltin // // Copyright: (C) 2019 Brian Piltin. // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY // SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Version: 0.1.2 // History: // 0.1.0: Initial version // 0.1.1: Add support for multi-timbral notes and chords // 0.1.2: Remove trailing empty array entry from notes // // TODO: // - Add control characters for changing speed, instrument, repeating //========================================================= "use strict"; let sc = (function () { function convert(song) { // Delete the comments first song = song.replace("\t", " ").replace(/\![ \S]+$/gm, ""); // Split along note boundaries let notes = song.split(","); let str = ""; for (let i = 0; i < notes.length; i++) { var note = notes[i].trim().split(" "); if (note[0][0] === "%") { // We have a control code, pass it on to the terminal str += `${note[0]},`; } else { // We have one or more notes. Split along "-" boundary var freqs = note[0].split("-"); if (freqs[freqs.length - 1] === "") { freqs.pop(); } for (let i = 0; i < freqs.length; i++) { freqs[i] = getFreqForNote(freqs[i]); } freqs = freqs.join("-"); var duration = Math.round(getMsForNote(parseInt(note[1]))); str += `${freqs} ${duration},`; if (i && i % 16 === 0) { str += "\n"; } // For increased readability } } return str.substr(0, str.length - 1); } function getMsForNote(duration, bpm = 120) { return 1000 / (bpm / 60) * (duration ? duration / 4 : 1 / 3); } //--------------------------------------------------------- // Compliments of: https://pages.mtu.edu/~suits/NoteFreqCalcs.html //--------------------------------------------------------- function getFreqForNote(noteName) { let letter = noteName[0].toUpperCase(); if (letter === "R" || letter === "0") { return 0; } // R is for REST let sharpFlat = (noteName[1] && isNaN(noteName[1])) ? noteName[1].toLowerCase() : undefined; let octave = sharpFlat ? (noteName[2] ? parseInt(noteName[2]) : 4) : (noteName[1] ? parseInt(noteName[1]) : 4); let halfSteps = getHalfStepsForNote(letter, sharpFlat, octave); return Math.round(440 * Math.pow(1.05946309435, halfSteps)); } //--------------------------------------------------------- // Compliments of: https://pages.mtu.edu/~suits/NoteFreqCalcs.html //--------------------------------------------------------- function getHalfStepsForNote(letter, sharpFlat, octave) { let range = (octave - 4) * 12; let steps = 0; if (letter === "B") { steps += 2; } else if (letter === "C") { steps += 3; } else if (letter === "D") { steps += 5; } else if (letter === "E") { steps += 7; } else if (letter === "F") { steps += 8; } else if (letter === "G") { steps += 10; } if (sharpFlat === "#") { steps++; } else if (sharpFlat === "b") { steps--; } return range + steps; } return { convert }; })(); module.exports = sc;