Skip to content

Instantly share code, notes, and snippets.

@funnbot
Created February 15, 2018 16:58
Show Gist options
  • Save funnbot/24709bcac31d92067b03c7c81bca7252 to your computer and use it in GitHub Desktop.
Save funnbot/24709bcac31d92067b03c7c81bca7252 to your computer and use it in GitHub Desktop.
I wrote a language for music
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