diff --git a/README.md b/README.md index 6ac1eae..3885b60 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ loop( () => { Note: Most browsers only allow audio after a user interacts with it. You should use the `play` method to start the audio after a user interaction. -### Controlling playback +## Controlling playback You can control the playback of the ditty using the following methods: @@ -60,31 +60,41 @@ dittytoy.stop(); // stop playing dittytoy.resume(); // resume playing ``` -### Events +### Change the volume -Dittytoy emits events you can listen to by subscribing to the `addListener` method. +You can change the volume of the ditty using the `setVolume` method. ```ts -dittytoy.addListener(MSG_PLAY, () => { - console.log('Dittytoy starts playing'); -}); +dittytoy.setVolume({master: {amp: 0.5}}); // set the volume to 50% ``` -Some of the events you can listen to are: +It is also possible to set the volume of the separate loops using the same method. + +```ts +dittytoy.setVolume({loops: [{name: loop1, amp: 0.5}, {name: loop2, amp: 0.75}]}); // set the volume of loop1 to 50% and loop2 to 75% +``` -#### Logging +### Set Input Parameters + +Dittytoy allows you to set [https://dittytoy.net/syntax#input-parameters](input parameters) for the ditty using the `setInputParameters` method. For example, to set two parameters `threshold` and `gain` to -15 and 4 respectively, you can use the following code: ```ts -dittytoy.addListener(MSG_LOG, (data: any) => { - console.log(data.message); -}); +dittytoy.setInputParameters([{key: 'threshold', value: -15}, {key: 'gain', value: 4}]); +``` -dittytoy.addListener(MSG_ERROR, (data: any) => { - console.error(data.message); +## Events + +Dittytoy emits events you can listen to by subscribing to the `addListener` method. For example, to listen to the `MSG_PLAY` event, you can use the following code: + +```ts +dittytoy.addListener(MSG_PLAY, () => { + console.log('Dittytoy starts playing'); }); ``` -#### Initialization +### Initialization + +The `MSG_INIT` event is emitted each time when the ditty is compiled successfully and ready to play. ```ts dittytoy.addListener(MSG_INIT, (data:any) => { @@ -94,12 +104,11 @@ dittytoy.addListener(MSG_INIT, (data:any) => { ``` -#### Playback +### Playback + +During playback, the `MSG_UPDATE` event is emitted each time the ditty is updated. This will be ~60 times per second. ```ts -dittytoy.addListener(MSG_NOTE_PLAYED, (data:any) => { - console.log(`♪ tick: ${data.tick.toFixed(3)}, note: ${data.note} (${data.loop}.${data.synth})`); -}); dittytoy.addEventListener(MSG_UPDATE, (data:any) => { // data.amp contains information about the volume of the ditty and the separate loops const state = data.state; @@ -109,7 +118,31 @@ dittytoy.addEventListener(MSG_UPDATE, (data:any) => { }); ``` -#### Flow +Each time a note is played, the `MSG_NOTE_PLAYED` event is emitted. + +```ts +dittytoy.addListener(MSG_NOTE_PLAYED, (data:any) => { + console.log(`♪ tick: ${data.tick.toFixed(3)}, note: ${data.note} (${data.loop}.${data.synth})`); +}); +``` + +### Logging + +Different types of messages are emitted using the `MSG_LOG` and `MSG_ERROR` events. + +```ts +dittytoy.addListener(MSG_LOG, (data: any) => { + console.log(data.message); +}); + +dittytoy.addListener(MSG_ERROR, (data: any) => { + console.error(data.message); +}); +``` + +### Flow + +Finally, the `MSG_PLAY`, `MSG_PAUSE`, `MSG_STOP`, and `MSG_RESUME` events are emitted when the ditty is played, paused, stopped, or resumed. ```ts dittytoy.addListener(MSG_PLAY, () => { diff --git a/example/package-lock.json b/example/package-lock.json index 9bfb452..3c278ec 100644 --- a/example/package-lock.json +++ b/example/package-lock.json @@ -8,7 +8,7 @@ "name": "dittytoyjukebox", "version": "0.0.0", "dependencies": { - "dittytoy": "0.1.5-dev.9ffff33" + "dittytoy": "0.1.6" }, "devDependencies": { "vite": "^5.1.6" @@ -561,9 +561,9 @@ "dev": true }, "node_modules/dittytoy": { - "version": "0.1.5-dev.9ffff33", - "resolved": "https://registry.npmjs.org/dittytoy/-/dittytoy-0.1.5-dev.9ffff33.tgz", - "integrity": "sha512-JtqK9jBEeX2EBAFUqjR7FtnC1k8rgIWW8qIWeWVxVfYU91Axet61y2Vc3UGtICtYNfGHZKvG7DzDt6IPJ4pBGA==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/dittytoy/-/dittytoy-0.1.6.tgz", + "integrity": "sha512-lE6lVsAwsptnu6yjh5EseqNV4V/vpvC9PeaZ4dwWVvkTXswMcte4dMFH8DJLrMM1F8ymDIXz6lqYTp55+tqGMA==", "engines": { "node": ">=20.0.0" } diff --git a/example/src/dittytoyVisualiser.js b/example/src/dittytoyVisualiser.js index 270c9c8..a64edfa 100644 --- a/example/src/dittytoyVisualiser.js +++ b/example/src/dittytoyVisualiser.js @@ -50,6 +50,7 @@ export default class DittytoyVisualiser { this.bpm = structure.bpm; this.sampleRate = structure.sampleRate; this.tick = 0; + this.targetTick = 0; const goldenAngle = Math.PI * (3 - Math.sqrt(5)); @@ -90,23 +91,25 @@ export default class DittytoyVisualiser { } } if (data.state) { - this.tick = data.state.tick; + this.targetTick = data.state.tick; } }); this.dittytoy.addListener(MSG_NOTE_PLAYED, data => { const loop = this.loops[data.loop]; - const angle = loop.a + step(this.tick) * loop.rotSpeed * 0.1 + data.note / 64 * Math.PI * 2; + const angle = loop.a + step(this.tick) * loop.rotSpeed * 0.1 - data.note / 64 * Math.PI * 2; loop.notes.push({...data, volume: loop.volume ** 1.5, angle}); }); } - update() { - window.requestAnimationFrame(() => this.update()); + update(time=0) { + window.requestAnimationFrame((time) => this.update(time)); const canvas = this.canvas; const ctx = this.ctx; const {width, height} = canvas.getBoundingClientRect(); + this.tick = lerp(this.tick, this.targetTick, 0.25); + if (canvas.width !== width | 0 || canvas.height !== height | 0) { canvas.width = width | 0; canvas.height = height | 0;