diff --git a/ToposServer/server.js b/ToposServer/server.js
index 7dc6216..ef8b4aa 100644
--- a/ToposServer/server.js
+++ b/ToposServer/server.js
@@ -15,6 +15,18 @@ console.log("Listening to: ws://localhost:3000. Open Topos.\n");
// Listening to WebSocket messages
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
wss.on("connection", function (ws) {
console.log("> Client connected");
diff --git a/index.html b/index.html
index edde30b..8ce117c 100644
--- a/index.html
+++ b/index.html
@@ -180,6 +180,7 @@
Patterns
MIDI
+ OSC
diff --git a/src/API.ts b/src/API.ts
index 8e4950a..11dbb79 100644
--- a/src/API.ts
+++ b/src/API.ts
@@ -1,4 +1,5 @@
import { EditorView } from "@codemirror/view";
+import { sendToServer, type OSCMessage } from "./IO/OSC";
import { getAllScaleNotes, nearScales, seededRandom } from "zifferjs";
import {
MidiCCEvent,
@@ -2095,6 +2096,20 @@ 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);
+ }
+
+
// =============================================================
// Transport functions
// =============================================================
diff --git a/src/Documentation.ts b/src/Documentation.ts
index 5a86c77..bf2dafa 100644
--- a/src/Documentation.ts
+++ b/src/Documentation.ts
@@ -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),
diff --git a/src/IO/OSC.ts b/src/IO/OSC.ts
index 312c176..b5814a7 100644
--- a/src/IO/OSC.ts
+++ b/src/IO/OSC.ts
@@ -1,4 +1,3 @@
-export let socket = new WebSocket("ws://localhost:3000");
export interface OSCMessage {
address: string;
port: number;
@@ -6,44 +5,52 @@ export interface OSCMessage {
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
-socket.onopen = function (event) {
+outputSocket.onopen = function (event) {
console.log("Connected to WebSocket Server");
// Send an OSC-like message
- socket.send(
+ outputSocket.send(
JSON.stringify({
address: "/successful_connexion",
args: true,
})
);
- socket.onerror = function (error) {
+ outputSocket.onerror = function (error) {
console.log("Websocket Error:", error);
};
- socket.onmessage = function (event) {
+ outputSocket.onmessage = function (event) {
console.log("Received: ", event.data);
};
};
export function sendToServer(message: OSCMessage) {
- if (socket.readyState === WebSocket.OPEN) {
- socket.send(JSON.stringify(message));
+ if (outputSocket.readyState === WebSocket.OPEN) {
+ outputSocket.send(JSON.stringify(message));
} else {
console.log("WebSocket is not open. Attempting to reconnect...");
if (
- socket.readyState === WebSocket.CONNECTING ||
- socket.readyState === WebSocket.OPEN
+ outputSocket.readyState === WebSocket.CONNECTING ||
+ outputSocket.readyState === WebSocket.OPEN
) {
- socket.close();
+ outputSocket.close();
}
// 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
- socket.onopen = () => {
- socket.send(JSON.stringify(message));
+ outputSocket.onopen = () => {
+ outputSocket.send(JSON.stringify(message));
};
}
}
diff --git a/src/InterfaceLogic.ts b/src/InterfaceLogic.ts
index 290a757..28d2cf0 100644
--- a/src/InterfaceLogic.ts
+++ b/src/InterfaceLogic.ts
@@ -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 {
diff --git a/src/WindowBehavior.ts b/src/WindowBehavior.ts
index 8678277..89462f3 100644
--- a/src/WindowBehavior.ts
+++ b/src/WindowBehavior.ts
@@ -1,5 +1,5 @@
import { type Editor } from "./main";
-import { socket } from "./IO/OSC";
+import { outputSocket, inputSocket } from "./IO/OSC";
const handleResize = (canvas: HTMLCanvasElement) => {
if (!canvas) return;
@@ -28,7 +28,8 @@ export const saveBeforeExit = (app: Editor): null => {
app.currentFile().committed = app.view.state.doc.toString();
app.settings.saveApplicationToLocalStorage(app.universes, app.settings);
// Close the websocket
- socket.close();
+ inputSocket.close();
+ outputSocket.close();
return null;
};
diff --git a/src/documentation/osc.ts b/src/documentation/osc.ts
new file mode 100644
index 0000000..9848616
--- /dev/null
+++ b/src/documentation/osc.ts
@@ -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 ToposServer 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 Topos and navigate to the ToposServer folder.
+- 2) Install the dependencies using npm install.
+- 3) Start the server using npm start.
+- 4) Open the Topos 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 osc() function:
+
+${makeExample(
+ "Sending a customized OSC message",
+ `
+// osc(address, port, ...message)
+osc('/my/osc/address', 5000, 1, 2, 3)
+ `, true)}
+
+`};
\ No newline at end of file
diff --git a/src/main.ts b/src/main.ts
index 73afa50..13972c2 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -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 { socket } from "./IO/OSC";
+import { outputSocket } from "./IO/OSC";
import {
initializeSelectedUniverse,
AppSettings,
@@ -95,7 +95,7 @@ export class Editor {
isPlaying: boolean = false;
// OSC
- socket: WebSocket = socket
+ outputSocket: WebSocket = outputSocket
// Hydra
public hydra_backend: any;