summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
authorjulian laplace <julescarbon@gmail.com>2025-07-05 13:46:12 +0200
committerjulian laplace <julescarbon@gmail.com>2025-07-05 13:46:12 +0200
commita7f55141d2270fcadfb218bef0a06c114636722c (patch)
tree52c5dd1dedcc3e9e09f12d75452b101106f13b01 /client
parent07dafb284ac799032fc0beefbcb562623ff8c2e6 (diff)
tuned triangle
Diffstat (limited to 'client')
-rw-r--r--client/index.js63
-rw-r--r--client/lib/kalimba.js44
-rw-r--r--client/lib/organ.js8
-rw-r--r--client/lib/primes.js13
4 files changed, 86 insertions, 42 deletions
diff --git a/client/index.js b/client/index.js
index 5de7b91..f3cdd4c 100644
--- a/client/index.js
+++ b/client/index.js
@@ -24,8 +24,8 @@ let dragging = false;
let erasing = false;
let lastFreq = 0;
let notes = [];
-let base_x = 0;
-let base_y = 0;
+let base_x = 128;
+let base_y = 128;
let is_split = false;
requestAudioContext(() => {
@@ -53,40 +53,35 @@ function rebuild() {
build();
}
function play(freq) {
- if (instrument === organ && freq.playing) return;
- freq.playing = true;
- let frequency = freq.frequency;
-
- // while (frequency < root) {
- // frequency *= 2;
- // }
- // while (frequency > root) {
- // frequency /= 2;
- // }
-
- instrument.play(frequency);
- if (instrument === organ || hash) {
- // || life.isRunning()) {
+ if (!freq.playing) {
+ freq.playing = true;
+ let frequency = freq.frequency;
+ // while (frequency < root) {
+ // frequency *= 2;
+ // }
+ // while (frequency > root) {
+ // frequency /= 2;
+ // }
+ organ.play(frequency);
freq.div.classList.add("playing");
}
- // 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);
+function trigger(freq) {
+ kalimba.play(freq.frequency);
}
-function assign(freq, state) {
- if (state) {
- play(freq);
- } else {
- pause(freq);
+function pause(freq) {
+ if (freq.playing) {
+ freq.playing = false;
+ organ.pause(freq.frequency);
+ freq.div.classList.remove("playing");
}
}
function toggle(freq) {
- assign(freq, !freq.playing);
+ if (freq.playing) {
+ pause(freq);
+ } else {
+ play(freq);
+ }
}
function add(i, j) {
@@ -159,6 +154,7 @@ function add(i, j) {
event.preventDefault();
// notes.forEach((row) => row.forEach((note) => note.recolor(a, b)));
is_split = [a, b];
+ toggle(freq);
return;
}
div.style.backgroundColor = color(frac, add + add_on, mul_on);
@@ -167,7 +163,7 @@ function add(i, j) {
toggle(freq);
erasing = !freq.playing;
} else {
- play(freq);
+ trigger(freq);
}
});
div.addEventListener("mouseenter", function () {
@@ -180,7 +176,7 @@ function add(i, j) {
toggle(freq);
}
} else {
- play(freq);
+ trigger(freq);
}
}
});
@@ -247,7 +243,6 @@ function swap_instrument() {
instrument = instrument === kalimba ? organ : kalimba;
}
-window.addEventListener("keydown", keydown, true);
function keydown(e) {
// console.log(e.keyCode)
if (e.altKey || e.ctrlKey || e.metaKey) return;
@@ -270,6 +265,8 @@ function keydown(e) {
break;
}
}
+window.addEventListener("keydown", keydown, true);
+
keys.listen(function (index) {
index += 7;
const x = index % 7;
@@ -278,7 +275,7 @@ keys.listen(function (index) {
const b = y + 1;
const freq = notes[a][b];
console.log(a, b, freq.frequency);
- play(freq);
+ trigger(freq);
// const freq = scales.current().index(index)
// document.body.style.backgroundColor = color( index / scales.current().scale.length )
// instrument.toggle(freq)
diff --git a/client/lib/kalimba.js b/client/lib/kalimba.js
index 11fb155..ceb19dd 100644
--- a/client/lib/kalimba.js
+++ b/client/lib/kalimba.js
@@ -1,13 +1,13 @@
import Tone from "tone";
import { choice } from "./util";
-const player_count = 8;
+const player_count = 3;
const samples = [
- { root: 226, fn: "samples/380737__cabled-mess__sansula-01-a-raw.wav" },
- { root: 267, fn: "samples/380736__cabled-mess__sansula-02-c-raw.wav" },
- { root: 340, fn: "samples/380735__cabled-mess__sansula-03-e-raw.wav" },
- { root: 452, fn: "samples/380733__cabled-mess__sansula-06-a-02-raw.wav" },
+ { root: 219.5, fn: "samples/380737__cabled-mess__sansula-01-a-raw.wav" },
+ { root: 260.9, fn: "samples/380736__cabled-mess__sansula-02-c-raw.wav" },
+ { root: 330.2, fn: "samples/380735__cabled-mess__sansula-03-e-raw.wav" },
+ // { root: 440.9, fn: "samples/380733__cabled-mess__sansula-06-a-02-raw.wav" },
// { root: 507, fn: 'samples/380734__cabled-mess__sansula-07-b-h-raw.wav', },
// { root: 535, fn: 'samples/380731__cabled-mess__sansula-08-c-raw.wav', },
// { root: 671, fn: 'samples/380732__cabled-mess__sansula-09-e-raw.wav', },
@@ -27,18 +27,29 @@ function load(output) {
retrigger: true,
playbackRate: 1,
});
- player.connect(output);
+ player.name = fn;
+ const gain = new Tone.Gain(1.6);
+ player.connect(gain);
+ gain.connect(output);
sample.players.push(player);
}
});
+ console.log(
+ "+ Voices:",
+ samples.reduce((count, sample) => count + sample.players.length, 0),
+ );
}
+let last = 440;
+
function play(freq) {
+ last = freq;
const best = choice(samples);
best.index = (best.index + 1) % player_count;
const player = best.players[best.index];
player.playbackRate = freq / best.root;
+ console.log(player.name);
player.start();
}
@@ -47,3 +58,24 @@ function pause() {
}
export default { load, play, pause };
+
+function keydown(e) {
+ // console.log(e.keyCode)
+ if (e.metaKey && last) {
+ let step = e.shiftKey ? (e.ctrlKey ? 0.01 : 0.1) : 1;
+ switch (e.keyCode) {
+ case 38: // up
+ e.preventDefault();
+ samples[0].root -= step;
+ play(last);
+ break;
+ case 40: // down
+ e.preventDefault();
+ samples[0].root += step;
+ play(last);
+ break;
+ }
+ console.log(samples[0].root);
+ }
+}
+window.addEventListener("keydown", keydown, true);
diff --git a/client/lib/organ.js b/client/lib/organ.js
index b1aa1c2..f67a3e8 100644
--- a/client/lib/organ.js
+++ b/client/lib/organ.js
@@ -12,7 +12,8 @@ function play(freq) {
if (!output) {
return;
}
- const osc = (oscillators[freq] = oscillators[freq] || {});
+ const rounded = Math.floor(freq);
+ const osc = (oscillators[rounded] = oscillators[rounded] || {});
if (!osc.el) {
osc.el = new Tone.Oscillator(freq, "sine");
osc.el.connect(output);
@@ -24,8 +25,9 @@ function play(freq) {
}
function pause(freq) {
- if (!oscillators[freq]) return;
- const osc = (oscillators[freq] = oscillators[freq] || {});
+ const rounded = Math.floor(freq);
+ if (!oscillators[rounded]) return;
+ const osc = (oscillators[rounded] = oscillators[rounded] || {});
if (osc.el) osc.el.stop();
osc.playing = false;
return osc;
diff --git a/client/lib/primes.js b/client/lib/primes.js
new file mode 100644
index 0000000..c14868b
--- /dev/null
+++ b/client/lib/primes.js
@@ -0,0 +1,13 @@
+export const PRIMES = [
+ 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
+ 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151,
+ 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233,
+ 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317,
+ 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
+ 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503,
+ 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607,
+ 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701,
+ 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811,
+ 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911,
+ 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997,
+];