Skip to content

Commit

Permalink
📦 Open Sound Control
Browse files Browse the repository at this point in the history
- UDP messaging
  • Loading branch information
vassbo committed Dec 10, 2024
1 parent d8d6980 commit 5963efb
Show file tree
Hide file tree
Showing 5 changed files with 154 additions and 1 deletion.
31 changes: 31 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@
"mp4box": "^0.5.2",
"node-machine-id": "^1.1.12",
"npm-run-all": "^4.1.5",
"osc-js": "^2.4.1",
"pdf2img-electron": "^1.2.3",
"pptx2json": "^0.0.10",
"protobufjs": "^7.2.3",
Expand Down
39 changes: 38 additions & 1 deletion src/electron/utils/api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ipcMain } from "electron"
import express from "express"
import http from "http"
import OSC from "osc-js"
import { Server } from "socket.io"
import { uid } from "uid"
import { toApp } from ".."
Expand All @@ -15,6 +16,7 @@ const DEFAULT_PORTS = { WebSocket: 5505, REST: 5506 }
export function startWebSocketAndRest(port: number | undefined) {
startRestListener(port ? port + 1 : 0)
startWebSocket(port)
startOSC(port)
}

// WEBSOCKET
Expand All @@ -24,7 +26,7 @@ export function startWebSocket(PORT: number | undefined) {

if (!PORT) PORT = DEFAULT_PORTS.WebSocket
server.listen(PORT, () => {
console.log(`WebSocket: Starting server at port ${PORT}.`)
console.log(`WebSocket: Starting server at port ${PORT}`)
})

server.once("error", (err: any) => {
Expand Down Expand Up @@ -93,6 +95,41 @@ export function startRestListener(PORT: number | undefined) {
})
}

// Open Sound Control

function startOSC(PORT: number | undefined) {
if (!PORT) PORT = DEFAULT_PORTS.WebSocket

// const osc = new OSC({ plugin: new OSC.WebsocketServerPlugin() }) // ws://ip:port
const osc = (servers.OSC = new OSC({ plugin: new OSC.DatagramPlugin() })) // UDP

osc.on("/freeshow/*", async (msg: any) => {
// const active = msg.args[1] || 0
let args: any = {}
try {
args = JSON.parse(msg.args[0] || "{}")
} catch (err) {
console.log("OSC: Could not parse JSON!\n", err)
}

const action = msg.address.replace("/freeshow", "")
const returnData = await receivedData({ action, ...args }, (msg: string) => console.log(`OSC: ${msg}`))
if (!returnData) return

var message = new OSC.Message(msg.address, returnData)
osc.send(message)
})

osc.on("open", () => {
console.log(`OSC: Listening for data at port ${PORT}`)
})
osc.on("error", (err: any) => {
console.log(`OSC: Error. ${JSON.stringify(err)}`)
})

osc.open({ port: PORT })
}

// DATA

async function receivedData(data: any = {}, log: any) {
Expand Down
4 changes: 4 additions & 0 deletions src/frontend/components/actions/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
startScripture,
toggleLock,
} from "./apiHelper"
import { oscToAPI } from "./apiOSC"
import { sendRestCommandSync } from "./rest"

/// STEPS TO CREATE A CUSTOM API ACTION ///
Expand Down Expand Up @@ -224,6 +225,9 @@ export const API_ACTIONS = {
/// RECEIVER / SENDER ///

export async function triggerAction(data: API) {
// Open Sound Control format
if (data.action.startsWith("/")) data = oscToAPI(data)

let id = data.action

// API start at 1, code start at 0
Expand Down
80 changes: 80 additions & 0 deletions src/frontend/components/actions/apiOSC.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Examples: /show/<id>/start | /slide/next | /clear/all
const oscActions = {
// project: {
// _id: (id: string) => ({
// open: () => ({ action: "id_select_project", id }),
// // open: () => ({ action: "index_select_project", index: Number(id) }),
// }),
// },
slide: {
next: () => ({ action: "next_slide" }),
previous: () => ({ action: "previous_slide" }),
},
show: {
_id: (id: string) => ({
// open: () => ({ action: "id_select_show", id }),
start: () => ({ action: "start_show", id }),
// slide: () => ({
// next: () => ({ action: "next_slide", id }),
// previous: () => ({ action: "previous_slide", id }),
// // _id: (slideId: string) => ({
// // start: () => ({ action: "id_select_slide", id, slideId }),
// // }),
// }),
}),
},
clear: {
all: () => ({ action: "clear_all" }),
background: () => ({ action: "clear_background" }),
slide: () => ({ action: "clear_slide" }),
overlays: () => ({ action: "clear_overlays" }),
audio: () => ({ action: "clear_audio" }),
next_timer: () => ({ action: "clear_next_timer" }),
},
timer: {
_id: (id: string) => ({
start: () => ({ action: "id_start_timer", id }),
}),
stop: () => ({ action: "stop_timers" }),
},
}

// data: { action: string, ... }
export function oscToAPI(data: any) {
try {
data = { ...data, ...parsePath(data.action) }
} catch (err) {
// use path value as api action id
let action = data.action.slice(1)
if (!action.includes("/")) return { ...data, action }

console.log(err)
return data
}

console.log("OSC API DATA:", data)
return data
}

function parsePath(path) {
const parts = path.split("/").filter(Boolean)
console.log("OSC API PATH:", path)

let currentPath: any = oscActions

for (let part of parts) {
if (typeof currentPath[part] === "function") {
currentPath = currentPath[part]()
} else if (currentPath[part]) {
currentPath = currentPath[part]
} else if (currentPath._id) {
currentPath = currentPath._id(part)
} else {
throw new Error(`Invalid OSC API path: ${path}`)
}
}

if (typeof currentPath !== "object") return {}

return currentPath
}

0 comments on commit 5963efb

Please sign in to comment.