diff --git a/packages/serial/README.md b/packages/serial/README.md new file mode 100644 index 00000000..5adb2282 --- /dev/null +++ b/packages/serial/README.md @@ -0,0 +1,4 @@ +# @strudel.cycles/serial + +This package adds webserial functionality to strudel Patterns, for e.g. sending messages to arduino microcontrollers. + diff --git a/packages/serial/package.json b/packages/serial/package.json new file mode 100644 index 00000000..5517c831 --- /dev/null +++ b/packages/serial/package.json @@ -0,0 +1,24 @@ +{ + "name": "@strudel.cycles/serial", + "version": "0.0.6", + "description": "Webserial API for strudel", + "main": "serial.mjs", + "repository": { + "type": "git", + "url": "git+https://github.com/tidalcycles/strudel.git" + }, + "keywords": [ + "titdalcycles", + "strudel", + "pattern", + "livecoding", + "algorave" + ], + "author": "Alex McLean ", + "license": "GPL-3.0-or-later", + "bugs": { + "url": "https://github.com/tidalcycles/strudel/issues" + }, + "homepage": "https://github.com/tidalcycles/strudel#readme", + "dependencies": {} +} diff --git a/packages/serial/serial.mjs b/packages/serial/serial.mjs new file mode 100644 index 00000000..00e78f58 --- /dev/null +++ b/packages/serial/serial.mjs @@ -0,0 +1,55 @@ +import { Pattern, isPattern } from '@strudel.cycles/core'; + +var serialWriter; +var choosing = false; + +export async function getWriter() { + if (choosing) { + return; + } + choosing = true; + if (serialWriter) { + return serialWriter; + } + if ('serial' in navigator) { + const port = await navigator.serial.requestPort(); + await port.open({ baudRate: 115200 }); + const textEncoder = new TextEncoderStream(); + const writableStreamClosed = textEncoder.readable.pipeTo(port.writable); + const writer = textEncoder.writable.getWriter(); + serialWriter = function (message) { + writer.write(message) + } + } + else { + throw('Webserial is not available in this browser.') + } +} + +const latency = 0.1; + +// Pattern.prototype.midi = function (output: string | number, channel = 1) { +Pattern.prototype.serial = function () { + return this._withEvent((event) => { + getWriter(); + if (!serialWriter) { + return event; + } + const onTrigger = (time, event, currentTime) => { + var message = ""; + if (typeof event.value === 'object') { + for (const [key, val] of Object.entries(event.value).flat()) { + message += `${key}:${val};` + } + } + else { + message = event.value; + } + const offset = (time - currentTime + latency) * 1000; + //const ts = Math.floor(Date.now() + offset); + console.log(`sending ${message}`) + window.setTimeout(serialWriter, offset, message) + }; + return event.setContext({ ...event.context, onTrigger }); + }); +}; diff --git a/repl/src/App.js b/repl/src/App.js index bfff776b..ef8e84d5 100644 --- a/repl/src/App.js +++ b/repl/src/App.js @@ -30,6 +30,7 @@ import '@strudel.cycles/core/speak.mjs'; import '@strudel.cycles/tone/pianoroll.mjs'; import '@strudel.cycles/tone/draw.mjs'; import '@strudel.cycles/osc/osc.mjs'; +import '@strudel.cycles/serial/serial.mjs'; import controls from '@strudel.cycles/core/controls.mjs'; extend(