diff --git a/README.md b/README.md index aee06ec..f835716 100644 --- a/README.md +++ b/README.md @@ -1 +1,149 @@ # Dittytoy + +The dittytoy package is a powerful package that allows you to compile and play ditties +from [Dittytoy.net](https://dittytoy.net). + +[Dittytoy.net](https://dittytoy.net) is an online platform that allows you to create generative music using a +minimalistic javascript API using Web Audio. + +The API syntax is loosely based on the syntax of [Sonic Pi](https://sonic-pi.net/tutorial.html). You can find the +full [Dittytoy API Reference here](https://dittytoy.net/syntax). + +## Getting started + +### Installing + +Add `dittytoy` to your project: + +```sh +npm i dittytoy +``` + +## Basic usage + +Compile a ditty and play. + +```ts +import {Dittytoy} from 'dittytoy'; + +const dittytoy = new Dittytoy(); + +dittytoy.compile(` +ditty.bpm = 120; + +loop( () => { + + for (let i=0; i<4; i++) { + sine.play(c4, { attack: 0.01, release: 0.25, duration: 0.125, pan: Math.random() * 2 - 1, amp: 1.0 }); + sleep( 0.25 ); + } + + sine.play(d4, { attack: 0.01, release: 0.25, duration: 0.25 }); // attack and release in seconds, duration in ticks + sleep(0.5); // sleep in ticks + + sine.play(f4, { attack: 0.01, release: 0.75, duration: 0.25 }); + sleep(0.5); + +}, { name: 'my first loop' }); +`).then(() => { + dittytoy.play(); +}) +``` + +Note: most browsers only allow audio to be played after a user interaction. You should use the `play` method to start the +audio after a user interaction. + +### Events + +Dittytoy emits events that you can listen to by subscribing to the `addEventListener` method. + +```ts +dittytoy.addEventListener(MSG_PLAY, () => { + console.log('Dittytoy starts playing'); +}); +``` + +Some of the events you can listen to are: + +#### Logging + +```ts +dittytoy.addEventListener(MSG_LOG, (data: any) => { + console.log(data.message); +}); + +dittytoy.addEventListener(MSG_ERROR, (data: any) => { + console.error(data.message); +}); +``` + +#### Initialization + +```ts +dittytoy.addEventListener(MSG_INIT, (data:any) => { + console.log('Dittytoy is initialized, ready to play'); + console.log('Structure of compiled ditty:', data.structure); +}); +``` + + +#### Playback + +```ts +dittytoy.addEventListener(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; + if (state) { + console.log(`tick: ${(state.tick || 0).toFixed(3)}, time: ${(state.time || 0).toFixed(3)} (${state.bpm.toFixed(0)} bpm)`); + } +}); +``` + +#### Flow + +```ts +dittytoy.addEventListener(MSG_PLAY, () => { + console.log('play'); +}); +dittytoy.addEventListener(MSG_PAUSE, () => { + console.log('pause'); +}); +dittytoy.addEventListener(MSG_STOP, () => { + console.log('stop'); +}); +dittytoy.addEventListener(MSG_RESUME, () => { + console.log('resume'); +}); +``` + +## Building + +To build Dittytoy, ensure that you have [Git](http://git-scm.com/downloads) +and [Node.js](http://nodejs.org/) installed. + +Clone a copy of the repo: + +```sh +git clone https://github.com/reindernijhoff/dittytoy-package.git +``` + +Change to the dittytoy directory: + +```sh +cd dittytoy-package +``` + +Install dev dependencies: + +```sh +npm i +``` + +Build package: + +```sh +npm run build +``` diff --git a/example/package-lock.json b/example/package-lock.json index 01ed1d9..32bf403 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.0-dev.12b7a9a" + "dittytoy": "0.1.1" }, "devDependencies": { "vite": "^5.1.6" @@ -561,9 +561,9 @@ "dev": true }, "node_modules/dittytoy": { - "version": "0.1.0-dev.12b7a9a", - "resolved": "https://registry.npmjs.org/dittytoy/-/dittytoy-0.1.0-dev.12b7a9a.tgz", - "integrity": "sha512-yGIwCvBJ5yX2f6AVmkETNHzjxwQd9Fg3Eb3x3xYlpA+Qq5vCUHvk9TOxNY/IkeQegwMuLNfsT21jxJLbzGisZw==", + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dittytoy/-/dittytoy-0.1.1.tgz", + "integrity": "sha512-kSdPXHnhf4WJpiLt6sd227lctPbehkqkVGh9An+iW17dJ58d8lvAy59Z1pgDPsim7Qeu6e+H0TduOpJKqEAl/Q==", "engines": { "node": ">=20.0.0" } diff --git a/example/src/dittytoyJukebox.js b/example/src/dittytoyJukebox.js index c2c186d..7f9d47f 100644 --- a/example/src/dittytoyJukebox.js +++ b/example/src/dittytoyJukebox.js @@ -38,7 +38,7 @@ export default class DittytoyJukebox { if (state) $('log2').innerText = `▸ tick: ${(state.tick || 0).toFixed(3)}, time: ${(state.time || 0).toFixed(3)} (${state.bpm.toFixed(0)} bpm)`; }); this.dittytoy.addListener(MSG_NOTE_PLAYED, (data) => { - $('log3').innerText = `♪ tick: ${data.tick.toFixed(2)}, note: ${data.note.toFixed(2)} (${data.loop}.${data.synth})`; + $('log3').innerText = `♪ tick: ${data.tick.toFixed(3)}, note: ${data.note.toFixed(2)} (${data.loop}.${data.synth})`; }); } @@ -58,7 +58,7 @@ export default class DittytoyJukebox { this.ditty = await fetch(`https://dittytoy.net/api/v1/ditty/${id}/`).then(e => e.json()).then(ditty => { $('song-name').innerHTML = `${ditty.title}`; - $('artist-name').innerHTML = `By ${ditty.user_id}`; + $('artist-name').innerHTML = `By ${ditty.user_id}`; return ditty; }); diff --git a/package.json b/package.json index 576228b..6b6216c 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,14 @@ "version": "0.1.2", "description": "", "keywords": [ - "dittytoy" + "Dittytoy", + "music", + "generative", + "DSP", + "audio", + "Sonic Pi", + "javascript", + "Web Audio API" ], "repository": "git@github.com:reindernijhoff/dittytoy-package.git", "author": "Reinder Nijhoff ",