diff options
| author | s-ol <s+removethis@s-ol.nu> | 2025-03-18 11:04:57 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-03-18 11:05:10 +0000 |
| commit | ca06f7154ded1fc44844d24ce3e2718cd4fa5171 (patch) | |
| tree | 4c74c1b8feddb19b2749d1445dde22c6215a45fd /alv-lib | |
| parent | lib: add random/int (diff) | |
| download | alive-ca06f7154ded1fc44844d24ce3e2718cd4fa5171.tar.gz alive-ca06f7154ded1fc44844d24ce3e2718cd4fa5171.zip | |
lib: support receiving OSC
Diffstat (limited to 'alv-lib')
| -rw-r--r-- | alv-lib/osc.moon | 120 |
1 files changed, 110 insertions, 10 deletions
diff --git a/alv-lib/osc.moon b/alv-lib/osc.moon index 8c93b07..d54b1b1 100644 --- a/alv-lib/osc.moon +++ b/alv-lib/osc.moon @@ -1,7 +1,10 @@ -import Op, PureOp, Constant, Input, T, sig, any from require 'alv.base' -import new_message, add_item from require 'alv-lib._osc' +import Op, PureOp, Constant, Input, T, Array, sig, const, any from require 'alv.base' +import new_message, add_item, Message from require 'alv-lib._osc' import dns, udp from require 'socket' +losc = require 'losc' +losc_Pattern = require 'losc.pattern' + unpack or= table.unpack connect = Constant.meta @@ -18,7 +21,7 @@ connect = Constant.meta host: Input.hot host port: Input.hot port - @setup_out '~', T['udp/socket'] + @setup_out '~', T['osc/out'] tick: => { :host, :port } = @unwrap_all! @@ -27,6 +30,101 @@ connect = Constant.meta @out\set with sock = udp! \setpeername ip, port +listen = Constant.meta + meta: + name: 'listen' + summary: "Create a UDP server." + examples: { '(osc/listen port bind-addr)' } + + value: class extends Op + pattern = sig.num + -sig.str + setup: (inputs) => + { port, host } = pattern\match inputs + super + port: Input.hot port + host: Input.hot host or Constant.str '*' + io: Input.hot T['osc/in']\mk_evt! + + @setup_out '!', T['osc/in'] + + poll: => + messages = while true + data = @state\receive! + break unless data + + ok, msg = pcall Message.unpack, data + if not ok + print "invalid OSC message:", msg + continue + + msg + + if #messages > 0 + @inputs.io.result\set messages + true + + tick: => + if @inputs.port\dirty! or @inputs.host\dirty! + @state = with udp! + \settimeout 0 + assert \setsockname @inputs.host!, @inputs.port! + + if msgs = @inputs.io! + @out\set msgs + +recv_evt = Constant.meta + meta: + name: 'recv!' + summary: "Receive OSC messages." + examples: { '(osc/recv! [socket] path [type-str])' } + + value: class extends Op + pattern: any!^0 + + full_pattern = -sig['osc/in'] + sig.str + -const.str + setup: (inputs, scope) => + { messages, path, types } = full_pattern\match inputs + types = types or Constant.str 'f' + super + messages: Input.hot messages or scope\get '*oscin*' + path: Input.cold path + types: Input.cold types + + @state, type = @get_type types.result! + @setup_out '!', type + + tick: => + { :messages, :path, :types } = @unwrap_all! + for msg in *(messages or {}) + continue if msg.address != path + continue if msg.types != types + + if @state + @out\set [v for v in *msg] + else + @out\set msg[1] + break + + get_type: (types) => + types = types\gsub '[%[%]]', '' + first = types\sub 1, 1 + assert first, "empty type string" + + for i=2, #types + assert first == (types\sub i, 1), "mixed types in array" + + typ = switch first + when 'f', 'i' then T.num + when 's', 'S', 'c' then T.str + when 'T', 'F' then T.bool + when 'I' then T.bang + else error "unknown typetag" + + if #types > 1 + true, Array #types, typ + else + false, typ + send = Constant.meta meta: name: 'send' @@ -35,13 +133,13 @@ send = Constant.meta description: "Sends an OSC message to `path` with `val…` as arguments. - `socket` should be a `udp/socket` value. This argument can be omitted and the - value be passed as a dynamic definition in `*sock*` instead. + value be passed as a dynamic definition in `*oscout*` instead. - `path` is the OSC path to send the message to. It should be a string-value. - the arguments can be any type: - `num` will be sent as `f` - `str` will be sent as `s` - `bool` will be sent as `T`/`F` - - `bang` will be sent as `T` + - `bang` will be sent as `I` - arrays will be unwrapped - structs will be sent as a series of key/value tuples @@ -50,11 +148,11 @@ This is a pure op, so between the values at most one !-stream input is allowed." value: class extends PureOp pattern: any!^0 - full_pattern = -sig['udp/socket'] + sig.str + any!^0 + full_pattern = -sig['osc/in'] + sig.str + any!^0 setup: (inputs, scope) => { socket, path, values } = full_pattern\match inputs super values, scope, { - socket: Input.cold socket or scope\get '*sock*' + socket: Input.cold socket or scope\get '*oscout*' path: Input.cold path } @@ -74,7 +172,7 @@ send_arr = Constant.meta description: "Sends an OSC message to `path` with `val…` as arguments. - `socket` should be a `udp/socket` value. This argument can be omitted and the - value be passed as a dynamic definition in `*sock*` instead. + value be passed as a dynamic definition in `*oscout*` instead. - `path` is the OSC path to send the message to. It should be a string-value. - the arguments can be any type: - `num` will be sent as `f` @@ -89,11 +187,11 @@ This is a pure op, so between the values at most one !-stream input is allowed." value: class extends PureOp pattern: any!^0 - full_pattern = -sig['udp/socket'] + sig.str + any!^0 + full_pattern = -sig['udp/out'] + sig.str + any!^0 setup: (inputs, scope) => { socket, path, values } = full_pattern\match inputs super values, scope, { - socket: Input.cold socket or scope\get '*sock*' + socket: Input.cold socket or scope\get '*oscout*' path: Input.cold path } @@ -112,5 +210,7 @@ Constant.meta value: :connect + :listen :send :sync + 'recv!': recv_evt |
