Merge pull request #98 from Bubobubobubobubo/osc
Support for OSC Input/Output
This commit is contained in:
10
README.md
10
README.md
@ -17,24 +17,24 @@
|
||||
</p>
|
||||
</p>
|
||||
|
||||
---------------------
|
||||
---
|
||||
|
||||
Topos is a web based live coding environment. Topos is capable of many things:
|
||||
|
||||
- it is a music sequencer made for improvisation and composition alike
|
||||
- it is a synthesizer capable of additive, substractive, FM and wavetable
|
||||
synthesis, backed up by a [powerful web based audio engine](https://www.npmjs.com/package/superdough)
|
||||
synthesis, backed up by a [powerful web based audio engine](https://www.npmjs.com/package/superdough)
|
||||
- it can also generate video thanks to [Hydra](https://hydra.ojack.xyz/) and
|
||||
custom oscilloscopes, frequency visualizers and image sequencing capabilities
|
||||
custom oscilloscopes, frequency visualizers and image sequencing capabilities
|
||||
- it can be used to sequence other MIDI devices (and soon.. OSC!)
|
||||
- it is made to be used without the need of installing anything, always ready at
|
||||
[https://topos.live](https://topos.live)
|
||||
- Topos is also an emulation and personal extension of the [Monome Teletype](https://monome.org/docs/teletype/)
|
||||
|
||||
---------------------
|
||||
---
|
||||
|
||||

|
||||
|
||||
|
||||
## Disclaimer
|
||||
|
||||
**Topos** is still a young project developed by two hobbyists :) Contributions are welcome! We wish to be as inclusive and welcoming as possible to your ideas and suggestions! The software is working quite well and we are continuously striving to improve it.
|
||||
|
||||
36
ToposServer/OSCtoTopos.js
Normal file
36
ToposServer/OSCtoTopos.js
Normal file
@ -0,0 +1,36 @@
|
||||
const WebSocket = require("ws");
|
||||
const osc = require("osc");
|
||||
|
||||
const cleanIncomingOSC = (oscMsg) => {
|
||||
let data = oscMsg.args;
|
||||
// Remove information about type of data
|
||||
data = data.map((item) => {
|
||||
return item.value;
|
||||
});
|
||||
return { data: data, address: oscMsg.address };
|
||||
};
|
||||
|
||||
// ==============================================
|
||||
// Receiving and forwarding OSC UDP messages
|
||||
// Create an osc.js UDP Port listening on port 57121.
|
||||
console.log("> OSC Input: 127.0.0.1:30000");
|
||||
const wss = new WebSocket.Server({ port: 3001 });
|
||||
var udpPort = new osc.UDPPort({
|
||||
localAddress: "0.0.0.0",
|
||||
localPort: 30000,
|
||||
metadata: true,
|
||||
});
|
||||
udpPort.on("message", function (oscMsg, timeTag, info) {
|
||||
console.log(
|
||||
`> Incoming OSC to ${oscMsg.address}:`,
|
||||
oscMsg.args.map((item) => {
|
||||
return item.value;
|
||||
}),
|
||||
);
|
||||
wss.clients.forEach((client) => {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(JSON.stringify(cleanIncomingOSC(oscMsg)));
|
||||
}
|
||||
});
|
||||
});
|
||||
udpPort.open();
|
||||
83
ToposServer/ToposToOSC.js
Normal file
83
ToposServer/ToposToOSC.js
Normal file
@ -0,0 +1,83 @@
|
||||
const WebSocket = require("ws");
|
||||
const osc = require("osc");
|
||||
|
||||
// Listening to WebSocket messages
|
||||
const wss = new WebSocket.Server({ port: 3000 });
|
||||
|
||||
// Setting up for message broadcasting
|
||||
wss.on("connection", function (ws) {
|
||||
console.log("> Client connected");
|
||||
ws.on("message", function (data) {
|
||||
try {
|
||||
const message = JSON.parse(data);
|
||||
sendOscMessage(
|
||||
formatAndTypeMessage(message),
|
||||
message.address,
|
||||
message.port,
|
||||
);
|
||||
console.log(
|
||||
`> Message sent to ${message.address}:${message.port}: ${JSON.stringify(
|
||||
message.args,
|
||||
)}`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("> Error processing message:", error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
wss.on("error", function (error) {
|
||||
console.error("> Server error:", error);
|
||||
});
|
||||
|
||||
wss.on("close", function () {
|
||||
// Close the websocket server
|
||||
wss.close();
|
||||
console.log("> Closing websocket server");
|
||||
});
|
||||
|
||||
let udpPort = new osc.UDPPort({
|
||||
localAddress: "0.0.0.0",
|
||||
localPort: 3000,
|
||||
metadata: true,
|
||||
remoteAddress: "0.0.0.0",
|
||||
remotePort: 57120,
|
||||
});
|
||||
udpPort.on("error", function (error) {
|
||||
console.error("> UDP Port error:", error);
|
||||
});
|
||||
udpPort.on("ready", function () {
|
||||
//console.log(`> UDP Receive: ${udpPort.options.localPort}`);
|
||||
console.log("> WebSocket server: 127.0.0.1:3000");
|
||||
});
|
||||
|
||||
udpPort.open();
|
||||
|
||||
function sendOscMessage(message, address, port) {
|
||||
try {
|
||||
udpPort.options.remotePort = port;
|
||||
message.address = address;
|
||||
udpPort.send(message);
|
||||
} catch (error) {
|
||||
console.error("> Error sending OSC message:", error);
|
||||
}
|
||||
}
|
||||
|
||||
const formatAndTypeMessage = (message) => {
|
||||
let newMessage = {};
|
||||
delete message.args["address"];
|
||||
delete message.args["port"];
|
||||
newMessage.address = message.address;
|
||||
newMessage.timestamp = osc.timeTag(message.timetag);
|
||||
|
||||
args = [...Object.entries(message.args)].flat().map((arg) => {
|
||||
if (typeof arg === "string") return { type: "s", value: arg };
|
||||
if (typeof arg === "number") return { type: "f", value: arg };
|
||||
if (typeof arg === "boolean")
|
||||
return value ? { type: "s", value: 1 } : { type: "s", value: 0 };
|
||||
});
|
||||
|
||||
newMessage.args = args;
|
||||
|
||||
return newMessage;
|
||||
};
|
||||
14
ToposServer/banner.js
Normal file
14
ToposServer/banner.js
Normal file
@ -0,0 +1,14 @@
|
||||
var pjson = require("./package.json");
|
||||
let banner = `
|
||||
┏┳┓ ┏┓┏┓┏┓
|
||||
┃ ┏┓┏┓┏┓┏ ┃┃┗┓┃
|
||||
┻ ┗┛┣┛┗┛┛ ┗┛┗┛┗┛
|
||||
┛
|
||||
${pjson.version}\n`;
|
||||
function greet() {
|
||||
console.log(banner);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
greet: greet,
|
||||
};
|
||||
332
ToposServer/package-lock.json
generated
Normal file
332
ToposServer/package-lock.json
generated
Normal file
@ -0,0 +1,332 @@
|
||||
{
|
||||
"name": "topos-server",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "topos-server",
|
||||
"version": "1.0.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"osc": "^2.4.4",
|
||||
"ws": "^8.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@serialport/binding-mock": {
|
||||
"version": "10.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/binding-mock/-/binding-mock-10.2.2.tgz",
|
||||
"integrity": "sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@serialport/bindings-interface": "^1.2.1",
|
||||
"debug": "^4.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@serialport/bindings-cpp": {
|
||||
"version": "10.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/bindings-cpp/-/bindings-cpp-10.8.0.tgz",
|
||||
"integrity": "sha512-OMQNJz5kJblbmZN5UgJXLwi2XNtVLxSKmq5VyWuXQVsUIJD4l9UGHnLPqM5LD9u3HPZgDI5w7iYN7gxkQNZJUw==",
|
||||
"hasInstallScript": true,
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@serialport/bindings-interface": "1.2.2",
|
||||
"@serialport/parser-readline": "^10.2.1",
|
||||
"debug": "^4.3.2",
|
||||
"node-addon-api": "^5.0.0",
|
||||
"node-gyp-build": "^4.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.17.0 <13.0 || >=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/serialport/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@serialport/bindings-interface": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/bindings-interface/-/bindings-interface-1.2.2.tgz",
|
||||
"integrity": "sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": "^12.22 || ^14.13 || >=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@serialport/parser-byte-length": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-byte-length/-/parser-byte-length-10.5.0.tgz",
|
||||
"integrity": "sha512-eHhr4lHKboq1OagyaXAqkemQ1XyoqbLQC8XJbvccm95o476TmEdW5d7AElwZV28kWprPW68ZXdGF2VXCkJgS2w==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/serialport/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@serialport/parser-cctalk": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-cctalk/-/parser-cctalk-10.5.0.tgz",
|
||||
"integrity": "sha512-Iwsdr03xmCKAiibLSr7b3w6ZUTBNiS+PwbDQXdKU/clutXjuoex83XvsOtYVcNZmwJlVNhAUbkG+FJzWwIa4DA==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/serialport/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@serialport/parser-delimiter": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-delimiter/-/parser-delimiter-10.5.0.tgz",
|
||||
"integrity": "sha512-/uR/yT3jmrcwnl2FJU/2ySvwgo5+XpksDUR4NF/nwTS5i3CcuKS+FKi/tLzy1k8F+rCx5JzpiK+koqPqOUWArA==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/serialport/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@serialport/parser-inter-byte-timeout": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-10.5.0.tgz",
|
||||
"integrity": "sha512-WPvVlSx98HmmUF9jjK6y9mMp3Wnv6JQA0cUxLeZBgS74TibOuYG3fuUxUWGJALgAXotOYMxfXSezJ/vSnQrkhQ==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/serialport/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@serialport/parser-packet-length": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-packet-length/-/parser-packet-length-10.5.0.tgz",
|
||||
"integrity": "sha512-jkpC/8w4/gUBRa2Teyn7URv1D7T//0lGj27/4u9AojpDVXsR6dtdcTG7b7dNirXDlOrSLvvN7aS5/GNaRlEByw==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@serialport/parser-readline": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-readline/-/parser-readline-10.5.0.tgz",
|
||||
"integrity": "sha512-0aXJknodcl94W9zSjvU+sLdXiyEG2rqjQmvBWZCr8wJZjWEtv3RgrnYiWq4i2OTOyC8C/oPK8ZjpBjQptRsoJQ==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@serialport/parser-delimiter": "10.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/serialport/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@serialport/parser-ready": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-ready/-/parser-ready-10.5.0.tgz",
|
||||
"integrity": "sha512-QIf65LTvUoxqWWHBpgYOL+soldLIIyD1bwuWelukem2yDZVWwEjR288cLQ558BgYxH4U+jLAQahhqoyN1I7BaA==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/serialport/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@serialport/parser-regex": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-regex/-/parser-regex-10.5.0.tgz",
|
||||
"integrity": "sha512-9jnr9+PCxRoLjtGs7uxwsFqvho+rxuJlW6ZWSB7oqfzshEZWXtTJgJRgac/RuLft4hRlrmRz5XU40i3uoL4HKw==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/serialport/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@serialport/parser-slip-encoder": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-slip-encoder/-/parser-slip-encoder-10.5.0.tgz",
|
||||
"integrity": "sha512-wP8m+uXQdkWSa//3n+VvfjLthlabwd9NiG6kegf0fYweLWio8j4pJRL7t9eTh2Lbc7zdxuO0r8ducFzO0m8CQw==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/serialport/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@serialport/parser-spacepacket": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/parser-spacepacket/-/parser-spacepacket-10.5.0.tgz",
|
||||
"integrity": "sha512-BEZ/HAEMwOd8xfuJSeI/823IR/jtnThovh7ils90rXD4DPL1ZmrP4abAIEktwe42RobZjIPfA4PaVfyO0Fjfhg==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/serialport/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/@serialport/stream": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@serialport/stream/-/stream-10.5.0.tgz",
|
||||
"integrity": "sha512-gbcUdvq9Kyv2HsnywS7QjnEB28g+6OGB5Z8TLP7X+UPpoMIWoUsoQIq5Kt0ZTgMoWn3JGM2lqwTsSHF+1qhniA==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@serialport/bindings-interface": "1.2.2",
|
||||
"debug": "^4.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/serialport/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/node-addon-api": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/node-gyp-build": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.0.tgz",
|
||||
"integrity": "sha512-PbZERfeFdrHQOOXiAKOY0VPbykZy90ndPKk0d+CFDegTKmWp1VgOTz2xACVbr1BjCWxrQp68CXtvNsveFhqDJg==",
|
||||
"optional": true,
|
||||
"bin": {
|
||||
"node-gyp-build": "bin.js",
|
||||
"node-gyp-build-optional": "optional.js",
|
||||
"node-gyp-build-test": "build-test.js"
|
||||
}
|
||||
},
|
||||
"node_modules/osc": {
|
||||
"version": "2.4.4",
|
||||
"resolved": "https://registry.npmjs.org/osc/-/osc-2.4.4.tgz",
|
||||
"integrity": "sha512-YJr2bUCQMc9BIaq1LXgqYpt5Ii7wNy2n0e0BkQiCSziMNrrsYHhH5OlExNBgCrQsum60EgXZ32lFsvR4aUf+ew==",
|
||||
"dependencies": {
|
||||
"long": "4.0.0",
|
||||
"slip": "1.0.2",
|
||||
"wolfy87-eventemitter": "5.2.9",
|
||||
"ws": "8.13.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"serialport": "10.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/osc/node_modules/ws": {
|
||||
"version": "8.13.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.13.0.tgz",
|
||||
"integrity": "sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==",
|
||||
"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
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/serialport": {
|
||||
"version": "10.5.0",
|
||||
"resolved": "https://registry.npmjs.org/serialport/-/serialport-10.5.0.tgz",
|
||||
"integrity": "sha512-7OYLDsu5i6bbv3lU81pGy076xe0JwpK6b49G6RjNvGibstUqQkI+I3/X491yBGtf4gaqUdOgoU1/5KZ/XxL4dw==",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@serialport/binding-mock": "10.2.2",
|
||||
"@serialport/bindings-cpp": "10.8.0",
|
||||
"@serialport/parser-byte-length": "10.5.0",
|
||||
"@serialport/parser-cctalk": "10.5.0",
|
||||
"@serialport/parser-delimiter": "10.5.0",
|
||||
"@serialport/parser-inter-byte-timeout": "10.5.0",
|
||||
"@serialport/parser-packet-length": "10.5.0",
|
||||
"@serialport/parser-readline": "10.5.0",
|
||||
"@serialport/parser-ready": "10.5.0",
|
||||
"@serialport/parser-regex": "10.5.0",
|
||||
"@serialport/parser-slip-encoder": "10.5.0",
|
||||
"@serialport/parser-spacepacket": "10.5.0",
|
||||
"@serialport/stream": "10.5.0",
|
||||
"debug": "^4.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://opencollective.com/serialport/donate"
|
||||
}
|
||||
},
|
||||
"node_modules/slip": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/slip/-/slip-1.0.2.tgz",
|
||||
"integrity": "sha512-XrcHe3NAcyD3wO+O4I13RcS4/3AF+S9RvGNj9JhJeS02HyImwD2E3QWLrmn9hBfL+fB6yapagwxRkeyYzhk98g=="
|
||||
},
|
||||
"node_modules/wolfy87-eventemitter": {
|
||||
"version": "5.2.9",
|
||||
"resolved": "https://registry.npmjs.org/wolfy87-eventemitter/-/wolfy87-eventemitter-5.2.9.tgz",
|
||||
"integrity": "sha512-P+6vtWyuDw+MB01X7UeF8TaHBvbCovf4HPEMF/SV7BdDc1SMTiBy13SRD71lQh4ExFTG1d/WNzDGDCyOKSMblw=="
|
||||
},
|
||||
"node_modules/ws": {
|
||||
"version": "8.14.2",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz",
|
||||
"integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==",
|
||||
"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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
ToposServer/package.json
Normal file
15
ToposServer/package.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "topos-server",
|
||||
"version": "0.0.1",
|
||||
"description": "Topos OSC Server",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Raphaël Forment",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"dependencies": {
|
||||
"osc": "^2.4.4",
|
||||
"ws": "^8.14.2"
|
||||
}
|
||||
}
|
||||
8
ToposServer/server.js
Normal file
8
ToposServer/server.js
Normal file
@ -0,0 +1,8 @@
|
||||
const WebSocket = require("ws");
|
||||
const osc = require("osc");
|
||||
|
||||
require("./banner").greet();
|
||||
// Topos to OSC
|
||||
require("./ToposToOSC");
|
||||
// OSC to Topos
|
||||
require("./OSCtoTopos");
|
||||
@ -180,6 +180,7 @@
|
||||
|
||||
<p rel="noopener noreferrer" id="docs_patterns" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">Patterns</p>
|
||||
<p rel="noopener noreferrer" id="docs_midi" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">MIDI</p>
|
||||
<p rel="noopener noreferrer" id="docs_osc" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">OSC</p>
|
||||
</div>
|
||||
</details>
|
||||
<details class="space-y-2" open=true>
|
||||
|
||||
@ -33,6 +33,7 @@
|
||||
"jisg": "^0.9.7",
|
||||
"lru-cache": "^10.0.1",
|
||||
"marked": "^7.0.3",
|
||||
"osc": "^2.4.4",
|
||||
"postcss": "^8.4.27",
|
||||
"showdown": "^2.1.0",
|
||||
"showdown-highlight": "^3.1.0",
|
||||
|
||||
29
src/API.ts
29
src/API.ts
@ -1,4 +1,5 @@
|
||||
import { EditorView } from "@codemirror/view";
|
||||
import { sendToServer, type OSCMessage, oscMessages } from "./IO/OSC";
|
||||
import { getAllScaleNotes, nearScales, seededRandom } from "zifferjs";
|
||||
import {
|
||||
MidiCCEvent,
|
||||
@ -1916,7 +1917,7 @@ export class UserAPI {
|
||||
// =============================================================
|
||||
|
||||
register = (name: string, operation: EventOperation<AbstractEvent>): void => {
|
||||
AbstractEvent.prototype[name] = function(
|
||||
AbstractEvent.prototype[name] = function (
|
||||
this: AbstractEvent,
|
||||
...args: any[]
|
||||
) {
|
||||
@ -2095,6 +2096,32 @@ export class UserAPI {
|
||||
}, real_duration * 1000);
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// OSC Functions
|
||||
// =============================================================
|
||||
|
||||
public osc = (address: string, port: number, ...args: any[]): void => {
|
||||
sendToServer({
|
||||
address: address,
|
||||
port: port,
|
||||
args: args,
|
||||
timetag: Math.round(Date.now() + this.app.clock.deadline),
|
||||
} as OSCMessage);
|
||||
};
|
||||
|
||||
public getOSC = (address?: string): any[] => {
|
||||
/**
|
||||
* Give access to incoming OSC messages. If no address is specified, returns the raw oscMessages array. If an address is specified, returns only the messages who contain the address and filter the address itself.
|
||||
*/
|
||||
if (address) {
|
||||
let messages = oscMessages.filter((msg) => msg.address === address);
|
||||
messages = messages.map((msg) => msg.data);
|
||||
return messages;
|
||||
} else {
|
||||
return oscMessages;
|
||||
}
|
||||
};
|
||||
|
||||
// =============================================================
|
||||
// Transport functions
|
||||
// =============================================================
|
||||
|
||||
@ -88,7 +88,8 @@ export class Clock {
|
||||
);
|
||||
this.app.clock.time_position = futureTimeStamp;
|
||||
if (futureTimeStamp.pulse % this.app.clock.ppqn == 0) {
|
||||
this.timeviewer.innerHTML = `${zeroPad(futureTimeStamp.bar, 2)}:${futureTimeStamp.beat + 1
|
||||
this.timeviewer.innerHTML = `${zeroPad(futureTimeStamp.bar, 2)}:${
|
||||
futureTimeStamp.beat + 1
|
||||
} / ${this.app.clock.bpm}`;
|
||||
}
|
||||
if (this.app.exampleIsPlaying) {
|
||||
|
||||
@ -24,6 +24,7 @@ import { linear_time } from "./documentation/time/linear_time";
|
||||
import { cyclical_time } from "./documentation/time/cyclical_time";
|
||||
import { long_forms } from "./documentation/long_forms";
|
||||
import { midi } from "./documentation/midi";
|
||||
import { osc } from "./documentation/osc";
|
||||
import { sound } from "./documentation/engine";
|
||||
import { patterns } from "./documentation/patterns";
|
||||
import { functions } from "./documentation/functions";
|
||||
@ -92,6 +93,7 @@ export const documentation_factory = (application: Editor) => {
|
||||
patterns: patterns(application),
|
||||
ziffers: ziffers(application),
|
||||
midi: midi(application),
|
||||
osc: osc(application),
|
||||
lfos: lfos(application),
|
||||
variables: variables(application),
|
||||
probabilities: probabilities(application),
|
||||
|
||||
62
src/IO/OSC.ts
Normal file
62
src/IO/OSC.ts
Normal file
@ -0,0 +1,62 @@
|
||||
export interface OSCMessage {
|
||||
address: string;
|
||||
port: number;
|
||||
args: object;
|
||||
timetag: number;
|
||||
}
|
||||
|
||||
// Send/receive messages from websocket
|
||||
export let outputSocket = new WebSocket("ws://localhost:3000");
|
||||
export let inputSocket = new WebSocket("ws://localhost:3001");
|
||||
|
||||
export let oscMessages: any[] = [];
|
||||
inputSocket.addEventListener("message", (event) => {
|
||||
let data = JSON.parse(event.data);
|
||||
if (oscMessages.length >= 1000) {
|
||||
oscMessages.shift();
|
||||
}
|
||||
oscMessages.push(data);
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
outputSocket.onopen = function (event) {
|
||||
console.log("Connected to WebSocket Server");
|
||||
// Send an OSC-like message
|
||||
outputSocket.send(
|
||||
JSON.stringify({
|
||||
address: "/successful_connexion",
|
||||
port: 3000,
|
||||
args: {},
|
||||
}),
|
||||
);
|
||||
|
||||
outputSocket.onerror = function (error) {
|
||||
console.log("Websocket Error:", error);
|
||||
};
|
||||
|
||||
outputSocket.onmessage = function (event) {
|
||||
console.log("Received: ", event.data);
|
||||
};
|
||||
};
|
||||
|
||||
export function sendToServer(message: OSCMessage) {
|
||||
if (outputSocket.readyState === WebSocket.OPEN) {
|
||||
outputSocket.send(JSON.stringify(message));
|
||||
} else {
|
||||
console.log("WebSocket is not open. Attempting to reconnect...");
|
||||
if (
|
||||
outputSocket.readyState === WebSocket.CONNECTING ||
|
||||
outputSocket.readyState === WebSocket.OPEN
|
||||
) {
|
||||
outputSocket.close();
|
||||
}
|
||||
|
||||
// Create a new WebSocket connection
|
||||
outputSocket = new WebSocket("ws://localhost:3000");
|
||||
|
||||
// Send the message once the socket is open
|
||||
outputSocket.onopen = () => {
|
||||
outputSocket.send(JSON.stringify(message));
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -499,12 +499,12 @@ export const installInterfaceLogic = (app: Editor) => {
|
||||
"linear",
|
||||
"cyclic",
|
||||
"longform",
|
||||
// "sound",
|
||||
"synths",
|
||||
"chaining",
|
||||
"patterns",
|
||||
"ziffers",
|
||||
"midi",
|
||||
"osc",
|
||||
"functions",
|
||||
"lfos",
|
||||
"probabilities",
|
||||
@ -520,7 +520,7 @@ export const installInterfaceLogic = (app: Editor) => {
|
||||
].forEach((e) => {
|
||||
let name = `docs_` + e;
|
||||
document.getElementById(name)!.addEventListener("click", async () => {
|
||||
if (name !== "docs_samples") {
|
||||
if (name !== "docs_sample_list") {
|
||||
app.currentDocumentationPane = e;
|
||||
updateDocumentationContent(app, bindings);
|
||||
} else {
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { type Editor } from "./main";
|
||||
import { outputSocket, inputSocket } from "./IO/OSC";
|
||||
|
||||
const handleResize = (canvas: HTMLCanvasElement) => {
|
||||
if (!canvas) return;
|
||||
@ -26,6 +27,9 @@ export const saveBeforeExit = (app: Editor): null => {
|
||||
app.currentFile().candidate = app.view.state.doc.toString();
|
||||
app.currentFile().committed = app.view.state.doc.toString();
|
||||
app.settings.saveApplicationToLocalStorage(app.universes, app.settings);
|
||||
// Close the websocket
|
||||
inputSocket.close();
|
||||
outputSocket.close();
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { type Editor } from "../main";
|
||||
import { AudibleEvent } from "./AbstractEvents";
|
||||
import { sendToServer, type OSCMessage } from "../IO/OSC";
|
||||
import {
|
||||
filterObject,
|
||||
arrayOfObjectsToObjectWithArrays,
|
||||
@ -42,6 +43,8 @@ export class SoundEvent extends AudibleEvent {
|
||||
pitchJumpTime: ["pitchJumpTime", "pjt"],
|
||||
lfo: ["lfo"],
|
||||
znoise: ["znoise"],
|
||||
address: ["address", "add"],
|
||||
port: ["port"],
|
||||
noise: ["noise"],
|
||||
zmod: ["zmod"],
|
||||
zcrush: ["zcrush"],
|
||||
@ -462,14 +465,32 @@ export class SoundEvent extends AudibleEvent {
|
||||
if (filteredEvent.freq) {
|
||||
delete filteredEvent.note;
|
||||
}
|
||||
if (this.values["debug"]) {
|
||||
if (this.values["debugFunction"]) {
|
||||
this.values["debugFunction"](filteredEvent);
|
||||
} else {
|
||||
console.log(filteredEvent);
|
||||
}
|
||||
}
|
||||
superdough(filteredEvent, this.app.clock.deadline, filteredEvent.dur);
|
||||
}
|
||||
};
|
||||
|
||||
osc = (orbit?: number | number[]): void => {
|
||||
if (orbit) this.values["orbit"] = orbit;
|
||||
const events = objectWithArraysToArrayOfObjects(this.values, [
|
||||
"parsedScale",
|
||||
]);
|
||||
for (const event of events) {
|
||||
const filteredEvent = event;
|
||||
|
||||
let oscAddress = "address" in event ? event.address : "/topos";
|
||||
oscAddress = oscAddress?.startsWith("/") ? oscAddress : "/" + oscAddress;
|
||||
|
||||
let oscPort = "port" in event ? event.port : 57120;
|
||||
|
||||
if (filteredEvent.freq) {
|
||||
delete filteredEvent.note;
|
||||
}
|
||||
sendToServer({
|
||||
address: oscAddress,
|
||||
port: oscPort,
|
||||
args: event,
|
||||
timetag: Math.round(Date.now() + this.app.clock.deadline),
|
||||
} as OSCMessage);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ ${makeExample(
|
||||
"Welcome! Eval to get started",
|
||||
examples[Math.floor(Math.random() * examples.length)],
|
||||
true,
|
||||
)}
|
||||
)}
|
||||
|
||||
# What is Topos?
|
||||
|
||||
@ -31,7 +31,7 @@ rhythm(.25, [5, 7].beat(2), 8) :: sound(['hc', 'fikea', 'hat'].pick(1))
|
||||
beat([2,0.5].dur(13.5, 0.5))::snd('fsoftsnare')
|
||||
.n(0).speed([1, 0.5]).o(4).out()`,
|
||||
false,
|
||||
)}
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Computer music should be immediate and intuitive",
|
||||
@ -48,7 +48,7 @@ beat(.25)::snd('sine')
|
||||
.room(0.5).size(8) // Reverb
|
||||
.out()`,
|
||||
false,
|
||||
)}
|
||||
)}
|
||||
|
||||
${makeExample(
|
||||
"Making the web less dreadful, one beep at at time",
|
||||
@ -59,7 +59,7 @@ beat(.25) :: sound('sid').note(
|
||||
[34, 36, 41].beat(.25) + [[0,-24].pick(),12].beat())
|
||||
.room(0.9).size(0.9).n(4).out()`,
|
||||
false,
|
||||
)}
|
||||
)}
|
||||
|
||||
Topos is deeply inspired by the [Monome Teletype](https://monome.org/). The Teletype is/was an open source hardware module for Eurorack synthesizers. While the Teletype was initially born as an hardware module, Topos aims to be a web-browser based cousin of it! It is a sequencer, a scriptable interface, a companion for algorithmic music-making. Topos wishes to fullfill the same goal as the Teletype, keeping the same spirit alive on the web. It is free, open-source, and made to be shared and used by everyone. Learn more about live coding on [livecoding.fr](https://livecoding.fr).
|
||||
|
||||
@ -75,4 +75,3 @@ Reloading the application will get you one random song example to study every ti
|
||||
|
||||
`;
|
||||
};
|
||||
|
||||
|
||||
75
src/documentation/osc.ts
Normal file
75
src/documentation/osc.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import { type Editor } from "../main";
|
||||
import { makeExampleFactory } from "../Documentation";
|
||||
|
||||
export const osc = (application: Editor): string => {
|
||||
// @ts-ignore
|
||||
const makeExample = makeExampleFactory(application);
|
||||
return `
|
||||
# Open Sound Control
|
||||
|
||||
Topos is a sandboxed web application. It cannot speak with your computer directly or only through a secure connexion. You can use the [Open Sound Control](https://en.wikipedia.org/wiki/Open_Sound_Control) protocol to send and receive data from your computer. This protocol is used by many softwares and hardware devices. You can use it to control your favorite DAW, your favorite synthesizer, your favorite robot, or anything really! To use **OSC** with Topos, you will need to download the <ic>ToposServer</ic> by [following this link](https://github.com/Bubobubobubobubo/Topos). You can download everything as a zip file or clone the project if you know what you are doing. Here is a quick guide to get you started:
|
||||
|
||||
- 1) Download <ic>Topos</ic> and navigate to the <ic>ToposServer</ic> folder.
|
||||
- 2) Install the dependencies using <ic>npm install</ic>. Start the server using <ic>npm start</ic>.
|
||||
- 3) Open the <ic>Topos</ic> application in your web browser (server first, then application).
|
||||
|
||||
The <ic>ToposServer</ic> server is used both for **OSC** _input_ and _output_.
|
||||
|
||||
## Input
|
||||
|
||||
Send an **OSC** message to the server from another application or device at the address <ic>localhost:30000</ic>. Topos will store the last 1000 messages in a queue. You can access this queue using the <ic>getOsc()</ic> function.
|
||||
|
||||
### Unfiltered messages
|
||||
|
||||
You can access the last 1000 messages using the <ic>getOsc()</ic> function without any argument. This is raw data, you will need to parse it yourself:
|
||||
|
||||
${makeExample(
|
||||
"Reading the last OSC messages",
|
||||
`
|
||||
beat(1)::getOsc()
|
||||
// 0 : {data: Array(2), address: '/lala'}
|
||||
// 1 : {data: Array(2), address: '/lala'}
|
||||
// 2 : {data: Array(2), address: '/lala'}`,
|
||||
true,
|
||||
)}
|
||||
|
||||
### Filtered messages
|
||||
|
||||
The <ic>getOsc()</ic> can receive an address filter as an argument. This will return only the messages that match the filter:
|
||||
|
||||
${makeExample(
|
||||
"Reading the last OSC messages (filtered)",
|
||||
`
|
||||
beat(1)::getOsc("/lala")
|
||||
// 0 : (2) [89, 'bob']
|
||||
// 1 : (2) [84, 'bob']
|
||||
// 2 : (2) [82, 'bob']
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
## Output
|
||||
|
||||
Once the server is loaded, you are ready to send an **OSC** message:
|
||||
|
||||
${makeExample(
|
||||
"Sending a simple OSC message",
|
||||
`
|
||||
beat(1)::sound('cp').speed(2).vel(0.5).osc()
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
This is a simple **OSC** message that will inherit all the properties of the sound. You can also send customized OSC messages using the <ic>osc()</ic> function:
|
||||
|
||||
${makeExample(
|
||||
"Sending a customized OSC message",
|
||||
`
|
||||
// osc(address, port, ...message)
|
||||
osc('/my/osc/address', 5000, 1, 2, 3)
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
`;
|
||||
};
|
||||
@ -4,6 +4,7 @@ import { scriptBlinkers } from "./Visuals/Blinkers";
|
||||
import { javascript } from "@codemirror/lang-javascript";
|
||||
import { markdown } from "@codemirror/lang-markdown";
|
||||
import { Extension } from "@codemirror/state";
|
||||
import { outputSocket } from "./IO/OSC";
|
||||
import {
|
||||
initializeSelectedUniverse,
|
||||
AppSettings,
|
||||
@ -93,6 +94,9 @@ export class Editor {
|
||||
manualPlay: boolean = false;
|
||||
isPlaying: boolean = false;
|
||||
|
||||
// OSC
|
||||
outputSocket: WebSocket = outputSocket;
|
||||
|
||||
// Hydra
|
||||
public hydra_backend: any;
|
||||
public hydra: any;
|
||||
|
||||
BIN
topos_frog.png
Normal file
BIN
topos_frog.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 427 KiB |
148
yarn.lock
148
yarn.lock
@ -1362,6 +1362,90 @@
|
||||
estree-walker "^1.0.1"
|
||||
picomatch "^2.2.2"
|
||||
|
||||
"@serialport/binding-mock@10.2.2":
|
||||
version "10.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/binding-mock/-/binding-mock-10.2.2.tgz#d322a8116a97806addda13c62f50e73d16125874"
|
||||
integrity sha512-HAFzGhk9OuFMpuor7aT5G1ChPgn5qSsklTFOTUX72Rl6p0xwcSVsRtG/xaGp6bxpN7fI9D/S8THLBWbBgS6ldw==
|
||||
dependencies:
|
||||
"@serialport/bindings-interface" "^1.2.1"
|
||||
debug "^4.3.3"
|
||||
|
||||
"@serialport/bindings-cpp@10.8.0":
|
||||
version "10.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/bindings-cpp/-/bindings-cpp-10.8.0.tgz#79507b57022ac264e963e7fbf3647a3821569a20"
|
||||
integrity sha512-OMQNJz5kJblbmZN5UgJXLwi2XNtVLxSKmq5VyWuXQVsUIJD4l9UGHnLPqM5LD9u3HPZgDI5w7iYN7gxkQNZJUw==
|
||||
dependencies:
|
||||
"@serialport/bindings-interface" "1.2.2"
|
||||
"@serialport/parser-readline" "^10.2.1"
|
||||
debug "^4.3.2"
|
||||
node-addon-api "^5.0.0"
|
||||
node-gyp-build "^4.3.0"
|
||||
|
||||
"@serialport/bindings-interface@1.2.2", "@serialport/bindings-interface@^1.2.1":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/bindings-interface/-/bindings-interface-1.2.2.tgz#c4ae9c1c85e26b02293f62f37435478d90baa460"
|
||||
integrity sha512-CJaUd5bLvtM9c5dmO9rPBHPXTa9R2UwpkJ0wdh9JCYcbrPWsKz+ErvR0hBLeo7NPeiFdjFO4sonRljiw4d2XiA==
|
||||
|
||||
"@serialport/parser-byte-length@10.5.0":
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-byte-length/-/parser-byte-length-10.5.0.tgz#f3d4c1c7923222df2f3d3c7c8aaaa207fe373b49"
|
||||
integrity sha512-eHhr4lHKboq1OagyaXAqkemQ1XyoqbLQC8XJbvccm95o476TmEdW5d7AElwZV28kWprPW68ZXdGF2VXCkJgS2w==
|
||||
|
||||
"@serialport/parser-cctalk@10.5.0":
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-cctalk/-/parser-cctalk-10.5.0.tgz#0ee88db0768a361b7cfb9a394b74e480c38e1992"
|
||||
integrity sha512-Iwsdr03xmCKAiibLSr7b3w6ZUTBNiS+PwbDQXdKU/clutXjuoex83XvsOtYVcNZmwJlVNhAUbkG+FJzWwIa4DA==
|
||||
|
||||
"@serialport/parser-delimiter@10.5.0":
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-delimiter/-/parser-delimiter-10.5.0.tgz#b0d93100cdfd0619d020a427d652495073f3b828"
|
||||
integrity sha512-/uR/yT3jmrcwnl2FJU/2ySvwgo5+XpksDUR4NF/nwTS5i3CcuKS+FKi/tLzy1k8F+rCx5JzpiK+koqPqOUWArA==
|
||||
|
||||
"@serialport/parser-inter-byte-timeout@10.5.0":
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-inter-byte-timeout/-/parser-inter-byte-timeout-10.5.0.tgz#8665ee5e6138f794ac055e83ef2d1c3653a577c0"
|
||||
integrity sha512-WPvVlSx98HmmUF9jjK6y9mMp3Wnv6JQA0cUxLeZBgS74TibOuYG3fuUxUWGJALgAXotOYMxfXSezJ/vSnQrkhQ==
|
||||
|
||||
"@serialport/parser-packet-length@10.5.0":
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-packet-length/-/parser-packet-length-10.5.0.tgz#4c4d733bdff8cc4749f2bd750e42e66f8f478def"
|
||||
integrity sha512-jkpC/8w4/gUBRa2Teyn7URv1D7T//0lGj27/4u9AojpDVXsR6dtdcTG7b7dNirXDlOrSLvvN7aS5/GNaRlEByw==
|
||||
|
||||
"@serialport/parser-readline@10.5.0", "@serialport/parser-readline@^10.2.1":
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-readline/-/parser-readline-10.5.0.tgz#df23365ae7f45679b1735deae26f72ba42802862"
|
||||
integrity sha512-0aXJknodcl94W9zSjvU+sLdXiyEG2rqjQmvBWZCr8wJZjWEtv3RgrnYiWq4i2OTOyC8C/oPK8ZjpBjQptRsoJQ==
|
||||
dependencies:
|
||||
"@serialport/parser-delimiter" "10.5.0"
|
||||
|
||||
"@serialport/parser-ready@10.5.0":
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-ready/-/parser-ready-10.5.0.tgz#1d9029f57b1abd664cb468e21bfccf7b44c6e8ea"
|
||||
integrity sha512-QIf65LTvUoxqWWHBpgYOL+soldLIIyD1bwuWelukem2yDZVWwEjR288cLQ558BgYxH4U+jLAQahhqoyN1I7BaA==
|
||||
|
||||
"@serialport/parser-regex@10.5.0":
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-regex/-/parser-regex-10.5.0.tgz#f98eab6e3d9bc99086269e9acf39a82db36d245f"
|
||||
integrity sha512-9jnr9+PCxRoLjtGs7uxwsFqvho+rxuJlW6ZWSB7oqfzshEZWXtTJgJRgac/RuLft4hRlrmRz5XU40i3uoL4HKw==
|
||||
|
||||
"@serialport/parser-slip-encoder@10.5.0":
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-slip-encoder/-/parser-slip-encoder-10.5.0.tgz#cb79ac0fda1fc87f049690ff7b498c787da67991"
|
||||
integrity sha512-wP8m+uXQdkWSa//3n+VvfjLthlabwd9NiG6kegf0fYweLWio8j4pJRL7t9eTh2Lbc7zdxuO0r8ducFzO0m8CQw==
|
||||
|
||||
"@serialport/parser-spacepacket@10.5.0":
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/parser-spacepacket/-/parser-spacepacket-10.5.0.tgz#2fc077c0ec16a9532c511ad5f2ab12d588796bc7"
|
||||
integrity sha512-BEZ/HAEMwOd8xfuJSeI/823IR/jtnThovh7ils90rXD4DPL1ZmrP4abAIEktwe42RobZjIPfA4PaVfyO0Fjfhg==
|
||||
|
||||
"@serialport/stream@10.5.0":
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/@serialport/stream/-/stream-10.5.0.tgz#cda8fb3e8d03094b0962a3d14b73adfcd591be58"
|
||||
integrity sha512-gbcUdvq9Kyv2HsnywS7QjnEB28g+6OGB5Z8TLP7X+UPpoMIWoUsoQIq5Kt0ZTgMoWn3JGM2lqwTsSHF+1qhniA==
|
||||
dependencies:
|
||||
"@serialport/bindings-interface" "1.2.2"
|
||||
debug "^4.3.2"
|
||||
|
||||
"@strudel.cycles/core@0.8.2":
|
||||
version "0.8.2"
|
||||
resolved "https://registry.yarnpkg.com/@strudel.cycles/core/-/core-0.8.2.tgz#62e957a3636b39938d1c4ecc3fd766d02fc523bb"
|
||||
@ -1885,7 +1969,7 @@ cssesc@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
|
||||
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
|
||||
|
||||
debug@^4.1.0, debug@^4.1.1, debug@^4.3.3, debug@^4.3.4:
|
||||
debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
@ -2718,6 +2802,11 @@ lodash@^4.17.20:
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
|
||||
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
|
||||
|
||||
long@4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
|
||||
integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==
|
||||
|
||||
lru-cache@^10.0.1:
|
||||
version "10.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a"
|
||||
@ -2814,6 +2903,11 @@ nanostores@^0.8.1:
|
||||
resolved "https://registry.yarnpkg.com/nanostores/-/nanostores-0.8.1.tgz#963577028ac10eeb50bec376535f4762ab5af9be"
|
||||
integrity sha512-1ZCfQtII2XeFDrtqXL2cdQ/diGrLxzRB3YMyQjn8m7GSGQrJfGST2iuqMpWnS/ZlifhtjgR/SX0Jy6Uij6lRLA==
|
||||
|
||||
node-addon-api@^5.0.0:
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762"
|
||||
integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==
|
||||
|
||||
node-fetch@^2.6.1:
|
||||
version "2.6.13"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.13.tgz#a20acbbec73c2e09f9007de5cda17104122e0010"
|
||||
@ -2821,6 +2915,11 @@ node-fetch@^2.6.1:
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-gyp-build@^4.3.0:
|
||||
version "4.7.0"
|
||||
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.7.0.tgz#749f0033590b2a89ac8edb5e0775f95f5ae86d15"
|
||||
integrity sha512-PbZERfeFdrHQOOXiAKOY0VPbykZy90ndPKk0d+CFDegTKmWp1VgOTz2xACVbr1BjCWxrQp68CXtvNsveFhqDJg==
|
||||
|
||||
node-releases@^2.0.13:
|
||||
version "2.0.13"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d"
|
||||
@ -2873,6 +2972,18 @@ once@^1.3.0:
|
||||
dependencies:
|
||||
wrappy "1"
|
||||
|
||||
osc@^2.4.4:
|
||||
version "2.4.4"
|
||||
resolved "https://registry.yarnpkg.com/osc/-/osc-2.4.4.tgz#52b2e914253b04a792d28ee892b0fa1ca8bdc1a2"
|
||||
integrity sha512-YJr2bUCQMc9BIaq1LXgqYpt5Ii7wNy2n0e0BkQiCSziMNrrsYHhH5OlExNBgCrQsum60EgXZ32lFsvR4aUf+ew==
|
||||
dependencies:
|
||||
long "4.0.0"
|
||||
slip "1.0.2"
|
||||
wolfy87-eventemitter "5.2.9"
|
||||
ws "8.13.0"
|
||||
optionalDependencies:
|
||||
serialport "10.5.0"
|
||||
|
||||
path-is-absolute@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
|
||||
@ -3149,6 +3260,26 @@ serialize-javascript@^4.0.0:
|
||||
dependencies:
|
||||
randombytes "^2.1.0"
|
||||
|
||||
serialport@10.5.0:
|
||||
version "10.5.0"
|
||||
resolved "https://registry.yarnpkg.com/serialport/-/serialport-10.5.0.tgz#b85f614def6e8914e5865c798b0555330903a0f8"
|
||||
integrity sha512-7OYLDsu5i6bbv3lU81pGy076xe0JwpK6b49G6RjNvGibstUqQkI+I3/X491yBGtf4gaqUdOgoU1/5KZ/XxL4dw==
|
||||
dependencies:
|
||||
"@serialport/binding-mock" "10.2.2"
|
||||
"@serialport/bindings-cpp" "10.8.0"
|
||||
"@serialport/parser-byte-length" "10.5.0"
|
||||
"@serialport/parser-cctalk" "10.5.0"
|
||||
"@serialport/parser-delimiter" "10.5.0"
|
||||
"@serialport/parser-inter-byte-timeout" "10.5.0"
|
||||
"@serialport/parser-packet-length" "10.5.0"
|
||||
"@serialport/parser-readline" "10.5.0"
|
||||
"@serialport/parser-ready" "10.5.0"
|
||||
"@serialport/parser-regex" "10.5.0"
|
||||
"@serialport/parser-slip-encoder" "10.5.0"
|
||||
"@serialport/parser-spacepacket" "10.5.0"
|
||||
"@serialport/stream" "10.5.0"
|
||||
debug "^4.3.3"
|
||||
|
||||
set-function-length@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed"
|
||||
@ -3193,6 +3324,11 @@ side-channel@^1.0.4:
|
||||
get-intrinsic "^1.0.2"
|
||||
object-inspect "^1.9.0"
|
||||
|
||||
slip@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/slip/-/slip-1.0.2.tgz#ba45a923034d6cf41b1a27aebe7128282c8d551f"
|
||||
integrity sha512-XrcHe3NAcyD3wO+O4I13RcS4/3AF+S9RvGNj9JhJeS02HyImwD2E3QWLrmn9hBfL+fB6yapagwxRkeyYzhk98g==
|
||||
|
||||
source-map-js@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||
@ -3709,6 +3845,11 @@ which-typed-array@^1.1.11, which-typed-array@^1.1.13:
|
||||
gopd "^1.0.1"
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
wolfy87-eventemitter@5.2.9:
|
||||
version "5.2.9"
|
||||
resolved "https://registry.yarnpkg.com/wolfy87-eventemitter/-/wolfy87-eventemitter-5.2.9.tgz#e879f770b30fbb6512a8afbb330c388591099c2a"
|
||||
integrity sha512-P+6vtWyuDw+MB01X7UeF8TaHBvbCovf4HPEMF/SV7BdDc1SMTiBy13SRD71lQh4ExFTG1d/WNzDGDCyOKSMblw==
|
||||
|
||||
workbox-background-sync@7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-7.0.0.tgz#2b84b96ca35fec976e3bd2794b70e4acec46b3a5"
|
||||
@ -3872,6 +4013,11 @@ wrappy@1:
|
||||
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
||||
|
||||
ws@8.13.0:
|
||||
version "8.13.0"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0"
|
||||
integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA==
|
||||
|
||||
yallist@^3.0.2:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||
|
||||
Reference in New Issue
Block a user