diff options
| author | julian laplace <julescarbon@gmail.com> | 2023-05-08 23:49:29 +0200 |
|---|---|---|
| committer | julian laplace <julescarbon@gmail.com> | 2023-05-08 23:49:29 +0200 |
| commit | b9dc2f677e7c3021aeeea6b1ab609a9b40806b48 (patch) | |
| tree | c49c179515b2dce0784205e1d707f99d9ba6d0ee /src/relabi | |
| parent | cee4e2e53e1d7df114960daa78e7fd7b38e165b9 (diff) | |
v1 of relabi generator
Diffstat (limited to 'src/relabi')
| -rw-r--r-- | src/relabi/index.js | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/src/relabi/index.js b/src/relabi/index.js new file mode 100644 index 0000000..867bc0d --- /dev/null +++ b/src/relabi/index.js @@ -0,0 +1,121 @@ +import * as Tone from "tone"; +import kalimba from "../lib/kalimba"; +import { randrange } from "../lib/util"; + +const TWO_PI = 2 * Math.PI; + +/** + * Wave functions + */ +const WAVE_FUNCTIONS = { + sine: Math.cos, + triangle: (time) => + (4 / TWO_PI) * + Math.abs( + ((((time - TWO_PI / 4) % TWO_PI) + TWO_PI) % TWO_PI) - TWO_PI / 2 + ) - + 1, + square: (time) => (time % TWO_PI < Math.PI ? 1 : -1), + saw: (time) => ((time % TWO_PI) - Math.PI) / Math.PI, +}; + +class Relabi { + /** + * Initialize relabi 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) }, + ]; + this.bounds = [-0.5, 0.5]; + this.frequencies = [220, (220 * 3) / 2, 440, (440 * 3) / 2]; + } + + /** + * Start the generator + */ + start() { + console.log("Start Relabi"); + this.stop(); + this.clock = new Tone.Clock((time) => this.step(time), this.updateTime); + this.clock.start(); + } + + /** + * Stop the generator and reset it + */ + stop() { + if (this.clock) { + this.clock.stop(); + this.clock.dispose(); + } + } + + /** + * Generate relabi events + */ + step(time) { + const waveCount = this.waves.length; + const boundsCount = this.bounds.length; + let previousValue = this.previousValue; + let index; + let step; + let value; + let noteCount = 0; + + // Generate several events per second + for (step = 0; step < this.steps; step += 1) { + // Time offset for this event + const offset = time + (step * this.updateTime) / this.steps; + + // Initialize value + value = 0; + + // Compute the wave functions for this event + for (index = 0; index < waveCount; index += 1) { + const wave = this.waves[index]; + value += WAVE_FUNCTIONS[wave.type](offset * wave.frequency); + } + + // Scale to [-1, 1] + value /= waveCount / Math.PI; + + // 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) { + // Going down + this.trigger(offset, index * 2); + noteCount += 1; + } else if (value > bound && bound > previousValue) { + // Going up + this.trigger(offset, index * 2 + 1); + noteCount += 1; + } + } + + // Update the previous value + previousValue = value; + } + + // Store the latest value + this.previousValue = value; + + console.log(`Tick ${Math.floor(time)}, played ${noteCount} notes`); + } + + /** + * Trigger an event + */ + trigger(time, index) { + // console.log("trigger index", index, time); + kalimba.play(this.frequencies[index], time); + } +} + +export default Relabi; |
