Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Theme The Editor #2

Merged
merged 21 commits into from
Oct 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Muse.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class Muse {

this.samples = { ...samples, ...getLetters(synthOptions) };

this.playing = [];
this.playing = []; // this is array to support multiple muse program args
}

play(...progs) {
Expand Down
93 changes: 48 additions & 45 deletions compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,7 @@ const lengthen = modifier => modifier.value.includes("-")
? 1/2**(modifier.value.length)
: 2**modifier.value.length;

const applyLengthen = (note, modifier) => ({
type: "beat",
value: note.value,
duration: note.duration * lengthen(modifier)
})
const applyLengthen = (note, modifier) => [ note[0], note[1] * lengthen(modifier) ];

const twelveNotes = ["a", "a#", "b", "c", "c#", "d", "d#", "e", "f", "f#", "g", "g#"];
const bindPitch = n => Math.min(Math.max(n, 0), 10);
Expand Down Expand Up @@ -35,69 +31,76 @@ const shiftHelper = (note, num) => {
}
}

return `${finalNote}${number + steps}`
const pitch = bindPitch(number + steps);

return `${finalNote}${pitch}`
}

const shift = (note, modifier, up = true) => ({
type: "beat",
value: shiftHelper(note.value, up ? modifier.number : -modifier.number),
duration: note.duration
})
const shift = (note, modifier, up = true) => [
shiftHelper(note[0], up ? modifier.number : -modifier.number),
note[1]
]

const repeat = (arr, num) => [].concat(... new Array(num).fill(arr));

const isBeat = (node) => {
if (!Array.isArray(node)) return false;

const [ symbol, duration ] = node;
return typeof symbol === "string" && typeof duration === "number";
}

const modifiers = {
"length": (notes, modifier) => Array.isArray(notes)
"length": (notes, modifier) => !isBeat(notes) && Array.isArray(notes)
? notes.map(x => applyLengthen(x, modifier))
: applyLengthen(notes, modifier),
"repeat": (notes, modifier) => repeat(notes, modifier.number),
"up-shift": (notes, modifier) => Array.isArray(notes)
"up-shift": (notes, modifier) => !isBeat(notes) && Array.isArray(notes)
? notes.map(x => shift(x, modifier))
: shift(notes, modifier),
"down-shift": (notes, modifier) => Array.isArray(notes)
"down-shift": (notes, modifier) => !isBeat(notes) && Array.isArray(notes)
? notes.map(x => shift(x, modifier, false))
: shift(notes, modifier, false),
"<": (notes, modifier) => Array.isArray(notes)
"<": (notes, modifier) => !isBeat(notes) && Array.isArray(notes)
? notes.reverse()
: notes,
}

const applyModifier = (notes, modifier) => {

const applyModifier = (notes, modifier, refs) => {
const type = modifier.type;
if (type in modifiers) return modifiers[type](notes, modifier);
else throw `Unrecongized modifier: ${modifier}`;
else if (type === "referenceFunc") {
const ref = refs[modifier.value];
return ref(Array.isArray(notes) ? notes : [notes]);
} else throw `Unrecongized modifier: ${modifier}`;
}

// const applyModifier = (notes, modifier) => {
// if (modifier.type === "length") {
// return Array.isArray(notes)
// ? notes.map(x => applyLengthen(x, modifier))
// : applyLengthen(notes, modifier);
// } else if (modifier.type === "repeat") {
// return repeat(notes, modifier.number);
// }
// }

const compileNode = node => {
if (Array.isArray(node)) return compile(node);
else if (node.type === "symbol" || node.type === ";") return { type: "beat", value: node.value, duration: 1 }
else if (node.type === "modifier") return node.modifiers.reduce( (acc, cur) => {
return applyModifier(acc, cur);
}, compileNode(node.notes))
const compileNode = (node, refs) => {
if (!isBeat(node) && Array.isArray(node)) return compile(node, refs);
else if (node.type === "symbol" || node.type === ";") return [ node.value, 1 ];
else if (node.type === "referenceArr") return refs[node.value];
else if (node.type === "modifier") return node.modifiers.reduce( (acc, cur, i) => {
return applyModifier(acc, cur, refs);
}, compileNode(node.notes, refs))
}

export const compile = ast => {
let i = 0;
let result = [];
while (i < ast.length) {
let current = ast[i];
let compiled = compileNode(current);

if (Array.isArray(compiled)) result.push(...compiled)
else result.push(compiled);
export const compile = (ast, refs = {}) => {
const result = [];
ast.forEach(item => {
const val = compileNode(item, refs);

i++;
}
if (!isBeat(val) && Array.isArray(val)) {
result.push(...val);
} else {
result.push(val);
}
});

return result;
}
}





149 changes: 149 additions & 0 deletions createMuse-tag.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import { getLetters } from "./letters.js";
import { parse, tokenize } from "./parser.js";
import { compile as compileTemp } from "./compile.js";

const sleep = m => new Promise(r => setTimeout(r, m));

const AudioContext = window.AudioContext || window.webkitAudioContext;
const audioCtx = new AudioContext();

const museTag = (strs, ...vals) => { // should be able to insert array of beats
let result = "";
let refs = {}; // if val is function store in here
strs.forEach((str, i) => {
if (i >= vals.length) result = result + str;
else {
const val = vals[i];
if (typeof val === "function") {
let tempName = `$f${i}`;
refs[tempName] = val;
result = result + str + tempName;
} else if (Array.isArray(val)) {
let tempName = `$a${i}`;
refs[tempName] = val;
result = result + str + tempName;
} else if (typeof val === "string" || typeof val === "number") {
result = result + str + val;
} else {
console.error("Unexpected interpolated value:", val);
}
}
})

// console.log(result);

const toks = tokenize(result);
// console.log(toks);

const [ ast, remainder ] = parse(toks);
// console.log(ast);
const compiled = compileTemp(ast, refs);
// console.log(compiled);
return compiled;
}

class Muse {
constructor(samples, ops = {}) {
this.bpm = ops.bpm ?? 110;
this.volume = ops.volume ?? 100; // TODO

const type = ops.type ?? "sine";
const synthOptions = { type };

this.samples = { ...samples, ...getLetters(synthOptions) };
this.playing = false;
}

async play() {
const arr = museTag(...arguments);
// const arr = museTag(...arguments).map(({ value, duration }) => [value, duration]);

this.playing = true;

for (let i = 0; i < arr.length; i++) {
if (this.playing === false) continue;
let [ symbol, beats ] = arr[i];
dispatch("ADD_PLAYED", { symbol });
if (symbol === ";") await sleep(1000/this.bpm*60*beats);
else if (symbol in this.samples) this.samples[symbol](60*1000/this.bpm*beats, audioCtx);
}

// playing = false; // leave this out to call muse multiple times, stop is just a force stop

return this; // arr
}


stop() {
this.playing = false;
}

}

const createMuse = samples => (ops = {}) => {
const newMuse = new Muse(samples, ops);
dispatch("ADD_ACTIVE_MUSE", { newMuse })

return newMuse;
}

const createMuse2 = samples => (ops = {}) => {
const bpm = ops.bpm ?? 110;
const volume = ops.volume ?? 100; // TODO
const type = ops.type ?? "sine";

const synthOptions = { type }

// const AudioContext = window.AudioContext || window.webkitAudioContext;
// this.audioCtx = new AudioContext();

// asdr

samples = { ...samples, ...getLetters(synthOptions) };

let playing = false;

const play = async (...args) => { // run vs play?
const arr = museTag(args);
// const arr = museTag(...arguments).map(({ value, duration }) => [value, duration]);

playing = true;

for (let i = 0; i < arr.length; i++) {
if (playing === false) continue;
let [ symbol, beats ] = arr[i];
dispatch("ADD_PLAYED", { symbol });
if (symbol === ";") await sleep(1000/bpm*60*beats);
else if (symbol in samples) samples[symbol](60*1000/bpm*beats, audioCtx);
}

// playing = false; // leave this out to call muse multiple times, stop is just a force stop

return arr;
}

const stop = async () => { playing = false }

// length, time

const museObj = { play, stop }

dispatch("ADD_ACTIVE_MUSE", { newMuse: museObj })

return museObj;
}


const length = x => x.reduce((acc, cur) => acc + (cur[0] == ";" ? cur[1] : 0), 0)
const compile = () => museTag(...arguments);
export { createMuse, compile, length };










20 changes: 19 additions & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,25 @@
<html lang="en">
<head>
<meta charset="utf-8">
<title>Muse</title>
<link rel="manifest" href="https://assets.hackclub.com/favicons/site.webmanifest">
<meta name="viewport" content="width=device-width">
<meta charset="utf-8">
<meta property="og:locale" content="en_US">
<meta property="og:type" content="website">
<meta property="og:site_name" content="Hack Club">
<meta name="twitter:site" content="@hackclub">
<title>Hack Club Muse</title>
<meta property="og:title" content="Hack Club Muse">
<meta name="twitter:title" content="Hack Club Muse">
<meta name="description" content="🎸 a simple language for jamming!">
<meta property="og:description" content="🎸 a simple language for jamming!">
<meta name="twitter:description" content="🎸 a simple language for jamming!">
<meta name="theme-color" content="#ec3750">
<meta name="msapplication-TileColor" content="#ec3750">
<link rel="mask-icon" href="https://assets.hackclub.com/favicons/safari-pinned-tab.svg" color="#ec3750">
<link rel="apple-touch-icon" sizes="180x180" href="https://assets.hackclub.com/favicons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="https://assets.hackclub.com/favicons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="https://assets.hackclub.com/favicons/favicon-16x16.png">
<link rel="stylesheet" href="./styles.css"></link>
<script defer type="module" src="./index.js"></script>
</head>
Expand Down
28 changes: 26 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { view } from "./view.js";
import { initialSamples } from "./samples.js";
import { init } from "./init.js";
import { play } from "./play.js";
import { createMuse } from "./createMuse-tag.js";

import "./test.js";

Expand All @@ -20,6 +21,7 @@ const STATE = {
showExamples: false,
examples: [],
showShared: false,
colorMode: "light",
}

function copy(str) {
Expand All @@ -31,10 +33,32 @@ function copy(str) {
inp.remove();
}


const ACTIONS = {
INIT: init,
RENDER: (args, state) => { render(view(STATE), document.body) },
TOGGLE_COLOR_MODE: (args, state) => {
const setProp = (name, val) => document.documentElement.style.setProperty(name, val);

let mode = args.mode;
if (!mode) {
if (state.colorMode === "dark") mode = "light";
else if (state.colorMode === "light") mode = "dark";
}

if (mode === "dark") {
setProp("--text-color", "white");
setProp("--background", "var(--dark-background)");
setProp("--background-2", "var(--dark-background-2)");
setProp("--cm-filter", "invert(1)");
} else if (mode === "light") {
setProp("--text-color", "black");
setProp("--background", "var(--light-background)");
setProp("--background-2", "var(--light-background-2)");
setProp("--cm-filter", "");
}

state.colorMode = mode;
},
ADD_ACTIVE_MUSE: ({ newMuse }, state) => {
state.activeMuses.push(newMuse);
},
Expand Down Expand Up @@ -110,7 +134,7 @@ const ACTIONS = {
}
}

const dispatch = (action, args) => {
const dispatch = (action, args = {}) => {
if (action in ACTIONS) ACTIONS[action](args, STATE);
else console.error("Unrecongized action:", action);
}
Expand Down
1 change: 1 addition & 0 deletions init.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ let audioChunks = [];

export async function init(args, state) {
dispatch("RENDER");
dispatch("TOGGLE_COLOR_MODE", { mode: state.colorMode })

listenBody("click", ".trigger-play", () => {
dispatch("PLAY");
Expand Down
1 change: 1 addition & 0 deletions letters.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ async function playNote(frequency, duration, ctx, ops = {}) {
}

function getLetters(ops) {
console.log(ops);
const newLetters = {};
for (const k in letters) {
let hz = letters[k];
Expand Down
Loading