summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/index.jsx43
-rw-r--r--src/lib/instruments.js22
-rw-r--r--src/lib/kalimba.js49
-rw-r--r--src/lib/sampler.js46
-rw-r--r--src/relabi/index.js44
5 files changed, 129 insertions, 75 deletions
diff --git a/src/index.jsx b/src/index.jsx
index e752c5a..8c07739 100644
--- a/src/index.jsx
+++ b/src/index.jsx
@@ -1,14 +1,53 @@
import * as React from "react";
import { createRoot } from "react-dom/client";
-import { requestAudioContext } from "./lib/util";
+import { requestAudioContext, randrange } from "./lib/util";
import Relabi from "./relabi";
+import { Kalimba, Drums } from "./lib/instruments";
document.body.style.backgroundColor = "#111";
document.body.style.color = "#fff";
requestAudioContext(() => {
document.body.innerHTML = '<div id="app"></div>';
- const relabi = new Relabi();
+
+ const relabi = new Relabi({
+ waves: [
+ { type: "sine", frequency: 0.75 },
+ { type: "sine", frequency: 1.0 },
+ { type: "sine", frequency: 1.617 },
+ { type: "sine", frequency: 3.141 },
+ ],
+ bounds: [
+ {
+ level: -0.5,
+ sounds: [
+ { instrument: Drums, index: 0 },
+ { instrument: Drums, index: 1 },
+ ],
+ },
+ {
+ level: 0.5,
+ sounds: [
+ { instrument: Drums, index: 2 },
+ { instrument: Drums, index: 3 },
+ ],
+ },
+ {
+ level: -0.25,
+ sounds: [
+ { instrument: Kalimba, frequency: 440 },
+ { instrument: Kalimba, frequency: (440 * 3) / 2 },
+ ],
+ },
+ {
+ level: 0.25,
+ sounds: [
+ { instrument: Kalimba, frequency: (440 * 6) / 5 },
+ { instrument: Kalimba, frequency: (440 * 6) / 7 },
+ ],
+ },
+ ],
+ });
relabi.start();
const root = createRoot(document.getElementById("app"));
diff --git a/src/lib/instruments.js b/src/lib/instruments.js
new file mode 100644
index 0000000..7bd0230
--- /dev/null
+++ b/src/lib/instruments.js
@@ -0,0 +1,22 @@
+import Sampler from "./sampler";
+
+export const Kalimba = new Sampler({
+ tonal: true,
+ samples: [
+ { root: 226, fn: "samples/380737__cabled-mess__sansula-01-a-raw.mp3" },
+ { root: 267, fn: "samples/380736__cabled-mess__sansula-02-c-raw.mp3" },
+ { root: 340, fn: "samples/380735__cabled-mess__sansula-03-e-raw.mp3" },
+ { root: 452, fn: "samples/380733__cabled-mess__sansula-06-a-02-raw.mp3" },
+ ],
+});
+
+export const Drums = new Sampler({
+ tonal: false,
+ samples: [
+ { fn: "samples/707_bd.mp3" },
+ { fn: "samples/707_clap.mp3" },
+ // { fn: "samples/707_cow.mp3" },
+ { fn: "samples/707_hat.mp3" },
+ { fn: "samples/707_rim.mp3" },
+ ],
+});
diff --git a/src/lib/kalimba.js b/src/lib/kalimba.js
deleted file mode 100644
index a8e0c0e..0000000
--- a/src/lib/kalimba.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import * as Tone from "tone";
-import { choice } from "./util";
-import output from "./output";
-
-const player_count = 4;
-let player_index = 0;
-
-const samples = [
- { root: 226, fn: "samples/380737__cabled-mess__sansula-01-a-raw.mp3" },
- { root: 267, fn: "samples/380736__cabled-mess__sansula-02-c-raw.mp3" },
- { root: 340, fn: "samples/380735__cabled-mess__sansula-03-e-raw.mp3" },
- { root: 452, fn: "samples/380733__cabled-mess__sansula-06-a-02-raw.mp3" },
- // { 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', },
-];
-
-samples.forEach((sample) => {
- sample.players = [];
- sample.index = -1;
- for (let i = 0; i < player_count; i++) {
- let fn = sample.fn;
- if (window.location.href.match(/asdf.us/)) {
- fn = "//asdf.us/kalimba/" + fn;
- }
- let player = new Tone.Player({
- url: fn,
- retrigger: true,
- playbackRate: 1,
- });
- player.connect(output);
- sample.players.push(player);
- }
-});
-
-function play(freq, time) {
- const best = choice(samples);
- best.index = (best.index + 1) % player_count;
-
- const player = best.players[best.index];
- player.playbackRate = freq / best.root;
- player.start(time || 0);
-}
-
-function pause() {
- // no-op
-}
-
-export default { play, pause };
diff --git a/src/lib/sampler.js b/src/lib/sampler.js
new file mode 100644
index 0000000..0db53e1
--- /dev/null
+++ b/src/lib/sampler.js
@@ -0,0 +1,46 @@
+import * as Tone from "tone";
+import { choice, randrange } from "./util";
+import output from "./output";
+
+const PLAYER_COUNT = 4;
+
+export default class Sampler {
+ constructor({ samples, tonal }) {
+ this.tonal = tonal;
+ this.samples = samples.map(({ ...sample }) => {
+ sample.players = [];
+ sample.index = -1;
+ for (let i = 0; i < PLAYER_COUNT; i++) {
+ let fn = sample.fn;
+ if (window.location.href.match(/asdf.us/)) {
+ fn = "//asdf.us/kalimba/" + fn;
+ }
+ let player = new Tone.Player({
+ url: fn,
+ retrigger: true,
+ playbackRate: 1,
+ });
+ player.connect(output);
+ sample.players.push(player);
+ }
+ return sample;
+ });
+ }
+
+ play(time, options) {
+ const sound = options.index
+ ? this.samples[options.index]
+ : choice(this.samples);
+
+ sound.index = (sound.index + 1) % PLAYER_COUNT;
+
+ const player = sound.players[sound.index];
+
+ if (this.tonal) {
+ player.playbackRate =
+ (options.frequency * choice([0.5, 1]) + randrange(0, 10)) / sound.root;
+ }
+
+ player.start(time || 0);
+ }
+}
diff --git a/src/relabi/index.js b/src/relabi/index.js
index 867bc0d..ad5b11d 100644
--- a/src/relabi/index.js
+++ b/src/relabi/index.js
@@ -1,6 +1,4 @@
import * as Tone from "tone";
-import kalimba from "../lib/kalimba";
-import { randrange } from "../lib/util";
const TWO_PI = 2 * Math.PI;
@@ -19,21 +17,23 @@ const WAVE_FUNCTIONS = {
saw: (time) => ((time % TWO_PI) - Math.PI) / Math.PI,
};
-class Relabi {
+/**
+ * Relabi generator
+ */
+export default class Relabi {
/**
- * Initialize relabi generator
+ * Initialize generator
*/
- constructor() {
- this.updateTime = 1.0;
- this.steps = 100;
- this.waves = [
- { type: "sine", frequency: randrange(0.5, 2) },
- { type: "sine", frequency: randrange(0.5, 2) },
- { type: "sine", frequency: randrange(1, 10) },
- { type: "sine", frequency: randrange(5, 10) },
+ constructor({ waves, bounds }) {
+ this.updateTime = 0.5;
+ this.steps = 50;
+ this.waves = waves || [
+ { type: "sine", frequency: randrange(0.5, 1.5) },
+ { type: "sine", frequency: randrange(0.75, 2.25) },
+ { type: "sine", frequency: randrange(1, 3) },
+ { type: "sine", frequency: randrange(2, 4) },
];
- this.bounds = [-0.5, 0.5];
- this.frequencies = [220, (220 * 3) / 2, 440, (440 * 3) / 2];
+ this.bounds = bounds;
}
/**
@@ -88,13 +88,13 @@ class Relabi {
// Compute whether we crossed a boundary, and which direction
for (index = 0; index < boundsCount; index += 1) {
const bound = this.bounds[index];
- if (value < bound && bound < previousValue) {
+ if (value < bound.level && bound.level < previousValue) {
// Going down
- this.trigger(offset, index * 2);
+ this.trigger(offset, bound.sounds[0]);
noteCount += 1;
- } else if (value > bound && bound > previousValue) {
+ } else if (value > bound.level && bound.level > previousValue) {
// Going up
- this.trigger(offset, index * 2 + 1);
+ this.trigger(offset, bound.sounds[1]);
noteCount += 1;
}
}
@@ -105,17 +105,13 @@ class Relabi {
// Store the latest value
this.previousValue = value;
-
- console.log(`Tick ${Math.floor(time)}, played ${noteCount} notes`);
}
/**
* Trigger an event
*/
- trigger(time, index) {
+ trigger(time, sound) {
// console.log("trigger index", index, time);
- kalimba.play(this.frequencies[index], time);
+ sound.instrument.play(time, sound);
}
}
-
-export default Relabi;