From b52c42c31d8e908fb374c4a61534ec40ff8c978b Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 19 Apr 2022 13:40:01 +0100 Subject: [PATCH 1/2] First go at serial output --- packages/serial/README.md | 4 +++ packages/serial/package.json | 24 ++++++++++++++++ packages/serial/serial.mjs | 55 ++++++++++++++++++++++++++++++++++++ repl/src/App.js | 1 + 4 files changed, 84 insertions(+) create mode 100644 packages/serial/README.md create mode 100644 packages/serial/package.json create mode 100644 packages/serial/serial.mjs 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( From d7dc158861dda7ca4190d3131a6b5af9e1657bdb Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 19 Apr 2022 14:28:16 +0100 Subject: [PATCH 2/2] Tidy up --- packages/serial/serial.mjs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/packages/serial/serial.mjs b/packages/serial/serial.mjs index 00e78f58..312a8c8b 100644 --- a/packages/serial/serial.mjs +++ b/packages/serial/serial.mjs @@ -3,7 +3,7 @@ import { Pattern, isPattern } from '@strudel.cycles/core'; var serialWriter; var choosing = false; -export async function getWriter() { +export async function getWriter(br=115200) { if (choosing) { return; } @@ -13,7 +13,7 @@ export async function getWriter() { } if ('serial' in navigator) { const port = await navigator.serial.requestPort(); - await port.open({ baudRate: 115200 }); + await port.open({ baudRate: br }); const textEncoder = new TextEncoderStream(); const writableStreamClosed = textEncoder.readable.pipeTo(port.writable); const writer = textEncoder.writable.getWriter(); @@ -29,11 +29,10 @@ export async function getWriter() { const latency = 0.1; // Pattern.prototype.midi = function (output: string | number, channel = 1) { -Pattern.prototype.serial = function () { +Pattern.prototype.serial = async function (...args) { return this._withEvent((event) => { - getWriter(); if (!serialWriter) { - return event; + getWriter(...args); } const onTrigger = (time, event, currentTime) => { var message = ""; @@ -46,9 +45,7 @@ Pattern.prototype.serial = function () { 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) + window.setTimeout(serialWriter, offset, message); }; return event.setContext({ ...event.context, onTrigger }); });