prepare for osc input

This commit is contained in:
2023-12-04 16:28:07 +01:00
parent 04a4f28f68
commit cc963ac54f
9 changed files with 99 additions and 19 deletions

View File

@ -15,6 +15,18 @@ console.log("Listening to: ws://localhost:3000. Open Topos.\n");
// Listening to WebSocket messages // Listening to WebSocket messages
const wss = new WebSocket.Server({ port: 3000 }); const wss = new WebSocket.Server({ port: 3000 });
// Sending WebSocket messages
const inputWss = new WebSocket.Server({ port: 3001 });
inputWss.on("connection", function (ws) {
inputWss.clients.forEach(function each(client) {
// Send message to all clients except sender
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send("New client connected");
}
})
});
// Setting up for message broadcasting // Setting up for message broadcasting
wss.on("connection", function (ws) { wss.on("connection", function (ws) {
console.log("> Client connected"); console.log("> Client connected");

View File

@ -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_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_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> </div>
</details> </details>
<details class="space-y-2" open=true> <details class="space-y-2" open=true>

View File

@ -1,4 +1,5 @@
import { EditorView } from "@codemirror/view"; import { EditorView } from "@codemirror/view";
import { sendToServer, type OSCMessage } from "./IO/OSC";
import { getAllScaleNotes, nearScales, seededRandom } from "zifferjs"; import { getAllScaleNotes, nearScales, seededRandom } from "zifferjs";
import { import {
MidiCCEvent, MidiCCEvent,
@ -2095,6 +2096,20 @@ export class UserAPI {
}, real_duration * 1000); }, 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);
}
// ============================================================= // =============================================================
// Transport functions // Transport functions
// ============================================================= // =============================================================

View File

@ -24,6 +24,7 @@ import { linear_time } from "./documentation/time/linear_time";
import { cyclical_time } from "./documentation/time/cyclical_time"; import { cyclical_time } from "./documentation/time/cyclical_time";
import { long_forms } from "./documentation/long_forms"; import { long_forms } from "./documentation/long_forms";
import { midi } from "./documentation/midi"; import { midi } from "./documentation/midi";
import { osc } from "./documentation/osc";
import { sound } from "./documentation/engine"; import { sound } from "./documentation/engine";
import { patterns } from "./documentation/patterns"; import { patterns } from "./documentation/patterns";
import { functions } from "./documentation/functions"; import { functions } from "./documentation/functions";
@ -92,6 +93,7 @@ export const documentation_factory = (application: Editor) => {
patterns: patterns(application), patterns: patterns(application),
ziffers: ziffers(application), ziffers: ziffers(application),
midi: midi(application), midi: midi(application),
osc: osc(application),
lfos: lfos(application), lfos: lfos(application),
variables: variables(application), variables: variables(application),
probabilities: probabilities(application), probabilities: probabilities(application),

View File

@ -1,4 +1,3 @@
export let socket = new WebSocket("ws://localhost:3000");
export interface OSCMessage { export interface OSCMessage {
address: string; address: string;
port: number; port: number;
@ -6,44 +5,52 @@ export interface OSCMessage {
timetag: number; timetag: number;
} }
// Send/receive messages from websocket
export let outputSocket = new WebSocket("ws://localhost:3000");
export let inputSocket = new WebSocket("ws://localhost:3001");
inputSocket.onmessage= function (event) {
console.log("Received: ", event.data);
}
// @ts-ignore // @ts-ignore
socket.onopen = function (event) { outputSocket.onopen = function (event) {
console.log("Connected to WebSocket Server"); console.log("Connected to WebSocket Server");
// Send an OSC-like message // Send an OSC-like message
socket.send( outputSocket.send(
JSON.stringify({ JSON.stringify({
address: "/successful_connexion", address: "/successful_connexion",
args: true, args: true,
}) })
); );
socket.onerror = function (error) { outputSocket.onerror = function (error) {
console.log("Websocket Error:", error); console.log("Websocket Error:", error);
}; };
socket.onmessage = function (event) { outputSocket.onmessage = function (event) {
console.log("Received: ", event.data); console.log("Received: ", event.data);
}; };
}; };
export function sendToServer(message: OSCMessage) { export function sendToServer(message: OSCMessage) {
if (socket.readyState === WebSocket.OPEN) { if (outputSocket.readyState === WebSocket.OPEN) {
socket.send(JSON.stringify(message)); outputSocket.send(JSON.stringify(message));
} else { } else {
console.log("WebSocket is not open. Attempting to reconnect..."); console.log("WebSocket is not open. Attempting to reconnect...");
if ( if (
socket.readyState === WebSocket.CONNECTING || outputSocket.readyState === WebSocket.CONNECTING ||
socket.readyState === WebSocket.OPEN outputSocket.readyState === WebSocket.OPEN
) { ) {
socket.close(); outputSocket.close();
} }
// Create a new WebSocket connection // Create a new WebSocket connection
socket = new WebSocket("ws://localhost:3000"); outputSocket = new WebSocket("ws://localhost:3000");
// Send the message once the socket is open // Send the message once the socket is open
socket.onopen = () => { outputSocket.onopen = () => {
socket.send(JSON.stringify(message)); outputSocket.send(JSON.stringify(message));
}; };
} }
} }

View File

@ -499,12 +499,12 @@ export const installInterfaceLogic = (app: Editor) => {
"linear", "linear",
"cyclic", "cyclic",
"longform", "longform",
// "sound",
"synths", "synths",
"chaining", "chaining",
"patterns", "patterns",
"ziffers", "ziffers",
"midi", "midi",
"osc",
"functions", "functions",
"lfos", "lfos",
"probabilities", "probabilities",
@ -520,7 +520,7 @@ export const installInterfaceLogic = (app: Editor) => {
].forEach((e) => { ].forEach((e) => {
let name = `docs_` + e; let name = `docs_` + e;
document.getElementById(name)!.addEventListener("click", async () => { document.getElementById(name)!.addEventListener("click", async () => {
if (name !== "docs_samples") { if (name !== "docs_sample_list") {
app.currentDocumentationPane = e; app.currentDocumentationPane = e;
updateDocumentationContent(app, bindings); updateDocumentationContent(app, bindings);
} else { } else {

View File

@ -1,5 +1,5 @@
import { type Editor } from "./main"; import { type Editor } from "./main";
import { socket } from "./IO/OSC"; import { outputSocket, inputSocket } from "./IO/OSC";
const handleResize = (canvas: HTMLCanvasElement) => { const handleResize = (canvas: HTMLCanvasElement) => {
if (!canvas) return; if (!canvas) return;
@ -28,7 +28,8 @@ export const saveBeforeExit = (app: Editor): null => {
app.currentFile().committed = app.view.state.doc.toString(); app.currentFile().committed = app.view.state.doc.toString();
app.settings.saveApplicationToLocalStorage(app.universes, app.settings); app.settings.saveApplicationToLocalStorage(app.universes, app.settings);
// Close the websocket // Close the websocket
socket.close(); inputSocket.close();
outputSocket.close();
return null; return null;
}; };

42
src/documentation/osc.ts Normal file
View File

@ -0,0 +1,42 @@
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 secure connexions. 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>.
- 3) Start the server using <ic>npm start</ic>.
- 4) Open the <ic>Topos</ic> application in your web browser.
## Input
## 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)}
`};

View File

@ -4,7 +4,7 @@ import { scriptBlinkers } from "./Visuals/Blinkers";
import { javascript } from "@codemirror/lang-javascript"; import { javascript } from "@codemirror/lang-javascript";
import { markdown } from "@codemirror/lang-markdown"; import { markdown } from "@codemirror/lang-markdown";
import { Extension } from "@codemirror/state"; import { Extension } from "@codemirror/state";
import { socket } from "./IO/OSC"; import { outputSocket } from "./IO/OSC";
import { import {
initializeSelectedUniverse, initializeSelectedUniverse,
AppSettings, AppSettings,
@ -95,7 +95,7 @@ export class Editor {
isPlaying: boolean = false; isPlaying: boolean = false;
// OSC // OSC
socket: WebSocket = socket outputSocket: WebSocket = outputSocket
// Hydra // Hydra
public hydra_backend: any; public hydra_backend: any;