OscIn
Receive OSC messages: one table entry per address, the latest value each.
out
- Type name
signal:OscIn- Plane
signal- Tags
input- Language
python- Tier
in-process- Bundle
signal- Source
node-bundles/signal/osc_in.py- Availability
- available
Slots
| Slot | Direction | Type | |
|---|---|---|---|
out | output | TABLE |
Parameters
osc
| Name | Type | Default | Range | Doc |
|---|---|---|---|---|
host | string | 0.0.0.0 | The address to listen on. | |
port | int | 9000 | 1 … 65535 | The port to listen on. |
clear | pulse | null | Forget every address collected so far. |
common
| Name | Type | Default | Range | Doc |
|---|---|---|---|---|
autotrigger | bool | true | Run on the node's own schedule, instead of waiting for an input frame. Turn this on for sources; leave it off for transforms driven by their input. | |
max_frequency | float | 0 | 0 … 100 | Rate cap for this node, read through `frequency_mode`. 0 means uncapped — the node runs as often as the scheduler and its inputs allow. |
frequency_mode | string | updates_per_second | updates_per_second | seconds_per_update | How to read `max_frequency`: as a rate in Hz (updates per second), or as a period in seconds between updates — convenient for very slow nodes. |
Source
The current source of this node, as it stands in the goofi repository at node-bundles/signal/osc_in.py.
"""OscIn — receives Open Sound Control messages, as one table entry per address.
The server runs on its own thread and the node reads what it has collected, so nothing blocks a
tick. Latest wins: an address that arrives twice between two reads keeps only what came last. A
message carrying one string is a String; anything else is an Array of its numbers.
The address is the path into the table, so `/goofi/eeg/alpha` arrives as `goofi.eeg.alpha` —
the same shape `OscOut` sends from.
"""
import threading
import numpy as np
from pythonosc.dispatcher import Dispatcher
from pythonosc.osc_server import ThreadingOSCUDPServer
import goofi
class OscIn(goofi.Node):
"""Receive OSC messages: one table entry per address, the latest value each."""
TAGS = ["input"]
OUTPUTS = {"out": goofi.DataType.TABLE}
PRODUCER = True
PARAMS = {
"osc": {
"host": goofi.StringParam("0.0.0.0", doc="The address to listen on."),
"port": goofi.IntParam(9000, 1, 65535, doc="The port to listen on."),
"clear": goofi.PulseParam(doc="Forget every address collected so far."),
}
}
def setup(self):
self.lock = threading.Lock()
self.latest = {}
self.server = None
self.bound = None
def pulse_osc_clear(self):
with self.lock:
self.latest = {}
def receive(self, address, *args):
with self.lock:
self.latest[address] = args
def process(self):
p = self.params.osc
if (p.host, p.port) != self.bound:
self.stop()
dispatcher = Dispatcher()
dispatcher.set_default_handler(self.receive)
self.server = ThreadingOSCUDPServer((p.host, p.port), dispatcher)
threading.Thread(target=self.server.serve_forever, daemon=True).start()
self.bound = (p.host, p.port)
with self.lock:
held = dict(self.latest)
if not held:
return None
tree = {}
for address, args in held.items():
parts = [part for part in address.strip("/").split("/") if part]
here = tree
for part in parts[:-1]:
if not isinstance(here.get(part), dict):
here[part] = {}
here = here[part]
here[parts[-1]] = entry(args)
return {"out": tree}
def stop(self):
if self.server is not None:
self.server.shutdown()
self.server.server_close()
self.server = None
def entry(args):
"""One message's arguments as a frame: a lone string stays text, everything else is numbers."""
if len(args) == 1 and isinstance(args[0], str):
return args[0]
return np.asarray([float(a) for a in args], dtype=np.float32)This reference describes goofi 3.1.0(537cd394), generated from a running instance on 2026-09-06.