From dfcdf790e3879678d8b3a9b729cca03174b32d55 Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Thu, 27 May 2021 18:12:46 +0200 Subject: quieter. rightclick to recenter fractions --- client/index.js | 549 ++++++++++++++++++++++++++++----------------------- client/lib/output.js | 9 +- 2 files changed, 306 insertions(+), 252 deletions(-) (limited to 'client') diff --git a/client/index.js b/client/index.js index edc5281..87cf113 100644 --- a/client/index.js +++ b/client/index.js @@ -1,367 +1,418 @@ -import keys from './lib/keys' -import color from './lib/color' -import kalimba from './lib/kalimba' -import life from './lib/life' -import organ from './lib/organ' -import { browser, requestAudioContext, choice } from './lib/util' +import gcd from "compute-gcd"; -let instrument = kalimba +import keys from "./lib/keys"; +import color from "./lib/color"; +import kalimba from "./lib/kalimba"; +import life from "./lib/life"; +import organ from "./lib/organ"; +import { browser, requestAudioContext, choice } from "./lib/util"; -const root = 440 -const s = 50 -const w = window.innerWidth -const h = window.innerHeight -const ws = Math.ceil(w/s), hs = Math.ceil(h/s) +let instrument = kalimba; -const add_on = 0 -const mul_on = 1.0 -const add_off = 0.1 -const mul_off = 0.9 +const root = 440; +const s = 50; +const w = window.innerWidth; +const h = window.innerHeight; +const ws = Math.ceil(w / s), + hs = Math.ceil(h / s); -let dragging = false -let erasing = false -let lastFreq = 0 -let notes = [] +const add_on = 0; +const mul_on = 1.0; +const add_off = 0.1; +const mul_off = 0.9; -requestAudioContext( () => { +let dragging = false; +let erasing = false; +let lastFreq = 0; +let notes = []; + +requestAudioContext(() => { for (var i = 0; i < ws; i++) { - notes[i] = [] + notes[i] = []; for (var j = 0; j < hs; j++) { - notes[i][j] = add(i, j) + notes[i][j] = add(i, j); } } - life.init(notes, assign) -}) + life.init(notes, assign); +}); function play(freq) { - if (freq.playing) return - freq.playing = true - instrument.play(freq.frequency) + if (freq.playing) return; + freq.playing = true; + instrument.play(freq.frequency); if (instrument === organ || hash || life.isRunning()) { - freq.div.classList.add('playing') + freq.div.classList.add("playing"); } - life.assign_item(freq, true) + life.assign_item(freq, true); } function pause(freq) { - if (!freq.playing) return - freq.playing = false - instrument.pause(freq.frequency) - freq.div.classList.remove('playing') - life.assign_item(freq, false) + if (!freq.playing) return; + freq.playing = false; + instrument.pause(freq.frequency); + freq.div.classList.remove("playing"); + life.assign_item(freq, false); } function assign(freq, state) { if (state) { - play(freq) + play(freq); } else { - pause(freq) + pause(freq); } } function toggle(freq) { - assign(freq, !freq.playing) + assign(freq, !freq.playing); } const gliderShape = [ - [0,0,0,0,0], - [0,1,1,1,0], - [0,0,0,1,0], - [0,0,1,0,0], - [0,0,0,0,0], -] -const gliderShapeFlip = gliderShape.map(a => a.slice(0).reverse()) + [0, 0, 0, 0, 0], + [0, 1, 1, 1, 0], + [0, 0, 0, 1, 0], + [0, 0, 1, 0, 0], + [0, 0, 0, 0, 0], +]; +const gliderShapeFlip = gliderShape.map((a) => a.slice(0).reverse()); const gliderShapes = [ gliderShape, gliderShapeFlip, gliderShape.slice(0).reverse(), gliderShapeFlip.slice(0).reverse(), -] +]; function glider() { - const x = Math.floor(Math.random() * ws) - const y = Math.floor(Math.random() * hs) - const shape = choice(gliderShapes) - weave(x,y,shape) + const x = Math.floor(Math.random() * ws); + const y = Math.floor(Math.random() * hs); + const shape = choice(gliderShapes); + weave(x, y, shape); } -function weave (x,y,shape) { - const xmag = shape.length - const ymag = shape[0].length - let i, j, px, py +function weave(x, y, shape) { + const xmag = shape.length; + const ymag = shape[0].length; + let i, j, px, py; for (i = 0; i < xmag; i++) { for (j = 0; j < ymag; j++) { - px = (x+i) % ws - py = (y+j) % hs - assign(notes[px][py], shape[i][j]) + px = (x + i) % ws; + py = (y + j) % hs; + assign(notes[px][py], shape[i][j]); } } } -function forEach(f){ +function forEach(f) { let i, j, note, s; for (i = 0; i < ws; i++) { for (j = 0; j < hs; j++) { - note = notes[i][j] - s = f(i, j, note.playing) - assign(note, s) + note = notes[i][j]; + s = f(i, j, note.playing); + assign(note, s); } } } -function clone(){ +function clone() { let i, j; - let a = [] + let a = []; for (i = 0; i < ws; i++) { - a[i] = [] + a[i] = []; for (j = 0; j < hs; j++) { - a[i][j] = notes[i][j].playing + a[i][j] = notes[i][j].playing; } } - return a + return a; } -function move(dx,dy){ - let a = clone() - forEach((x,y,state) => { - x = (x+dx+ws)%ws - y = (y+dy+hs)%hs - return a[x][y] - }) +function move(dx, dy) { + let a = clone(); + forEach((x, y, state) => { + x = (x + dx + ws) % ws; + y = (y + dy + hs) % hs; + return a[x][y]; + }); } -function clear(){ +function clear() { forEach(() => { - return false - }) + return false; + }); } -function stripex(odd){ - odd = !! odd +function stripex(odd) { + odd = !!odd; forEach((x) => { - return x % 2 ? odd : !odd - }) + return x % 2 ? odd : !odd; + }); } -function stripey(odd){ - odd = !! odd - forEach((x,y) => { - return y % 2 ? odd : !odd - }) +function stripey(odd) { + odd = !!odd; + forEach((x, y) => { + return y % 2 ? odd : !odd; + }); } -function checker(odd, n){ - odd = !! odd - n = n || 1 - forEach((x,y) => { - return ((Math.floor(x/n)%2) ^ (Math.floor(y/n)%2)) ? odd : !odd - }) +function checker(odd, n) { + odd = !!odd; + n = n || 1; + forEach((x, y) => { + return Math.floor(x / n) % 2 ^ Math.floor(y / n) % 2 ? odd : !odd; + }); } -function noise(n){ - n = n || 0.5 - n = n * n +function noise(n) { + n = n || 0.5; + n = n * n; forEach(() => { - return Math.random() < n - }) + return Math.random() < n; + }); } -function add (i, j) { - const a = i + 1 - const b = j + 1 - const div = document.createElement('div') - const frequency = root * a/b - let add = 0 - let frac = Math.log2(a/b) % 1 - div.style.left = (i * s) + 'px' - div.style.top = (j * s) + 'px' - div.innerHTML = `
${a}<\/div>
\/
${b}<\/div>` - const freq = { frequency, div, i, j, playing: false } - if (frac < 0) { - frac += 1 - } - if (a < b) { - add = -Math.log(b/a) / 3.5 - } - else { - add = Math.log(a/b) / 6 - } - if ( frac === 0) { - div.style.fontWeight = '900' - div.style.left = (i * s) + 'px' - div.style.top = (j * s) + 'px' - } - div.style.backgroundColor = color(frac, add_off + add, mul_off) +function add(i, j) { + const a = i + 1; + const b = j + 1; + const div = document.createElement("div"); + const frequency = (root * a) / b; + let add = 0; + let frac; + div.style.left = i * s + "px"; + div.style.top = j * s + "px"; + + const freq = { + frequency, + div, + i, + j, + playing: false, + recolor: (numerator, denominator) => { + let aa = a / numerator; + let bb = b / denominator; + if (aa < bb) { + add = -Math.log(bb / aa) / 3.5; + } else { + add = Math.log(aa / bb) / 6; + } + let a_inv = a * denominator; + let b_inv = b * numerator; + let ba_gcd = gcd(a_inv, b_inv); + let a_disp = a_inv / ba_gcd; + let b_disp = b_inv / ba_gcd; + + frac = Math.log2(aa / bb) % 1; + let frac_orig = Math.log2(a / b) % 1; + if (frac < 0) { + frac += 1; + } + if (frac_orig < 0) { + frac += 1; + } + if (frac_orig === 0) { + div.style.fontWeight = "900"; + } else { + div.style.fontWeight = "500"; + } + + div.innerHTML = `
${a_disp}
/
${b_disp}
`; + if (freq.playing) { + div.style.backgroundColor = color(frac, add + add_on, mul_on); + } else { + div.style.backgroundColor = color(frac, add + add_off, mul_off); + } + }, + }; + + freq.recolor(1, 1); if (browser.isDesktop) { - div.addEventListener('mousedown', function(){ - div.style.backgroundColor = color(frac, add + add_on, mul_on) - toggle( freq ) - erasing = !freq.playing - }) - div.addEventListener('mouseenter', function(){ - div.style.backgroundColor = color(frac, add + add_on, mul_on) + div.addEventListener("mousedown", function (event) { + if (event.button === 2) { + // rightclick + event.preventDefault(); + notes.forEach((row) => row.forEach((note) => note.recolor(a, b))); + return; + } + div.style.backgroundColor = color(frac, add + add_on, mul_on); + toggle(freq); + erasing = !freq.playing; + }); + div.addEventListener("mouseenter", function () { + div.style.backgroundColor = color(frac, add + add_on, mul_on); if (dragging) { if (erasing) { - pause( freq ) + pause(freq); } else { - toggle( freq ) + toggle(freq); } } - }) - div.addEventListener('mouseleave', function(){ - div.style.backgroundColor = color(frac, add + add_off, mul_off) - }) - } - else { - div.addEventListener('touchstart', function(e){ - e.preventDefault() - toggle( freq ) - erasing = !freq.playing - lastFreq = freq - }) - } - document.body.appendChild(div) - return freq + }); + div.addEventListener("mouseleave", function () { + div.style.backgroundColor = color(frac, add + add_off, mul_off); + }); + div.addEventListener("contextmenu", function (event) { + if (!event.ctrlKey || !event.metaKey || !event.altKey) { + event.preventDefault(); + } + }); + } else { + div.addEventListener("touchstart", function (e) { + e.preventDefault(); + toggle(freq); + erasing = !freq.playing; + lastFreq = freq; + }); + } + document.body.appendChild(div); + return freq; } if (browser.isDesktop) { - document.addEventListener('mousedown', () => { dragging = true }) - document.addEventListener('mouseup', () => { dragging = false }) -} -else { - document.addEventListener('touchstart', (e) => { e.preventDefault(); dragging = true }) - document.addEventListener('touchmove', (e) => { - e.preventDefault() - const x = Math.floor( e.touches[0].pageX / s ) - const y = Math.floor( e.touches[0].pageY / s ) - if (! (x in notes) || ! (y in notes[x])) return - const freq = notes[x][y] - if (freq !== lastFreq) { + document.addEventListener("mousedown", (event) => { + if (event.button !== 2) { + dragging = true; + } + }); + document.addEventListener("mouseup", () => { + dragging = false; + }); +} else { + document.addEventListener("touchstart", (e) => { + e.preventDefault(); + dragging = true; + }); + document.addEventListener("touchmove", (e) => { + e.preventDefault(); + const x = Math.floor(e.touches[0].pageX / s); + const y = Math.floor(e.touches[0].pageY / s); + if (!(x in notes) || !(y in notes[x])) return; + const freq = notes[x][y]; + if (freq !== lastFreq) { if (dragging) { if (erasing) { - pause( freq ) + pause(freq); } else { - toggle( freq ) + toggle(freq); } } - lastFreq = freq - } - }) - document.addEventListener('touchend', () => { dragging = false }) + lastFreq = freq; + } + }); + document.addEventListener("touchend", () => { + dragging = false; + }); } -function swap_instrument(){ - instrument = (instrument === kalimba) ? organ : kalimba +function swap_instrument() { + instrument = instrument === kalimba ? organ : kalimba; } -let life_bpm = 50 -window.addEventListener("keydown", keydown, true) -function keydown(e){ +let life_bpm = 50; +window.addEventListener("keydown", keydown, true); +function keydown(e) { // console.log(e.keyCode) - if (e.altKey || e.ctrlKey || e.metaKey) return - switch (e.keyCode){ + if (e.altKey || e.ctrlKey || e.metaKey) return; + switch (e.keyCode) { case 32: // space - life.toggle() - break + life.toggle(); + break; case 188: // comma - life_bpm += e.shiftKey ? 1 : 5 - life.setTempo(life_bpm) - break + life_bpm += e.shiftKey ? 1 : 5; + life.setTempo(life_bpm); + break; case 190: // period - life_bpm -= e.shiftKey ? 1 : 5 - life_bpm = Math.max(1, life_bpm) - life.setTempo(life_bpm) - break + life_bpm -= e.shiftKey ? 1 : 5; + life_bpm = Math.max(1, life_bpm); + life.setTempo(life_bpm); + break; case 37: // left - move(1, 0) - break + move(1, 0); + break; case 38: // up - move(0, 1) - break + move(0, 1); + break; case 39: // right - move(-1, 0) - break + move(-1, 0); + break; case 40: // down - move(0, -1) - break + move(0, -1); + break; case 71: // g - glider() - break + glider(); + break; case 83: // s - swap_instrument() - break + swap_instrument(); + break; case 67: // c - clear() - break + clear(); + break; case 87: // w - clear() - break + clear(); + break; case 78: // n - noise(0.5) - break + noise(0.5); + break; case 69: // e - stripex(Math.random() < 0.5) - break + stripex(Math.random() < 0.5); + break; case 82: // r - stripey(Math.random() < 0.5) - break + stripey(Math.random() < 0.5); + break; case 84: // t - checker(Math.random() < 0.5, 1) - break + checker(Math.random() < 0.5, 1); + break; case 89: // y - checker(Math.random() < 0.5, 2) - break + checker(Math.random() < 0.5, 2); + break; case 85: // u - checker(Math.random() < 0.5, 3) - break + checker(Math.random() < 0.5, 3); + break; case 73: // i - checker(Math.random() < 0.5, 4) - break + checker(Math.random() < 0.5, 4); + break; case 79: // o - checker(Math.random() < 0.5, 5) - break + checker(Math.random() < 0.5, 5); + break; case 80: // p - checker(Math.random() < 0.5, 6) - break + checker(Math.random() < 0.5, 6); + break; case 219: // [ - checker(Math.random() < 0.5, 7) - break + checker(Math.random() < 0.5, 7); + break; case 221: // ] - checker(Math.random() < 0.5, 11) - break + checker(Math.random() < 0.5, 11); + break; case 49: // 1 - noise(0.1) - break + noise(0.1); + break; case 50: // 2 - noise(0.2) - break + noise(0.2); + break; case 51: // 3 - noise(0.3) - break + noise(0.3); + break; case 52: // 4 - noise(0.4) - break + noise(0.4); + break; case 53: // 5 - noise(0.5) - break + noise(0.5); + break; case 54: // 6 - noise(0.6) - break + noise(0.6); + break; case 55: // 7 - noise(0.7) - break + noise(0.7); + break; case 56: // 8 - noise(0.8) - break + noise(0.8); + break; case 57: // 9 - noise(0.9) - break + noise(0.9); + break; case 48: // 0 - noise(1) - break + noise(1); + break; } } -keys.listen(function(index){ +keys.listen(function (index) { // const freq = scales.current().index(index) // document.body.style.backgroundColor = color( index / scales.current().scale.length ) // instrument.toggle(freq) -}) +}); -let hash = window.location.hash || window.location.search -if (hash.match('sin') || hash.match('organ')) { - instrument = organ +let hash = window.location.hash || window.location.search; +if (hash.match("sin") || hash.match("organ")) { + instrument = organ; } -if (hash.match('glider')) { - instrument = organ - clear() - glider() - life.setTempo(life_bpm = 120 * 8) - life.toggle() +if (hash.match("glider")) { + instrument = organ; + clear(); + glider(); + life.setTempo((life_bpm = 120 * 8)); + life.toggle(); } diff --git a/client/lib/output.js b/client/lib/output.js index 2155009..9947327 100644 --- a/client/lib/output.js +++ b/client/lib/output.js @@ -1,5 +1,8 @@ -import Tone from 'tone' +import Tone from "tone"; -const compressor = new Tone.Compressor(-30, 3).toMaster() +const compressor = new Tone.Compressor(-30, 3); +const gain = new Tone.Gain(0.3); +compressor.connect(gain); +gain.toMaster(); -export default compressor +export default compressor; -- cgit v1.2.3-70-g09d2