Created
February 15, 2018 16:58
-
-
Save funnbot/24709bcac31d92067b03c7c81bca7252 to your computer and use it in GitHub Desktop.
I wrote a language for music
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const tone = require("tonegenerator") | |
const { notes, shapes } = require("./constants.js"); | |
module.exports = function (text) { | |
const input = new InputStream(text); | |
const tokenizer = new Tokenizer(input); | |
const parser = new Parse(tokenizer); | |
const tracks = CreateTracks(parser); | |
const track = AllignTracks(tracks); | |
return track; | |
} | |
function AllignTracks(tracks) { | |
let track = []; | |
let longest = 0; | |
for (let i = 0; i < tracks.length; i++) | |
if (tracks[i].length > tracks[longest].length) longest = i; | |
for (let i = 0; i < tracks[longest].length; i++) { | |
let val = 0; | |
for (let j = 0; j < tracks.length; j++) { | |
val += tracks[j][i] || 0; | |
} | |
track.push(val); | |
} | |
return track; | |
} | |
function CreateTracks(input) { | |
const tracks = [[]]; | |
let trk = 0; | |
while (!input.eof()) { | |
const token = input.next(); | |
if (token.type === "track") { | |
trk++; | |
if (!tracks[trk]) tracks[trk] = []; | |
continue; | |
} | |
else if (Array.isArray(token)) { | |
tracks[trk] = tracks[trk].concat(token); | |
} | |
tracks[trk] = tracks[trk].concat(tone(token)) | |
} | |
return tracks; | |
} | |
function Parse(input) { | |
function next() { | |
const token = input.next(); | |
let shape = "triangle"; | |
if (token.type === "note") { | |
return { freq: notes[token.note], lengthInSecs: token.length / 8, shape, sampleRate: 5512.5 }; | |
} | |
else if (token.type === "rest") { | |
return new Array(5512.5 * token.length / 8).fill(0); | |
} | |
else if (token.type === "tone") { | |
shape = shapes[token.tone]; | |
return next(); | |
} | |
return token; | |
} | |
return { next, eof: input.eof }; | |
} | |
function Tokenizer(input) { | |
function next() { | |
const char = input.next(); | |
if (isRest(char)) { | |
const chars = read(isRest); | |
return { type: "rest", length: chars.length + 1}; | |
} | |
if (isNote(char)) { | |
const chars = read(isMatch(char)); | |
var sharp = isSharp(input.peek()); | |
if (sharp) input.next(); | |
return { type: "note", note: char, sharp, length: chars.length + 1}; | |
} | |
if (isTone(char)) { | |
return { type: "tone", tone: char}; | |
} | |
if (isTrack(char)) { | |
return { type: "track" } | |
} | |
return { type: "error", msg: "Invalid Character: " + char}; | |
} | |
function read(action) { | |
let res = ""; | |
while (!input.eof() && action(input.peek())) { | |
res += input.next(); | |
} | |
return res; | |
} | |
const isRest = c => c === " "; | |
const isSharp = c => c === "#"; | |
const isNote = c => "abcdefgABCDEFG".includes(c); | |
const isTrack = c => c === "|"; | |
const isTone = c => "1234".includes(c); | |
const isMatch = function (m) { return c => c === m }; | |
return { next, eof: input.eof }; | |
} | |
function InputStream(text) { | |
let pos = 0; | |
function next() { | |
return text[pos++]; | |
} | |
function peek() { | |
return text[pos]; | |
} | |
function eof() { | |
return peek() === undefined; | |
} | |
return { | |
next, | |
peek, | |
eof | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment