From 0be0347f8bd9fe60df720f140f74d2e48f1fc1a1 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 28 Mar 2022 21:22:24 +0200 Subject: [PATCH 01/24] basic communication with superdirt --- packages/osc/README.md | 33 +++++++++ packages/osc/index.html | 22 ++++++ packages/osc/package-lock.json | 76 +++++++++++++++++++ packages/osc/package.json | 32 ++++++++ packages/osc/server.js | 129 +++++++++++++++++++++++++++++++++ packages/osc/tidal-sniffer.js | 125 ++++++++++++++++++++++++++++++++ 6 files changed, 417 insertions(+) create mode 100644 packages/osc/README.md create mode 100644 packages/osc/index.html create mode 100644 packages/osc/package-lock.json create mode 100644 packages/osc/package.json create mode 100644 packages/osc/server.js create mode 100644 packages/osc/tidal-sniffer.js diff --git a/packages/osc/README.md b/packages/osc/README.md new file mode 100644 index 00000000..e8f5069b --- /dev/null +++ b/packages/osc/README.md @@ -0,0 +1,33 @@ +# @strudel.cycles/osc + +OSC messaging between strudel and super collider? + +## Sniffing Tidal Messages + +```sh +npm run tidal-sniffer +``` + +Now open a .tidal file and play something. There should be logs like: + +```log +received: /dirt/play [ + '_id_', '1', + 'cps', 0.5625, + 'cutoff', 100, + 'cycle', 724.1875, + 'delta', 0.11111068725585938, + 'orbit', 0, + 's', 'arpy' +] +``` + +## Web Client + Server (WIP) + +```sh +npm run client +npm run server # another terminal +``` + +Then go to [http://localhost:4321](localhost:4321) and push the button. +In your server terminal, there should be a log. diff --git a/packages/osc/index.html b/packages/osc/index.html new file mode 100644 index 00000000..0890caa1 --- /dev/null +++ b/packages/osc/index.html @@ -0,0 +1,22 @@ + + + diff --git a/packages/osc/package-lock.json b/packages/osc/package-lock.json new file mode 100644 index 00000000..33516920 --- /dev/null +++ b/packages/osc/package-lock.json @@ -0,0 +1,76 @@ +{ + "name": "@strudel.cycles/osc", + "version": "0.0.1", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "@strudel.cycles/osc", + "version": "0.0.1", + "license": "GPL-3.0-or-later", + "dependencies": { + "osc-js": "^2.3.0" + } + }, + "node_modules/isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "peerDependencies": { + "ws": "*" + } + }, + "node_modules/osc-js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/osc-js/-/osc-js-2.3.0.tgz", + "integrity": "sha512-P2Oy9tf8Z9lQw8JZeR62HNqbKdxj7Kqbsag+ImiJvyxPDReGMVt5LtZbMh/7Ve/wbYEGODkQdFAaLHFVkIlHPw==", + "dependencies": { + "isomorphic-ws": "4.0.1", + "ws": "8.5.0" + } + }, + "node_modules/ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + } + }, + "dependencies": { + "isomorphic-ws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz", + "integrity": "sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==", + "requires": {} + }, + "osc-js": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/osc-js/-/osc-js-2.3.0.tgz", + "integrity": "sha512-P2Oy9tf8Z9lQw8JZeR62HNqbKdxj7Kqbsag+ImiJvyxPDReGMVt5LtZbMh/7Ve/wbYEGODkQdFAaLHFVkIlHPw==", + "requires": { + "isomorphic-ws": "4.0.1", + "ws": "8.5.0" + } + }, + "ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "requires": {} + } + } +} diff --git a/packages/osc/package.json b/packages/osc/package.json new file mode 100644 index 00000000..67a0cc76 --- /dev/null +++ b/packages/osc/package.json @@ -0,0 +1,32 @@ +{ + "name": "@strudel.cycles/osc", + "version": "0.0.1", + "description": "OSC messaging for strudel", + "main": "osc.mjs", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "server": "node server.js", + "tidal-sniffer": "node tidal-sniffer.js", + "client": "npx serve -p 4321" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/tidalcycles/strudel.git" + }, + "keywords": [ + "tidalcycles", + "strudel", + "pattern", + "livecoding", + "algorave" + ], + "author": "Felix Roos ", + "license": "GPL-3.0-or-later", + "bugs": { + "url": "https://github.com/tidalcycles/strudel/issues" + }, + "homepage": "https://github.com/tidalcycles/strudel#readme", + "dependencies": { + "osc-js": "^2.3.0" + } +} diff --git a/packages/osc/server.js b/packages/osc/server.js new file mode 100644 index 00000000..0d5c6986 --- /dev/null +++ b/packages/osc/server.js @@ -0,0 +1,129 @@ +const OSC = require('osc-js'); + +const config = { + receiver: 'ws', // @param {string} Where messages sent via 'send' method will be delivered to, 'ws' for Websocket clients, 'udp' for udp client + udpServer: { + host: 'localhost', // @param {string} Hostname of udp server to bind to + port: 57121, // @param {number} Port of udp client for messaging + // enabling the following line will receive tidal messages: + // port: 57120, // @param {number} Port of udp client for messaging + exclusive: false, // @param {boolean} Exclusive flag + }, + udpClient: { + host: 'localhost', // @param {string} Hostname of udp client for messaging + port: 57120, // @param {number} Port of udp client for messaging + }, + wsServer: { + host: 'localhost', // @param {string} Hostname of WebSocket server + port: 8080, // @param {number} Port of WebSocket server + }, +}; +const osc = new OSC({ plugin: new OSC.BridgePlugin(config) }); + +osc.open(); // start a WebSocket server on port 8080 + +console.log('osc client running on port', config.udpClient.port); +console.log('osc server running on port', config.udpServer.port); +console.log('websocket server running on port', config.wsServer.port); + +// listen for messages from the client +osc.on('*', (m) => { + // forward message to supercollider?? + const message = new OSC.Message(m.address, ...m.args); + osc.send(message); // will it even be received?? + console.log('forward:', m.address, m.args); +}); + +/* +example tidal messages: + +/* + +received: /dirt/play [ + '_id_', + '1', + 'cps', + 0.5625, + 'cycle', + 503.5, + 'delta', + 0.8888888359069824, + 'orbit', + 0, + 's', + 'bd' +] +received: /dirt/play [ + '_id_', '1', + 'cps', 0.5625, + 'cycle', 503.6666564941406, + 'delta', 0.592592716217041, + 'orbit', 0, + 's', 'hh' +] +received: /dirt/play [ + '_id_', + '1', + 'cps', + 0.5625, + 'cycle', + 504, + 'delta', + 0.8888888359069824, + 'orbit', + 0, + 's', + 'bd' +] +received: /dirt/play [ + '_id_', + '1', + 'cps', + 0.5625, + 'cycle', + 504, + 'delta', + 0.592592716217041, + 'orbit', + 0, + 's', + 'hh' +] +received: /dirt/play [ + '_id_', + '1', + 'cps', + 0.5625, + 'cycle', + 504.3333435058594, + 'delta', + 0.5925922393798828, + 'orbit', + 0, + 's', + 'hh' +] +received: /dirt/play [ + '_id_', + '1', + 'cps', + 0.5625, + 'cycle', + 504.5, + 'delta', + 0.8888888359069824, + 'orbit', + 0, + 's', + 'bd' +] +received: /dirt/play [ + '_id_', '1', + 'cps', 0.5625, + 'cycle', 504.6666564941406, + 'delta', 0.592592716217041, + 'orbit', 0, + 's', 'hh' +] + +*/ diff --git a/packages/osc/tidal-sniffer.js b/packages/osc/tidal-sniffer.js new file mode 100644 index 00000000..a1bdd659 --- /dev/null +++ b/packages/osc/tidal-sniffer.js @@ -0,0 +1,125 @@ +const OSC = require('osc-js'); + +const config = { + receiver: 'ws', // @param {string} Where messages sent via 'send' method will be delivered to, 'ws' for Websocket clients, 'udp' for udp client + udpServer: { + host: 'localhost', // @param {string} Hostname of udp server to bind to + port: 57120, // @param {number} Port of udp client for messaging + exclusive: false, // @param {boolean} Exclusive flag + }, + udpClient: { + host: 'localhost', // @param {string} Hostname of udp client for messaging + // port: 57120, // @param {number} Port of udp client for messaging + port: 41235, // @param {number} Port of udp client for messaging + }, + wsServer: { + host: 'localhost', // @param {string} Hostname of WebSocket server + port: 8080, // @param {number} Port of WebSocket server + }, +}; +const osc = new OSC({ plugin: new OSC.BridgePlugin(config) }); + +osc.open(); // start a WebSocket server on port 8080 + +console.log('osc client running on port', config.udpClient.port); +console.log('osc server running on port', config.udpServer.port); +console.log('websocket server running on port', config.wsServer.port); + +// listen for messages from the client +osc.on('*', (m) => { + console.log('received:', m.address, m.args); +}); + +/* +example tidal messages: + +/* + +received: /dirt/play [ + '_id_', + '1', + 'cps', + 0.5625, + 'cycle', + 503.5, + 'delta', + 0.8888888359069824, + 'orbit', + 0, + 's', + 'bd' +] +received: /dirt/play [ + '_id_', '1', + 'cps', 0.5625, + 'cycle', 503.6666564941406, + 'delta', 0.592592716217041, + 'orbit', 0, + 's', 'hh' +] +received: /dirt/play [ + '_id_', + '1', + 'cps', + 0.5625, + 'cycle', + 504, + 'delta', + 0.8888888359069824, + 'orbit', + 0, + 's', + 'bd' +] +received: /dirt/play [ + '_id_', + '1', + 'cps', + 0.5625, + 'cycle', + 504, + 'delta', + 0.592592716217041, + 'orbit', + 0, + 's', + 'hh' +] +received: /dirt/play [ + '_id_', + '1', + 'cps', + 0.5625, + 'cycle', + 504.3333435058594, + 'delta', + 0.5925922393798828, + 'orbit', + 0, + 's', + 'hh' +] +received: /dirt/play [ + '_id_', + '1', + 'cps', + 0.5625, + 'cycle', + 504.5, + 'delta', + 0.8888888359069824, + 'orbit', + 0, + 's', + 'bd' +] +received: /dirt/play [ + '_id_', '1', + 'cps', 0.5625, + 'cycle', 504.6666564941406, + 'delta', 0.592592716217041, + 'orbit', 0, + 's', 'hh' +] + +*/ From ed6cf2ecf1925a6a8ffd3ef78d930c3f9b3b626b Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Mon, 28 Mar 2022 21:24:44 +0200 Subject: [PATCH 02/24] enlarge button --- packages/osc/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/osc/index.html b/packages/osc/index.html index 0890caa1..e7630904 100644 --- a/packages/osc/index.html +++ b/packages/osc/index.html @@ -1,4 +1,4 @@ - + +

+ This page shows how skypack can be used to import strudel core directly into a simple html page.
+ No server, no bundler and no build setup is needed to run this! +

diff --git a/packages/core/examples/canvas.html b/packages/core/examples/canvas.html new file mode 100644 index 00000000..685cc56e --- /dev/null +++ b/packages/core/examples/canvas.html @@ -0,0 +1,34 @@ + + + + + From ac01a621beb6be0e739ae7d14078c577ee6b23c4 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 1 Apr 2022 15:37:50 +0100 Subject: [PATCH 11/24] workaround for osc-js timestamp bug, reduce latency --- packages/osc/osc.mjs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/osc/osc.mjs b/packages/osc/osc.mjs index 33586d29..34169ed0 100644 --- a/packages/osc/osc.mjs +++ b/packages/osc/osc.mjs @@ -3,17 +3,18 @@ import { Pattern, isPattern } from '@strudel.cycles/core/strudel.mjs'; const comm = new OSC(); comm.open(); +// TODO - get startTime from scheduler const startTime = Date.now(); -const latency = 0.2; +const latency = 0.1; Pattern.prototype.osc = function () { return this._withEvent((event) => { const onTrigger = (time, event) => { - console.log(time); const keyvals = Object.entries(event.value).flat(); - const ts = startTime + ((time+latency)*1000); + const ts = Math.floor(startTime + ((time + latency) * 1000)); const message = new OSC.Message('/dirt/play',...keyvals); - const bundle = new OSC.Bundle([message], ts) + const bundle = new OSC.Bundle([message], ts); + bundle.timestamp(ts); // workaround for https://github.com/adzialocha/osc-js/issues/60 comm.send(bundle); }; return event.setContext({ ...event.context, onTrigger }); From bf7c8cebc4468ac825f93cf7776ea00bf1c54c6a Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 1 Apr 2022 15:38:06 +0100 Subject: [PATCH 12/24] tidy --- packages/osc/server.js | 106 ----------------------------------------- 1 file changed, 106 deletions(-) diff --git a/packages/osc/server.js b/packages/osc/server.js index dd697d8c..f765b284 100644 --- a/packages/osc/server.js +++ b/packages/osc/server.js @@ -1,4 +1,3 @@ -const dgram = require('dgram') const OSC = require('osc-js'); const config = { @@ -29,108 +28,3 @@ osc.open(); // start a WebSocket server on port 8080 console.log('osc client running on port', config.udpClient.port); console.log('osc server running on port', config.udpServer.port); console.log('websocket server running on port', config.wsServer.port); - -// listen for messages from the client -// osc.on('*', (m) => { -// console.log("hmm!"); -// const ts = m.args.shift(); -// const message = new OSC.Message(m.address, ...m.args); -// console.log(m.args); -// const bundle = new OSC.Bundle([message], new Date(ts)) -// osc.send(bundle); -// console.log('forward:', bundle); -// }); - -/* -example tidal messages: - -/* - -received: /dirt/play [ - '_id_', - '1', - 'cps', - 0.5625, - 'cycle', - 503.5, - 'delta', - 0.8888888359069824, - 'orbit', - 0, - 's', - 'bd' -] -received: /dirt/play [ - '_id_', '1', - 'cps', 0.5625, - 'cycle', 503.6666564941406, - 'delta', 0.592592716217041, - 'orbit', 0, - 's', 'hh' -] -received: /dirt/play [ - '_id_', - '1', - 'cps', - 0.5625, - 'cycle', - 504, - 'delta', - 0.8888888359069824, - 'orbit', - 0, - 's', - 'bd' -] -received: /dirt/play [ - '_id_', - '1', - 'cps', - 0.5625, - 'cycle', - 504, - 'delta', - 0.592592716217041, - 'orbit', - 0, - 's', - 'hh' -] -received: /dirt/play [ - '_id_', - '1', - 'cps', - 0.5625, - 'cycle', - 504.3333435058594, - 'delta', - 0.5925922393798828, - 'orbit', - 0, - 's', - 'hh' -] -received: /dirt/play [ - '_id_', - '1', - 'cps', - 0.5625, - 'cycle', - 504.5, - 'delta', - 0.8888888359069824, - 'orbit', - 0, - 's', - 'bd' -] -received: /dirt/play [ - '_id_', '1', - 'cps', 0.5625, - 'cycle', 504.6666564941406, - 'delta', 0.592592716217041, - 'orbit', 0, - 's', 'hh' -] - -*/ From 723b239fda4cfae0d89df9fa7b9bfc3083545ab6 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 1 Apr 2022 23:15:39 +0100 Subject: [PATCH 13/24] formatting --- packages/osc/osc.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/osc/osc.mjs b/packages/osc/osc.mjs index 34169ed0..bfa3cf96 100644 --- a/packages/osc/osc.mjs +++ b/packages/osc/osc.mjs @@ -12,7 +12,7 @@ Pattern.prototype.osc = function () { const onTrigger = (time, event) => { const keyvals = Object.entries(event.value).flat(); const ts = Math.floor(startTime + ((time + latency) * 1000)); - const message = new OSC.Message('/dirt/play',...keyvals); + const message = new OSC.Message('/dirt/play', ...keyvals); const bundle = new OSC.Bundle([message], ts); bundle.timestamp(ts); // workaround for https://github.com/adzialocha/osc-js/issues/60 comm.send(bundle); From 46b2e82b4301831c35375a6fe9c39abbf172c4ff Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 1 Apr 2022 23:15:54 +0100 Subject: [PATCH 14/24] formatting --- packages/core/strudel.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/strudel.mjs b/packages/core/strudel.mjs index d285fe76..f6c4f7f9 100644 --- a/packages/core/strudel.mjs +++ b/packages/core/strudel.mjs @@ -780,7 +780,7 @@ class Pattern { // known as iter' in tidalcycles iterBack(times) { - return this.iter(times,true) + return this.iter(times, true) } _chunk(n, func, back = false) { From 89a7bd4e7d04622e18c192a206802758ca0b7025 Mon Sep 17 00:00:00 2001 From: alex Date: Fri, 1 Apr 2022 23:17:52 +0100 Subject: [PATCH 15/24] superdirt parameters --- packages/osc/superdirt.mjs | 23 +++++++++++++++++++++++ repl/src/App.js | 3 ++- 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 packages/osc/superdirt.mjs diff --git a/packages/osc/superdirt.mjs b/packages/osc/superdirt.mjs new file mode 100644 index 00000000..95615781 --- /dev/null +++ b/packages/osc/superdirt.mjs @@ -0,0 +1,23 @@ +import { Pattern } from '@strudel.cycles/core/strudel.mjs'; + +const _name = (name, pat) => pat.withValue(x => ({ [name]: x })); + +export const s = pat => _name("s", pat); +Pattern.prototype.s = function (pat) { return (this.union(s(pat))) }; +export const snd = s; +Pattern.prototype.snd = Pattern.prototype.s; +export const sound = s; +Pattern.prototype.sound = Pattern.prototype.s; +export const n = pat => _name("n", pat); +Pattern.prototype.n = function (pat) { return (this.union(n(pat))) }; +export const num = n; +Pattern.prototype.num = Pattern.prototype.n; +export const number = n; +Pattern.prototype.number = Pattern.prototype.n; +export const room = pat => _name("room", pat); +Pattern.prototype.room = function (pat) { return (this.union(room(pat))) }; +export const size = pat => _name("size", pat); +Pattern.prototype.size = function (pat) { return (this.union(size(pat))) }; +export const speed = pat => _name("speed", pat); +Pattern.prototype.speed = function (pat) { return (this.union(speed(pat))) }; + diff --git a/repl/src/App.js b/repl/src/App.js index 4f522aea..7f37b75f 100644 --- a/repl/src/App.js +++ b/repl/src/App.js @@ -29,8 +29,9 @@ import '@strudel.cycles/core/euclid.mjs'; import '@strudel.cycles/tone/pianoroll.mjs'; import '@strudel.cycles/tone/draw.mjs'; import '@strudel.cycles/osc/osc.mjs'; +import * as superdirt from '@strudel.cycles/osc/superdirt.mjs'; -extend(Tone, strudel, strudel.Pattern.prototype.bootstrap(), toneHelpers, voicingHelpers, drawHelpers, uiHelpers, { +extend(Tone, strudel, superdirt, strudel.Pattern.prototype.bootstrap(), toneHelpers, voicingHelpers, drawHelpers, uiHelpers, { gist, euclid, mini, From e28a411408100a1e102b2ed2681ee1cd23a09dff Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 2 Apr 2022 16:03:50 +0100 Subject: [PATCH 16/24] Make param args a sequence, remove some redundancy --- packages/osc/superdirt.mjs | 46 +++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/packages/osc/superdirt.mjs b/packages/osc/superdirt.mjs index 95615781..5f9c6efd 100644 --- a/packages/osc/superdirt.mjs +++ b/packages/osc/superdirt.mjs @@ -1,23 +1,37 @@ -import { Pattern } from '@strudel.cycles/core/strudel.mjs'; +import { Pattern, sequence } from '@strudel.cycles/core/strudel.mjs'; -const _name = (name, pat) => pat.withValue(x => ({ [name]: x })); +const _name = (name, ...pats) => sequence(...pats).withValue((x) => ({ [name]: x })); -export const s = pat => _name("s", pat); -Pattern.prototype.s = function (pat) { return (this.union(s(pat))) }; -export const snd = s; -Pattern.prototype.snd = Pattern.prototype.s; +const _unionise = (func) => + function (...pats) { + return this.union(func(...pats)); + }; + +export const s = (...pats) => _name('s', ...pats); +Pattern.prototype.s = _unionise(s); export const sound = s; Pattern.prototype.sound = Pattern.prototype.s; -export const n = pat => _name("n", pat); -Pattern.prototype.n = function (pat) { return (this.union(n(pat))) }; -export const num = n; -Pattern.prototype.num = Pattern.prototype.n; + +export const n = (pat) => _name('n', pat); +Pattern.prototype.n = _unionise(n); export const number = n; Pattern.prototype.number = Pattern.prototype.n; -export const room = pat => _name("room", pat); -Pattern.prototype.room = function (pat) { return (this.union(room(pat))) }; -export const size = pat => _name("size", pat); -Pattern.prototype.size = function (pat) { return (this.union(size(pat))) }; -export const speed = pat => _name("speed", pat); -Pattern.prototype.speed = function (pat) { return (this.union(speed(pat))) }; +export const room = (pat) => _name('room', pat); +Pattern.prototype.room = _unionise(room); + +export const size = (pat) => _name('size', pat); +Pattern.prototype.size = _unionise(size); + +export const speed = (pat) => _name('speed', pat); +Pattern.prototype.speed = _unionise(speed); + +export const squiz = (pat) => _name('squiz', pat); +Pattern.prototype.squiz = _unionise(squiz); + +// currently overwritten by tone package +export const gain = (pat) => _name('gain', pat); +Pattern.prototype.gain = _unionise(gain); + +export const vowel = (pat) => _name('vowel', pat); +Pattern.prototype.vowel = _unionise(vowel); \ No newline at end of file From acca363a4eae462c3606e335cb7891e85b1ff12b Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 2 Apr 2022 16:06:42 +0100 Subject: [PATCH 17/24] move osc/superdirt to core/controls --- packages/osc/superdirt.mjs | 37 ------------------------------------- repl/src/App.js | 4 ++-- 2 files changed, 2 insertions(+), 39 deletions(-) delete mode 100644 packages/osc/superdirt.mjs diff --git a/packages/osc/superdirt.mjs b/packages/osc/superdirt.mjs deleted file mode 100644 index 5f9c6efd..00000000 --- a/packages/osc/superdirt.mjs +++ /dev/null @@ -1,37 +0,0 @@ -import { Pattern, sequence } from '@strudel.cycles/core/strudel.mjs'; - -const _name = (name, ...pats) => sequence(...pats).withValue((x) => ({ [name]: x })); - -const _unionise = (func) => - function (...pats) { - return this.union(func(...pats)); - }; - -export const s = (...pats) => _name('s', ...pats); -Pattern.prototype.s = _unionise(s); -export const sound = s; -Pattern.prototype.sound = Pattern.prototype.s; - -export const n = (pat) => _name('n', pat); -Pattern.prototype.n = _unionise(n); -export const number = n; -Pattern.prototype.number = Pattern.prototype.n; - -export const room = (pat) => _name('room', pat); -Pattern.prototype.room = _unionise(room); - -export const size = (pat) => _name('size', pat); -Pattern.prototype.size = _unionise(size); - -export const speed = (pat) => _name('speed', pat); -Pattern.prototype.speed = _unionise(speed); - -export const squiz = (pat) => _name('squiz', pat); -Pattern.prototype.squiz = _unionise(squiz); - -// currently overwritten by tone package -export const gain = (pat) => _name('gain', pat); -Pattern.prototype.gain = _unionise(gain); - -export const vowel = (pat) => _name('vowel', pat); -Pattern.prototype.vowel = _unionise(vowel); \ No newline at end of file diff --git a/repl/src/App.js b/repl/src/App.js index 7f37b75f..df74aed5 100644 --- a/repl/src/App.js +++ b/repl/src/App.js @@ -29,9 +29,9 @@ import '@strudel.cycles/core/euclid.mjs'; import '@strudel.cycles/tone/pianoroll.mjs'; import '@strudel.cycles/tone/draw.mjs'; import '@strudel.cycles/osc/osc.mjs'; -import * as superdirt from '@strudel.cycles/osc/superdirt.mjs'; +import * as controls from '@strudel.cycles/core/controls.mjs'; -extend(Tone, strudel, superdirt, strudel.Pattern.prototype.bootstrap(), toneHelpers, voicingHelpers, drawHelpers, uiHelpers, { +extend(Tone, strudel, strudel.Pattern.prototype.bootstrap(), controls, toneHelpers, voicingHelpers, drawHelpers, uiHelpers, { gist, euclid, mini, From 64ea9b7c8ba1d3dbf626e90db7ab3eb8ac68de3e Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 2 Apr 2022 17:32:36 +0100 Subject: [PATCH 18/24] controls (renamed from packages/osc/superdirt.mjs --- packages/core/controls.mjs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 packages/core/controls.mjs diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs new file mode 100644 index 00000000..5f9c6efd --- /dev/null +++ b/packages/core/controls.mjs @@ -0,0 +1,37 @@ +import { Pattern, sequence } from '@strudel.cycles/core/strudel.mjs'; + +const _name = (name, ...pats) => sequence(...pats).withValue((x) => ({ [name]: x })); + +const _unionise = (func) => + function (...pats) { + return this.union(func(...pats)); + }; + +export const s = (...pats) => _name('s', ...pats); +Pattern.prototype.s = _unionise(s); +export const sound = s; +Pattern.prototype.sound = Pattern.prototype.s; + +export const n = (pat) => _name('n', pat); +Pattern.prototype.n = _unionise(n); +export const number = n; +Pattern.prototype.number = Pattern.prototype.n; + +export const room = (pat) => _name('room', pat); +Pattern.prototype.room = _unionise(room); + +export const size = (pat) => _name('size', pat); +Pattern.prototype.size = _unionise(size); + +export const speed = (pat) => _name('speed', pat); +Pattern.prototype.speed = _unionise(speed); + +export const squiz = (pat) => _name('squiz', pat); +Pattern.prototype.squiz = _unionise(squiz); + +// currently overwritten by tone package +export const gain = (pat) => _name('gain', pat); +Pattern.prototype.gain = _unionise(gain); + +export const vowel = (pat) => _name('vowel', pat); +Pattern.prototype.vowel = _unionise(vowel); \ No newline at end of file From 1fb1d15d4c14e6397a34684c703a9e1e15d602d7 Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 2 Apr 2022 18:38:20 +0100 Subject: [PATCH 19/24] add all the superdirt controls --- packages/core/controls.mjs | 557 +++++++++++++++++++++++++++++++++++-- 1 file changed, 532 insertions(+), 25 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 5f9c6efd..a4587f0b 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -1,5 +1,278 @@ import { Pattern, sequence } from '@strudel.cycles/core/strudel.mjs'; +const controls = {}; +const generic_params = [ + ['s', 's', 'sound'], + //['s', 'toArg', 'for internal sound routing'], + // ["f", "from", "for internal sound routing"), + //['f', 'to', 'for internal sound routing'], + ['f', 'accelerate', 'a pattern of numbers that speed up (or slow down) samples while they play.'], + ['f', 'amp', 'like @gain@, but linear.'], + [ + 'f', + 'attack', + 'a pattern of numbers to specify the attack time (in seconds) of an envelope applied to each sample.', + ], + ['f', 'bandf', 'a pattern of numbers from 0 to 1. Sets the center frequency of the band-pass filter.'], + ['f', 'bandq', 'a pattern of anumbers from 0 to 1. Sets the q-factor of the band-pass filter.'], + [ + 'f', + 'begin', + 'a pattern of numbers from 0 to 1. Skips the beginning of each sample, e.g. `0.25` to cut off the first quarter from each sample.', + ], + ['f', 'legato', 'controls the amount of overlap between two adjacent sounds'], + // ['f', 'clhatdecay', ''], + [ + 'f', + 'crush', + 'bit crushing, a pattern of numbers from 1 (for drastic reduction in bit-depth) to 16 (for barely no reduction).', + ], + [ + 'f', + 'coarse', + 'fake-resampling, a pattern of numbers for lowering the sample rate, i.e. 1 for original 2 for half, 3 for a third and so on.', + ], + ['i', 'channel', 'choose the channel the pattern is sent to in superdirt'], + [ + 'i', + 'cut', + 'In the style of classic drum-machines, `cut` will stop a playing sample as soon as another samples with in same cutgroup is to be played. An example would be an open hi-hat followed by a closed one, essentially muting the open.', + ], + ['f', 'cutoff', 'a pattern of numbers from 0 to 1. Applies the cutoff frequency of the low-pass filter.'], + // ['f', 'cutoffegint', ''], + ['f', 'decay', ''], + ['f', 'delay', 'a pattern of numbers from 0 to 1. Sets the level of the delay signal.'], + ['f', 'delayfeedback', 'a pattern of numbers from 0 to 1. Sets the amount of delay feedback.'], + ['f', 'delaytime', 'a pattern of numbers from 0 to 1. Sets the length of the delay.'], + ['f', 'detune', ''], + ['f', 'djf', 'DJ filter, below 0.5 is low pass filter, above is high pass filter.'], + [ + 'f', + 'dry', + 'when set to `1` will disable all reverb for this pattern. See `room` and `size` for more information about reverb.', + ], + [ + 'f', + 'end', + 'the same as `begin`, but cuts the end off samples, shortening them; e.g. `0.75` to cut off the last quarter of each sample.', + ], + [ + 'f', + 'fadeTime', + "Used when using begin/end or chop/striate and friends, to change the fade out time of the 'grain' envelope.", + ], + [ + 'f', + 'fadeInTime', + 'As with fadeTime, but controls the fade in time of the grain envelope. Not used if the grain begins at position 0 in the sample.', + ], + ['f', 'freq', ''], + [ + 'f', + 'gain', + 'a pattern of numbers that specify volume. Values less than 1 make the sound quieter. Values greater than 1 make the sound louder. For the linear equivalent, see @amp@.', + ], + ['f', 'gate', ''], + // ['f', 'hatgrain', ''], + [ + 'f', + 'hcutoff', + 'a pattern of numbers from 0 to 1. Applies the cutoff frequency of the high-pass filter. Also has alias @hpf@', + ], + [ + 'f', + 'hold', + 'a pattern of numbers to specify the hold time (in seconds) of an envelope applied to each sample. Only takes effect if `attack` and `release` are also specified.', + ], + [ + 'f', + 'hresonance', + 'a pattern of numbers from 0 to 1. Applies the resonance of the high-pass filter. Has alias @hpq@', + ], + // ['f', 'lagogo', ''], + // ['f', 'lclap', ''], + // ['f', 'lclaves', ''], + // ['f', 'lclhat', ''], + // ['f', 'lcrash', ''], + ['f', 'leslie', ''], + ['f', 'lrate', ''], + ['f', 'lsize', ''], + // ['f', 'lfo', ''], + // ['f', 'lfocutoffint', ''], + // ['f', 'lfodelay', ''], + // ['f', 'lfoint', ''], + // ['f', 'lfopitchint', ''], + // ['f', 'lfoshape', ''], + // ['f', 'lfosync', ''], + // ['f', 'lhitom', ''], + // ['f', 'lkick', ''], + // ['f', 'llotom', ''], + [ + 'f', + 'lock', + 'A pattern of numbers. Specifies whether delaytime is calculated relative to cps. When set to 1, delaytime is a direct multiple of a cycle.', + ], + ['f', 'loop', 'loops the sample (from `begin` to `end`) the specified number of times.'], + // ['f', 'lophat', ''], + // ['f', 'lsnare', ''], + ['f', 'n', 'The note or sample number to choose for a synth or sampleset'], + ['f', 'note', 'The note or pitch to play a sound or synth with'], + ['f', 'degree', ''], + ['f', 'mtranspose', ''], + ['f', 'ctranspose', ''], + ['f', 'harmonic', ''], + ['f', 'stepsPerOctave', ''], + ['f', 'octaveR', ''], + [ + 'f', + 'nudge', + 'Nudges events into the future by the specified number of seconds. Negative numbers work up to a point as well (due to internal latency)', + ], + ['i', 'octave', ''], + ['f', 'offset', ''], + // ['f', 'ophatdecay', ''], + [ + 'i', + 'orbit', + 'a pattern of numbers. An `orbit` is a global parameter context for patterns. Patterns with the same orbit will share hardware output bus offset and global effects, e.g. reverb and delay. The maximum number of orbits is specified in the superdirt startup, numbers higher than maximum will wrap around.', + ], + ['f', 'overgain', ''], + ['f', 'overshape', ''], + [ + 'f', + 'pan', + 'a pattern of numbers between 0 and 1, from left to right (assuming stereo), once round a circle (assuming multichannel)', + ], + [ + 'f', + 'panspan', + 'a pattern of numbers between -inf and inf, which controls how much multichannel output is fanned out (negative is backwards ordering)', + ], + [ + 'f', + 'pansplay', + 'a pattern of numbers between 0.0 and 1.0, which controls the multichannel spread range (multichannel only)', + ], + [ + 'f', + 'panwidth', + 'a pattern of numbers between 0.0 and inf, which controls how much each channel is distributed over neighbours (multichannel only)', + ], + [ + 'f', + 'panorient', + 'a pattern of numbers between -1.0 and 1.0, which controls the relative position of the centre pan in a pair of adjacent speakers (multichannel only)', + ], + // ['f', 'pitch1', ''], + // ['f', 'pitch2', ''], + // ['f', 'pitch3', ''], + // ['f', 'portamento', ''], + ['f', 'rate', "used in SuperDirt softsynths as a control rate or 'speed'"], + [ + 'f', + 'release', + 'a pattern of numbers to specify the release time (in seconds) of an envelope applied to each sample.', + ], + ['f', 'resonance', 'a pattern of numbers from 0 to 1. Specifies the resonance of the low-pass filter.'], + ['f', 'room', 'a pattern of numbers from 0 to 1. Sets the level of reverb.'], + // ['f', 'sagogo', ''], + // ['f', 'sclap', ''], + // ['f', 'sclaves', ''], + // ['f', 'scrash', ''], + ['f', 'semitone', ''], + [ + 'f', + 'shape', + 'wave shaping distortion, a pattern of numbers from 0 for no distortion up to 1 for loads of distortion.', + ], + [ + 'f', + 'size', + 'a pattern of numbers from 0 to 1. Sets the perceptual size (reverb time) of the `room` to be used in reverb.', + ], + ['f', 'slide', ''], + [ + 'f', + 'speed', + 'a pattern of numbers which changes the speed of sample playback, i.e. a cheap way of changing pitch. Negative values will play the sample backwards!', + ], + ['f', 'squiz', ''], + ['f', 'stutterdepth', ''], + ['f', 'stuttertime', ''], + ['f', 'sustain', ''], + ['f', 'timescale', ''], + ['f', 'timescalewin', ''], + // ['f', 'tomdecay', ''], + [ + 's', + 'unit', + 'used in conjunction with `speed`, accepts values of "r" (rate, default behavior), "c" (cycles), or "s" (seconds). Using `unit "c"` means `speed` will be interpreted in units of cycles, e.g. `speed "1"` means samples will be stretched to fill a cycle. Using `unit "s"` means the playback speed will be adjusted so that the duration is the number of seconds specified by `speed`.', + ], + ['f', 'velocity', ''], + // ['f', 'vcfegint', ''], + // ['f', 'vcoegint', ''], + ['f', 'voice', ''], + [ + 's', + 'vowel', + 'formant filter to make things sound like vowels, a pattern of either `a`, `e`, `i`, `o` or `u`. Use a rest (`~`) for no effect.', + ], + ['f', 'waveloss', ''], + ['f', 'dur', ''], + // ['f', 'modwheel', ''], + ['f', 'expression', ''], + ['f', 'sustainpedal', ''], + ['f', 'tremolodepth', "Tremolo Audio DSP effect | params are 'tremolorate' and 'tremolodepth'"], + ['f', 'tremolorate', "Tremolo Audio DSP effect | params are 'tremolorate' and 'tremolodepth'"], + ['f', 'phaserdepth', "Phaser Audio DSP effect | params are 'phaserrate' and 'phaserdepth'"], + ['f', 'phaserrate', "Phaser Audio DSP effect | params are 'phaserrate' and 'phaserdepth'"], + ['f', 'fshift', 'frequency shifter'], + ['f', 'fshiftnote', 'frequency shifter'], + ['f', 'fshiftphase', 'frequency shifter'], + ['f', 'triode', 'tube distortion'], + ['f', 'krush', 'shape/bass enhancer'], + ['f', 'kcutoff', ''], + ['f', 'octer', 'octaver effect'], + ['f', 'octersub', 'octaver effect'], + ['f', 'octersubsub', 'octaver effect'], + ['f', 'ring', 'ring modulation'], + ['f', 'ringf', 'ring modulation'], + ['f', 'ringdf', 'ring modulation'], + ['f', 'distort', 'noisy fuzzy distortion'], + ['f', 'freeze', 'Spectral freeze'], + ['f', 'xsdelay', ''], + ['f', 'tsdelay', ''], + ['f', 'real', 'Spectral conform'], + ['f', 'imag', ''], + ['f', 'enhance', 'Spectral enhance'], + ['f', 'partials', ''], + ['f', 'comb', 'Spectral comb'], + ['f', 'smear', 'Spectral smear'], + ['f', 'scram', 'Spectral scramble'], + ['f', 'binshift', 'Spectral binshift'], + ['f', 'hbrick', 'High pass sort of spectral filter'], + ['f', 'lbrick', 'Low pass sort of spectral filter'], + ['f', 'midichan', ''], + ['f', 'control', ''], + ['f', 'ccn', ''], + ['f', 'ccv', ''], + ['f', 'polyTouch', ''], + ['f', 'midibend', ''], + ['f', 'miditouch', ''], + ['f', 'ctlNum', ''], + ['f', 'frameRate', ''], + ['f', 'frames', ''], + ['f', 'hours', ''], + ['s', 'midicmd', ''], + ['f', 'minutes', ''], + ['f', 'progNum', ''], + ['f', 'seconds', ''], + ['f', 'songPtr', ''], + ['f', 'uid', ''], + ['f', 'val', ''], + ['f', 'cps', ''], +]; + const _name = (name, ...pats) => sequence(...pats).withValue((x) => ({ [name]: x })); const _unionise = (func) => @@ -7,31 +280,265 @@ const _unionise = (func) => return this.union(func(...pats)); }; -export const s = (...pats) => _name('s', ...pats); -Pattern.prototype.s = _unionise(s); -export const sound = s; -Pattern.prototype.sound = Pattern.prototype.s; +generic_params.forEach(([type, name, description]) => { + controls[name] = (...pats) => _name(name, ...pats); + Pattern.prototype[name] = _unionise(controls[name]); +}); -export const n = (pat) => _name('n', pat); -Pattern.prototype.n = _unionise(n); -export const number = n; -Pattern.prototype.number = Pattern.prototype.n; +console.log(Object.keys(controls).sort().join(', ')); -export const room = (pat) => _name('room', pat); -Pattern.prototype.room = _unionise(room); +const { + accelerate, + amp, + attack, + bandf, + bandq, + begin, + binshift, + ccn, + ccv, + channel, + coarse, + comb, + control, + cps, + crush, + ctlNum, + ctranspose, + cut, + cutoff, + decay, + degree, + delay, + delayfeedback, + delaytime, + detune, + distort, + djf, + dry, + dur, + end, + enhance, + expression, + fadeInTime, + fadeTime, + frameRate, + frames, + freeze, + freq, + fshift, + fshiftnote, + fshiftphase, + gain, + gate, + harmonic, + hbrick, + hcutoff, + hold, + hours, + hresonance, + imag, + kcutoff, + krush, + lbrick, + legato, + leslie, + lock, + loop, + lrate, + lsize, + midibend, + midichan, + midicmd, + miditouch, + minutes, + mtranspose, + n, + note, + nudge, + octave, + octaveR, + octer, + octersub, + octersubsub, + offset, + orbit, + overgain, + overshape, + pan, + panorient, + panspan, + pansplay, + panwidth, + partials, + phaserdepth, + phaserrate, + polyTouch, + progNum, + rate, + real, + release, + resonance, + ring, + ringdf, + ringf, + room, + s, + scram, + seconds, + semitone, + shape, + size, + slide, + smear, + songPtr, + speed, + squiz, + stepsPerOctave, + stutterdepth, + stuttertime, + sustain, + sustainpedal, + timescale, + timescalewin, + tremolodepth, + tremolorate, + triode, + tsdelay, + uid, + unit, + val, + velocity, + voice, + vowel, + waveloss, + xsdelay, +} = controls; -export const size = (pat) => _name('size', pat); -Pattern.prototype.size = _unionise(size); - -export const speed = (pat) => _name('speed', pat); -Pattern.prototype.speed = _unionise(speed); - -export const squiz = (pat) => _name('squiz', pat); -Pattern.prototype.squiz = _unionise(squiz); - -// currently overwritten by tone package -export const gain = (pat) => _name('gain', pat); -Pattern.prototype.gain = _unionise(gain); - -export const vowel = (pat) => _name('vowel', pat); -Pattern.prototype.vowel = _unionise(vowel); \ No newline at end of file +export { + accelerate, + amp, + attack, + bandf, + bandq, + begin, + binshift, + ccn, + ccv, + channel, + coarse, + comb, + control, + cps, + crush, + ctlNum, + ctranspose, + cut, + cutoff, + decay, + degree, + delay, + delayfeedback, + delaytime, + detune, + distort, + djf, + dry, + dur, + end, + enhance, + expression, + fadeInTime, + fadeTime, + frameRate, + frames, + freeze, + freq, + fshift, + fshiftnote, + fshiftphase, + gain, + gate, + harmonic, + hbrick, + hcutoff, + hold, + hours, + hresonance, + imag, + kcutoff, + krush, + lbrick, + legato, + leslie, + lock, + loop, + lrate, + lsize, + midibend, + midichan, + midicmd, + miditouch, + minutes, + mtranspose, + n, + note, + nudge, + octave, + octaveR, + octer, + octersub, + octersubsub, + offset, + orbit, + overgain, + overshape, + pan, + panorient, + panspan, + pansplay, + panwidth, + partials, + phaserdepth, + phaserrate, + polyTouch, + progNum, + rate, + real, + release, + resonance, + ring, + ringdf, + ringf, + room, + s, + scram, + seconds, + semitone, + shape, + size, + slide, + smear, + songPtr, + speed, + squiz, + stepsPerOctave, + stutterdepth, + stuttertime, + sustain, + sustainpedal, + timescale, + timescalewin, + tremolodepth, + tremolorate, + triode, + tsdelay, + uid, + unit, + val, + velocity, + voice, + vowel, + waveloss, + xsdelay, +}; \ No newline at end of file From 7bbac0ed23887c8c8671e224349a1a7f6cad60ee Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 2 Apr 2022 18:40:27 +0100 Subject: [PATCH 20/24] newline --- packages/core/controls.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index a4587f0b..093fcf0f 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -541,4 +541,4 @@ export { vowel, waveloss, xsdelay, -}; \ No newline at end of file +}; From c3b15fffa4bc560a26b0169a0ab2a69cd277ea6f Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 2 Apr 2022 18:48:30 +0100 Subject: [PATCH 21/24] remove duplication --- packages/core/controls.mjs | 258 +------------------------------------ repl/src/App.js | 2 +- 2 files changed, 2 insertions(+), 258 deletions(-) diff --git a/packages/core/controls.mjs b/packages/core/controls.mjs index 093fcf0f..5ae19da6 100644 --- a/packages/core/controls.mjs +++ b/packages/core/controls.mjs @@ -285,260 +285,4 @@ generic_params.forEach(([type, name, description]) => { Pattern.prototype[name] = _unionise(controls[name]); }); -console.log(Object.keys(controls).sort().join(', ')); - -const { - accelerate, - amp, - attack, - bandf, - bandq, - begin, - binshift, - ccn, - ccv, - channel, - coarse, - comb, - control, - cps, - crush, - ctlNum, - ctranspose, - cut, - cutoff, - decay, - degree, - delay, - delayfeedback, - delaytime, - detune, - distort, - djf, - dry, - dur, - end, - enhance, - expression, - fadeInTime, - fadeTime, - frameRate, - frames, - freeze, - freq, - fshift, - fshiftnote, - fshiftphase, - gain, - gate, - harmonic, - hbrick, - hcutoff, - hold, - hours, - hresonance, - imag, - kcutoff, - krush, - lbrick, - legato, - leslie, - lock, - loop, - lrate, - lsize, - midibend, - midichan, - midicmd, - miditouch, - minutes, - mtranspose, - n, - note, - nudge, - octave, - octaveR, - octer, - octersub, - octersubsub, - offset, - orbit, - overgain, - overshape, - pan, - panorient, - panspan, - pansplay, - panwidth, - partials, - phaserdepth, - phaserrate, - polyTouch, - progNum, - rate, - real, - release, - resonance, - ring, - ringdf, - ringf, - room, - s, - scram, - seconds, - semitone, - shape, - size, - slide, - smear, - songPtr, - speed, - squiz, - stepsPerOctave, - stutterdepth, - stuttertime, - sustain, - sustainpedal, - timescale, - timescalewin, - tremolodepth, - tremolorate, - triode, - tsdelay, - uid, - unit, - val, - velocity, - voice, - vowel, - waveloss, - xsdelay, -} = controls; - -export { - accelerate, - amp, - attack, - bandf, - bandq, - begin, - binshift, - ccn, - ccv, - channel, - coarse, - comb, - control, - cps, - crush, - ctlNum, - ctranspose, - cut, - cutoff, - decay, - degree, - delay, - delayfeedback, - delaytime, - detune, - distort, - djf, - dry, - dur, - end, - enhance, - expression, - fadeInTime, - fadeTime, - frameRate, - frames, - freeze, - freq, - fshift, - fshiftnote, - fshiftphase, - gain, - gate, - harmonic, - hbrick, - hcutoff, - hold, - hours, - hresonance, - imag, - kcutoff, - krush, - lbrick, - legato, - leslie, - lock, - loop, - lrate, - lsize, - midibend, - midichan, - midicmd, - miditouch, - minutes, - mtranspose, - n, - note, - nudge, - octave, - octaveR, - octer, - octersub, - octersubsub, - offset, - orbit, - overgain, - overshape, - pan, - panorient, - panspan, - pansplay, - panwidth, - partials, - phaserdepth, - phaserrate, - polyTouch, - progNum, - rate, - real, - release, - resonance, - ring, - ringdf, - ringf, - room, - s, - scram, - seconds, - semitone, - shape, - size, - slide, - smear, - songPtr, - speed, - squiz, - stepsPerOctave, - stutterdepth, - stuttertime, - sustain, - sustainpedal, - timescale, - timescalewin, - tremolodepth, - tremolorate, - triode, - tsdelay, - uid, - unit, - val, - velocity, - voice, - vowel, - waveloss, - xsdelay, -}; +export default controls; diff --git a/repl/src/App.js b/repl/src/App.js index df74aed5..e3fe8f70 100644 --- a/repl/src/App.js +++ b/repl/src/App.js @@ -29,7 +29,7 @@ import '@strudel.cycles/core/euclid.mjs'; import '@strudel.cycles/tone/pianoroll.mjs'; import '@strudel.cycles/tone/draw.mjs'; import '@strudel.cycles/osc/osc.mjs'; -import * as controls from '@strudel.cycles/core/controls.mjs'; +import controls from '@strudel.cycles/core/controls.mjs'; extend(Tone, strudel, strudel.Pattern.prototype.bootstrap(), controls, toneHelpers, voicingHelpers, drawHelpers, uiHelpers, { gist, From 86b1e992a158a56d8fa03aef0f6c2407c803015b Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 2 Apr 2022 21:46:56 +0200 Subject: [PATCH 22/24] fix absolute time --- packages/osc/osc.mjs | 8 +++----- repl/src/useCycle.mjs | 9 +++++++-- repl/src/useRepl.mjs | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/osc/osc.mjs b/packages/osc/osc.mjs index bfa3cf96..3baefb0f 100644 --- a/packages/osc/osc.mjs +++ b/packages/osc/osc.mjs @@ -1,17 +1,15 @@ import OSC from './node_modules/osc-js/lib/osc.js'; -import { Pattern, isPattern } from '@strudel.cycles/core/strudel.mjs'; +import { Pattern } from '@strudel.cycles/core/strudel.mjs'; const comm = new OSC(); comm.open(); -// TODO - get startTime from scheduler -const startTime = Date.now(); const latency = 0.1; Pattern.prototype.osc = function () { return this._withEvent((event) => { - const onTrigger = (time, event) => { + const onTrigger = (time, event, startedAt) => { const keyvals = Object.entries(event.value).flat(); - const ts = Math.floor(startTime + ((time + latency) * 1000)); + const ts = Math.floor(startedAt + (time + latency) * 1000); const message = new OSC.Message('/dirt/play', ...keyvals); const bundle = new OSC.Bundle([message], ts); bundle.timestamp(ts); // workaround for https://github.com/adzialocha/osc-js/issues/60 diff --git a/repl/src/useCycle.mjs b/repl/src/useCycle.mjs index 199ffb59..9c5f16c4 100644 --- a/repl/src/useCycle.mjs +++ b/repl/src/useCycle.mjs @@ -10,6 +10,8 @@ import { State, TimeSpan } from '@strudel.cycles/core'; ready?: boolean; // if false, query will not be called on change props } */ +export let startedAt; + // function useCycle(props: UseCycleProps) { function useCycle(props) { // onX must use useCallback! @@ -42,7 +44,7 @@ function useCycle(props) { ?.filter((event) => event.part.begin.equals(event.whole.begin)) .forEach((event) => { Tone.getTransport().schedule((time) => { - onEvent(time, event); + onEvent(time, event, startedAt); Tone.Draw.schedule(() => { // do drawing or DOM manipulation here onDraw?.(time, event); @@ -57,7 +59,10 @@ function useCycle(props) { const start = async () => { setStarted(true); - await Tone.start(); + if (!startedAt) { + await Tone.start(); + startedAt = Date.now() - Tone.getContext().currentTime * 1000; + } Tone.getTransport().start('+0.1'); }; const stop = () => { diff --git a/repl/src/useRepl.mjs b/repl/src/useRepl.mjs index f1a3dcde..ce823f9b 100644 --- a/repl/src/useRepl.mjs +++ b/repl/src/useRepl.mjs @@ -27,7 +27,7 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw }) { const cycle = useCycle({ onDraw, onEvent: useCallback( - (time, event) => { + (time, event, startedAt) => { try { onEvent?.(event); const { onTrigger, velocity } = event.context; @@ -41,7 +41,7 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw }) { /* console.warn('no instrument chosen', event); throw new Error(`no instrument chosen for ${JSON.stringify(event)}`); */ } else { - onTrigger(time, event); + onTrigger(time, event, startedAt); } } catch (err) { console.warn(err); From 0c726d835df70fcbaf71ffca07a3c0cf816050a3 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 2 Apr 2022 22:40:35 +0200 Subject: [PATCH 23/24] fix keybindings on safari --- repl/src/App.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/repl/src/App.js b/repl/src/App.js index e3fe8f70..0a928ce0 100644 --- a/repl/src/App.js +++ b/repl/src/App.js @@ -77,8 +77,10 @@ function App() { if (e.ctrlKey || e.altKey) { if (e.code === 'Enter') { await activateCode(); + e.preventDefault(); } else if (e.code === 'Period') { cycle.stop(); + e.preventDefault(); } } }; From c8e9fe519f5c22a83b89cba49cb7cee4b8cd02b0 Mon Sep 17 00:00:00 2001 From: Felix Roos Date: Sat, 2 Apr 2022 22:41:04 +0200 Subject: [PATCH 24/24] osc timing now hopefully works everywhere --- packages/osc/osc.mjs | 7 +++++-- repl/src/useCycle.mjs | 9 ++------- repl/src/useRepl.mjs | 4 ++-- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/packages/osc/osc.mjs b/packages/osc/osc.mjs index 3baefb0f..932e803d 100644 --- a/packages/osc/osc.mjs +++ b/packages/osc/osc.mjs @@ -7,9 +7,12 @@ const latency = 0.1; Pattern.prototype.osc = function () { return this._withEvent((event) => { - const onTrigger = (time, event, startedAt) => { + const onTrigger = (time, event, currentTime) => { + // time should be audio time of onset + // currentTime should be current time of audio context (slightly before time) const keyvals = Object.entries(event.value).flat(); - const ts = Math.floor(startedAt + (time + latency) * 1000); + const offset = (time - currentTime + latency) * 1000; + const ts = Math.floor(Date.now() + offset); const message = new OSC.Message('/dirt/play', ...keyvals); const bundle = new OSC.Bundle([message], ts); bundle.timestamp(ts); // workaround for https://github.com/adzialocha/osc-js/issues/60 diff --git a/repl/src/useCycle.mjs b/repl/src/useCycle.mjs index 9c5f16c4..908193f9 100644 --- a/repl/src/useCycle.mjs +++ b/repl/src/useCycle.mjs @@ -10,8 +10,6 @@ import { State, TimeSpan } from '@strudel.cycles/core'; ready?: boolean; // if false, query will not be called on change props } */ -export let startedAt; - // function useCycle(props: UseCycleProps) { function useCycle(props) { // onX must use useCallback! @@ -44,7 +42,7 @@ function useCycle(props) { ?.filter((event) => event.part.begin.equals(event.whole.begin)) .forEach((event) => { Tone.getTransport().schedule((time) => { - onEvent(time, event, startedAt); + onEvent(time, event, Tone.getContext().currentTime); Tone.Draw.schedule(() => { // do drawing or DOM manipulation here onDraw?.(time, event); @@ -59,10 +57,7 @@ function useCycle(props) { const start = async () => { setStarted(true); - if (!startedAt) { - await Tone.start(); - startedAt = Date.now() - Tone.getContext().currentTime * 1000; - } + await Tone.start(); Tone.getTransport().start('+0.1'); }; const stop = () => { diff --git a/repl/src/useRepl.mjs b/repl/src/useRepl.mjs index ce823f9b..061cd2f3 100644 --- a/repl/src/useRepl.mjs +++ b/repl/src/useRepl.mjs @@ -27,7 +27,7 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw }) { const cycle = useCycle({ onDraw, onEvent: useCallback( - (time, event, startedAt) => { + (time, event, currentTime) => { try { onEvent?.(event); const { onTrigger, velocity } = event.context; @@ -41,7 +41,7 @@ function useRepl({ tune, defaultSynth, autolink = true, onEvent, onDraw }) { /* console.warn('no instrument chosen', event); throw new Error(`no instrument chosen for ${JSON.stringify(event)}`); */ } else { - onTrigger(time, event, startedAt); + onTrigger(time, event, currentTime); } } catch (err) { console.warn(err);