lint topos
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.
|
||||
|
||||
@ -6,9 +6,9 @@ const cleanIncomingOSC = (oscMsg) => {
|
||||
// Remove information about type of data
|
||||
data = data.map((item) => {
|
||||
return item.value;
|
||||
})
|
||||
return {data: data, address: oscMsg.address};
|
||||
}
|
||||
});
|
||||
return { data: data, address: oscMsg.address };
|
||||
};
|
||||
|
||||
// ==============================================
|
||||
// Receiving and forwarding OSC UDP messages
|
||||
@ -18,13 +18,16 @@ const wss = new WebSocket.Server({ port: 3001 });
|
||||
var udpPort = new osc.UDPPort({
|
||||
localAddress: "0.0.0.0",
|
||||
localPort: 30000,
|
||||
metadata: true
|
||||
metadata: true,
|
||||
});
|
||||
udpPort.on("message", function (oscMsg, timeTag, info) {
|
||||
console.log(`> Incoming OSC to ${oscMsg.address}:`, oscMsg.args.map(
|
||||
(item) => {return item.value})
|
||||
console.log(
|
||||
`> Incoming OSC to ${oscMsg.address}:`,
|
||||
oscMsg.args.map((item) => {
|
||||
return item.value;
|
||||
}),
|
||||
);
|
||||
wss.clients.forEach(client => {
|
||||
wss.clients.forEach((client) => {
|
||||
if (client.readyState === WebSocket.OPEN) {
|
||||
client.send(JSON.stringify(cleanIncomingOSC(oscMsg)));
|
||||
}
|
||||
|
||||
@ -13,9 +13,13 @@ wss.on("connection", function (ws) {
|
||||
sendOscMessage(
|
||||
formatAndTypeMessage(message),
|
||||
message.address,
|
||||
message.port
|
||||
message.port,
|
||||
);
|
||||
console.log(
|
||||
`> Message sent to ${message.address}:${message.port}: ${JSON.stringify(
|
||||
message.args,
|
||||
)}`,
|
||||
);
|
||||
console.log(`> Message sent to ${message.address}:${message.port}: ${JSON.stringify(message.args)}`)
|
||||
} catch (error) {
|
||||
console.error("> Error processing message:", error);
|
||||
}
|
||||
@ -24,12 +28,12 @@ wss.on("connection", function (ws) {
|
||||
|
||||
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")
|
||||
console.log("> Closing websocket server");
|
||||
});
|
||||
|
||||
let udpPort = new osc.UDPPort({
|
||||
@ -51,7 +55,7 @@ udpPort.open();
|
||||
|
||||
function sendOscMessage(message, address, port) {
|
||||
try {
|
||||
udpPort.options.remotePort = port
|
||||
udpPort.options.remotePort = port;
|
||||
message.address = address;
|
||||
udpPort.send(message);
|
||||
} catch (error) {
|
||||
@ -61,21 +65,19 @@ function sendOscMessage(message, address, port) {
|
||||
|
||||
const formatAndTypeMessage = (message) => {
|
||||
let newMessage = {};
|
||||
delete message.args['address'];
|
||||
delete message.args['port'];
|
||||
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};
|
||||
})
|
||||
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
|
||||
newMessage.args = args;
|
||||
|
||||
return newMessage;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
var pjson = require('./package.json');
|
||||
var pjson = require("./package.json");
|
||||
let banner = `
|
||||
┏┳┓ ┏┓┏┓┏┓
|
||||
┃ ┏┓┏┓┏┓┏ ┃┃┗┓┃
|
||||
┻ ┗┛┣┛┗┛┛ ┗┛┗┛┗┛
|
||||
┛
|
||||
${pjson.version}\n`
|
||||
${pjson.version}\n`;
|
||||
function greet() {
|
||||
console.log(banner)
|
||||
console.log(banner);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
greet: greet
|
||||
}
|
||||
greet: greet,
|
||||
};
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
const WebSocket = require("ws");
|
||||
const osc = require("osc");
|
||||
|
||||
require('./banner').greet();
|
||||
require("./banner").greet();
|
||||
// Topos to OSC
|
||||
require('./ToposToOSC')
|
||||
require("./ToposToOSC");
|
||||
// OSC to Topos
|
||||
require("./OSCtoTopos")
|
||||
|
||||
require("./OSCtoTopos");
|
||||
|
||||
@ -1917,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[]
|
||||
) {
|
||||
@ -2107,7 +2107,7 @@ export class UserAPI {
|
||||
args: args,
|
||||
timetag: Math.round(Date.now() + this.app.clock.deadline),
|
||||
} as OSCMessage);
|
||||
}
|
||||
};
|
||||
|
||||
public getOSC = (address?: string): any[] => {
|
||||
/**
|
||||
@ -2116,11 +2116,11 @@ export class UserAPI {
|
||||
if (address) {
|
||||
let messages = oscMessages.filter((msg) => msg.address === address);
|
||||
messages = messages.map((msg) => msg.data);
|
||||
return messages
|
||||
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) {
|
||||
|
||||
@ -9,8 +9,8 @@ export interface OSCMessage {
|
||||
export let outputSocket = new WebSocket("ws://localhost:3000");
|
||||
export let inputSocket = new WebSocket("ws://localhost:3001");
|
||||
|
||||
export let oscMessages : any[] = [];
|
||||
inputSocket.addEventListener('message', (event) => {
|
||||
export let oscMessages: any[] = [];
|
||||
inputSocket.addEventListener("message", (event) => {
|
||||
let data = JSON.parse(event.data);
|
||||
if (oscMessages.length >= 1000) {
|
||||
oscMessages.shift();
|
||||
@ -18,7 +18,6 @@ inputSocket.addEventListener('message', (event) => {
|
||||
oscMessages.push(data);
|
||||
});
|
||||
|
||||
|
||||
// @ts-ignore
|
||||
outputSocket.onopen = function (event) {
|
||||
console.log("Connected to WebSocket Server");
|
||||
@ -26,8 +25,9 @@ outputSocket.onopen = function (event) {
|
||||
outputSocket.send(
|
||||
JSON.stringify({
|
||||
address: "/successful_connexion",
|
||||
port: 3000, args: {}
|
||||
})
|
||||
port: 3000,
|
||||
args: {},
|
||||
}),
|
||||
);
|
||||
|
||||
outputSocket.onerror = function (error) {
|
||||
|
||||
@ -465,11 +465,7 @@ export class SoundEvent extends AudibleEvent {
|
||||
if (filteredEvent.freq) {
|
||||
delete filteredEvent.note;
|
||||
}
|
||||
superdough(
|
||||
filteredEvent,
|
||||
this.app.clock.deadline,
|
||||
filteredEvent.dur
|
||||
);
|
||||
superdough(filteredEvent, this.app.clock.deadline, filteredEvent.dur);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
`;
|
||||
};
|
||||
|
||||
|
||||
@ -30,21 +30,23 @@ beat(1)::getOsc()
|
||||
// 0 : {data: Array(2), address: '/lala'}
|
||||
// 1 : {data: Array(2), address: '/lala'}
|
||||
// 2 : {data: Array(2), address: '/lala'}`,
|
||||
true)}
|
||||
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(
|
||||
${makeExample(
|
||||
"Reading the last OSC messages (filtered)",
|
||||
`
|
||||
beat(1)::getOsc("/lala")
|
||||
// 0 : (2) [89, 'bob']
|
||||
// 1 : (2) [84, 'bob']
|
||||
// 2 : (2) [82, 'bob']
|
||||
`, true)}
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
## Output
|
||||
|
||||
@ -54,7 +56,8 @@ ${makeExample(
|
||||
"Sending a simple OSC message",
|
||||
`
|
||||
beat(1)::sound('cp').speed(2).vel(0.5).osc()
|
||||
`, true
|
||||
`,
|
||||
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:
|
||||
@ -64,6 +67,9 @@ ${makeExample(
|
||||
`
|
||||
// osc(address, port, ...message)
|
||||
osc('/my/osc/address', 5000, 1, 2, 3)
|
||||
`, true)}
|
||||
`,
|
||||
true,
|
||||
)}
|
||||
|
||||
`};
|
||||
`;
|
||||
};
|
||||
|
||||
@ -4,7 +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, oscMessages } from "./IO/OSC";
|
||||
import { outputSocket } from "./IO/OSC";
|
||||
import {
|
||||
initializeSelectedUniverse,
|
||||
AppSettings,
|
||||
@ -95,7 +95,7 @@ export class Editor {
|
||||
isPlaying: boolean = false;
|
||||
|
||||
// OSC
|
||||
outputSocket: WebSocket = outputSocket
|
||||
outputSocket: WebSocket = outputSocket;
|
||||
|
||||
// Hydra
|
||||
public hydra_backend: any;
|
||||
@ -205,8 +205,6 @@ export class Editor {
|
||||
|
||||
// Loading universe from URL (if needed)
|
||||
loadUniverserFromUrl(this);
|
||||
|
||||
|
||||
}
|
||||
|
||||
private getBuffer(type: string): any {
|
||||
|
||||
Reference in New Issue
Block a user