summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/index.js66
-rw-r--r--client/lib/bandpass.js97
-rw-r--r--client/lib/kalimba.js2
-rw-r--r--client/lib/sine.js (renamed from client/lib/organ.js)31
4 files changed, 178 insertions, 18 deletions
diff --git a/client/index.js b/client/index.js
index a7bdd9e..7fdaa9b 100644
--- a/client/index.js
+++ b/client/index.js
@@ -9,7 +9,8 @@ import keys from "./lib/keys";
import color from "./lib/color";
import kalimba from "./lib/kalimba";
import sampler from "./lib/sampler";
-import organ from "./lib/organ";
+import sine from "./lib/sine";
+import bandpass from "./lib/bandpass";
import midi from "./lib/midi";
import oktransition from "./vendor/oktransition";
import { getOutput } from "./lib/output";
@@ -25,6 +26,7 @@ import {
import { scales } from "./lib/scales";
let instrument = kalimba;
+let organ = sine;
let grid = document.createElement("grid");
let root = 440;
@@ -47,18 +49,6 @@ let scaleMode = 0;
let is_split = false;
let intervals;
-requestAudioContext(() => {
- const output = getOutput();
- document.body.appendChild(grid);
- kalimba.load(output);
- organ.load(output);
- sampler.load(output, function ready() {
- instrument = sampler;
- });
- build();
- bind();
-});
-
function build() {
w = window.innerWidth;
h = window.innerHeight;
@@ -115,6 +105,12 @@ function play(note) {
}
}
function trigger(note) {
+ if (organ === bandpass) {
+ toggle(note);
+ if (instrument === kalimba) {
+ return;
+ }
+ }
if (intervalInRange(note.interval, root)) {
instrument.play(note.interval, root);
}
@@ -127,7 +123,6 @@ function trigger_index(index) {
}
function pause(note) {
organ.pause(note.interval);
- trigger(note);
const rounded = roundInterval(note.interval);
notes.forEach((row) =>
row.forEach(
@@ -144,6 +139,29 @@ function toggle(note) {
}
}
+const modes = {
+ sine,
+ bandpass,
+};
+
+let modus = null;
+function toggleModus() {
+ let intervals;
+ if (modes[modus]) {
+ intervals = modes[modus].getPlaying();
+ modes[modus].stop();
+ document.querySelector(`#modus .${modus}`).classList.remove("visible");
+ // rebuild();
+ }
+
+ modus = modus === "bandpass" ? "sine" : "bandpass";
+ organ = modes[modus];
+ document.querySelector(`#modus .${modus}`).classList.add("visible");
+ if (intervals) {
+ intervals.forEach((interval) => modes[modus].play(interval));
+ }
+}
+
function add(i, j) {
const ii = i + Math.round(base_x);
const jj = j + Math.round(base_y);
@@ -334,6 +352,9 @@ function bind() {
.addEventListener("click", () =>
document.querySelector("#help").classList.toggle("visible"),
);
+ // toggleModus();
+ toggleModus();
+ document.querySelector("#modus").addEventListener("click", toggleModus);
Array.from(document.querySelectorAll(".mode")).forEach((el) => {
console.log(el.getAttribute("name"));
el.addEventListener("click", (event) => {
@@ -410,7 +431,9 @@ function keydown(e) {
document.querySelector("#help").classList.toggle("visible");
break;
case 189: // -
+ case 173: // -
e.preventDefault();
+ e.stopPropagation();
if (e.altKey || e.metaKey) {
root = clamp(root - (e.shiftKey ? 10 : 1), 1, 200000);
organ.setRoot(root);
@@ -422,7 +445,9 @@ function keydown(e) {
}
break;
case 187: // =
+ case 61: // =
e.preventDefault();
+ e.stopPropagation();
if (e.altKey || e.metaKey) {
root = clamp(root + (e.shiftKey ? 10 : 1), 1, 200000);
organ.setRoot(root);
@@ -453,3 +478,16 @@ function showMessage(message) {
easing: oktransition.easing.circ_out,
});
}
+
+requestAudioContext(() => {
+ const output = getOutput();
+ document.body.appendChild(grid);
+ kalimba.load(output);
+ sine.load(output);
+ bandpass.load(output);
+ sampler.load(output, function ready() {
+ instrument = sampler;
+ });
+ build();
+ bind();
+});
diff --git a/client/lib/bandpass.js b/client/lib/bandpass.js
new file mode 100644
index 0000000..3e1ed16
--- /dev/null
+++ b/client/lib/bandpass.js
@@ -0,0 +1,97 @@
+/**
+ * Bandpassed noise organ
+ * @module lib/bandpass.js;
+ */
+
+import Tone from "tone";
+import { roundInterval } from "./util";
+
+let root = 440;
+
+let oscillators = {};
+let output;
+let lastPlayed;
+let noise;
+
+function load(out) {
+ output = out;
+}
+function isPlaying(interval) {
+ const rounded = roundInterval(interval);
+ const osc = oscillators[rounded];
+ return osc && osc.playing;
+}
+function play(interval) {
+ if (!output) {
+ return;
+ }
+ if (!noise) {
+ noise = new Tone.Noise().start();
+ }
+ const rounded = roundInterval(interval);
+ const osc = (oscillators[rounded] = oscillators[rounded] || {});
+ if (!osc.el) {
+ osc.interval = interval;
+ osc.el = new Tone.Filter(interval * root, "bandpass");
+ osc.el.Q.value = 100;
+ }
+ noise.connect(osc.el);
+ osc.el.connect(output);
+ osc.playing = true;
+ lastPlayed = osc;
+ return osc;
+}
+
+function pause(interval) {
+ const rounded = roundInterval(interval);
+ if (!oscillators[rounded]) return;
+ const osc = (oscillators[rounded] = oscillators[rounded] || {});
+ osc.el.disconnect(output);
+ noise.disconnect(osc.el);
+ osc.playing = false;
+ return osc;
+}
+function toggle(interval) {
+ const rounded = roundInterval(interval);
+ const osc = (oscillators[rounded] = oscillators[rounded] || {});
+ if (osc && osc.playing) {
+ pause(interval);
+ } else {
+ play(interval);
+ }
+}
+
+function setRoot(newRoot) {
+ root = newRoot;
+ for (const osc of Object.values(oscillators)) {
+ osc.el.frequency.value = osc.interval * newRoot;
+ }
+}
+function stop() {
+ for (const osc of Object.values(oscillators)) {
+ if (!osc.playing) {
+ continue;
+ }
+ osc.el.disconnect(output);
+ noise.disconnect(osc.el);
+ osc.playing = false;
+ delete osc.el;
+ }
+ oscillators = {};
+}
+function getPlaying() {
+ return Object.values(oscillators)
+ .filter((osc) => osc.playing)
+ .map((osc) => osc.interval);
+}
+
+export default {
+ load,
+ isPlaying,
+ getPlaying,
+ play,
+ pause,
+ toggle,
+ stop,
+ setRoot,
+};
diff --git a/client/lib/kalimba.js b/client/lib/kalimba.js
index 53fcb99..c3ee30f 100644
--- a/client/lib/kalimba.js
+++ b/client/lib/kalimba.js
@@ -33,7 +33,7 @@ function load({ output }) {
playbackRate: 1,
});
player.name = fn;
- const gain = new Tone.Gain(1.6);
+ const gain = new Tone.Gain(0.15);
player.connect(gain);
gain.connect(output);
sample.players.push(player);
diff --git a/client/lib/organ.js b/client/lib/sine.js
index e66f89d..b414b27 100644
--- a/client/lib/organ.js
+++ b/client/lib/sine.js
@@ -1,6 +1,6 @@
/**
* Sine wave organ
- * @module lib/organ.js;
+ * @module lib/sine.js;
*/
import Tone from "tone";
@@ -29,7 +29,9 @@ function play(interval) {
if (!osc.el) {
osc.interval = interval;
osc.el = new Tone.Oscillator(interval * root, "sine");
- osc.el.connect(output);
+ osc.gain = new Tone.Gain(0.025);
+ osc.el.connect(osc.gain);
+ osc.gain.connect(output);
}
osc.el.start();
osc.playing = true;
@@ -47,6 +49,15 @@ function pause(interval) {
osc.playing = false;
return osc;
}
+function toggle(interval) {
+ const rounded = roundInterval(interval);
+ const osc = (oscillators[rounded] = oscillators[rounded] || {});
+ if (osc && osc.playing) {
+ pause(interval);
+ } else {
+ play(interval);
+ }
+}
function setRoot(newRoot) {
root = newRoot;
@@ -63,5 +74,19 @@ function stop() {
}
oscillators = {};
}
+function getPlaying() {
+ return Object.values(oscillators)
+ .filter((osc) => osc.playing)
+ .map((osc) => osc.interval);
+}
-export default { load, isPlaying, play, pause, stop, setRoot };
+export default {
+ load,
+ isPlaying,
+ getPlaying,
+ play,
+ pause,
+ toggle,
+ stop,
+ setRoot,
+};