aboutsummaryrefslogtreecommitdiffstats
path: root/alv-lib
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2025-03-14 23:59:49 +0000
committers-ol <s+removethis@s-ol.nu>2025-03-14 23:59:49 +0000
commitda9f62d57afc464fd04bb39e0f250b40f23d78f3 (patch)
tree49b1ed4eafe17b313aed23519692c16ef3b38919 /alv-lib
parentupdate Dockerfile ref (diff)
downloadalive-macros.tar.gz
alive-macros.zip
Diffstat (limited to 'alv-lib')
-rw-r--r--alv-lib/glsl.moon66
-rw-r--r--alv-lib/osc.moon128
-rw-r--r--alv-lib/time.moon2
3 files changed, 186 insertions, 10 deletions
diff --git a/alv-lib/glsl.moon b/alv-lib/glsl.moon
new file mode 100644
index 0000000..3f973ae
--- /dev/null
+++ b/alv-lib/glsl.moon
@@ -0,0 +1,66 @@
+import Constant, Op, Builtin, Input, T, Struct, const, any from require 'alv.base'
+import new_message, add_item from require 'alv-lib._osc'
+import dns, udp from require 'socket'
+
+glsl_type = (type) ->
+ switch type
+ when T.num then 'float'
+ when T.str then 'sampler3D'
+ when T.bool then 'bool'
+ else
+ error "unknown primitive type"
+
+resubst = (str, fn) ->
+ str = str\gsub '##', '#'
+ str\gsub '#{(%d+)}', fn
+
+shader = Constant.meta
+ meta:
+ name: 'gate'
+ summary: "gate from note-on and note-off messages."
+ examples: { '(midi/gate [port] note [chan])' }
+
+ value: class extends Op
+ pattern = const.str + any!^0
+
+ setup: (inputs, scope) =>
+ { code, params } = pattern\match inputs
+
+ super [Input.hot param for param in *params]
+
+ uniforms = {}
+ inputs = {}
+
+ for i, input in ipairs @inputs
+ table.insert uniforms, "uniform #{glsl_type input\type!} alv_param_#{i};"
+
+ code = code.result!
+ code = resubst code, (i) -> "alv_param_#{i}"
+
+ table.insert uniforms, code
+ code = table.concat uniforms, '\n'
+
+ print code
+
+ @state or= with sock = udp!
+ \setpeername '127.0.0.1', 9000
+
+ msg = new_message "/-/shader"
+ add_item msg, T.str, code
+ @state\send msg.pack msg.content
+
+ tick: (setup) =>
+ for i, input in ipairs @inputs
+ if setup or input\dirty!
+ msg = new_message "/alv_param_#{i}"
+ print "/alv_param_#{i}", input!
+ add_item msg, input\type!, input!
+ @state\send msg.pack msg.content
+
+Constant.meta
+ meta:
+ name: 'glsl-view'
+ summary: "glsl-view integration via OSC."
+
+ value:
+ :shader
diff --git a/alv-lib/osc.moon b/alv-lib/osc.moon
index 8c93b07..a1c7c5d 100644
--- a/alv-lib/osc.moon
+++ b/alv-lib/osc.moon
@@ -1,7 +1,23 @@
-import Op, PureOp, Constant, Input, T, sig, any from require 'alv.base'
+import Op, PureOp, Constant, Input, T, Array, sig, const, any from require 'alv.base'
import new_message, add_item from require 'alv-lib._osc'
import dns, udp from require 'socket'
+losc = require 'losc'
+losc_Pattern = require 'losc.pattern'
+
+class LoscPlugin
+ new: (host, port) =>
+ @handle = udp!
+ assert @handle\setsockname host, port
+ @handle\settimeout 0
+
+ poll: =>
+ data = @handle\receive!
+ if data
+ losc_Pattern.dispatch data, @
+
+ close: => @handle\close!
+
unpack or= table.unpack
connect = Constant.meta
@@ -18,7 +34,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 +43,98 @@ 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
+ host: Input.hot host or Constant.str ''
+ port: Input.hot port
+
+ @setup_out '~', T['osc/in']
+
+ tick: =>
+ { :host, :port } = @unwrap_all!
+
+ @out\set with p = losc.new plugin: losc_udp2.new!
+ print \open host, port
+
+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 + sig.str
+ setup: (inputs, scope) =>
+ { port, path, types } = full_pattern\match inputs
+ types = types or Constant.str 'f'
+ super
+ port: Input.hot message or scope\get '*oscin*'
+ path: Input.hot path
+ types: Input.cold types
+
+ io: Input.hot (@get_type types.result!)\mk_evt!
+
+ @setup_out '!', @get_type types.result!
+ @state or= {}
+
+ poll: => @inputs.io\dirty!
+
+ handler: (data) =>
+ { :message } = data
+ print "handler called", message
+ types = @inputs.types!
+ assert types == message\types!
+
+ if @out\type!.__class == Array
+ @inputs.io.result\set message\args!
+ else
+ @inputs.io.result\set message[1]
+
+ tick: =>
+ if @inputs.port\dirty! or @inputs.path\dirty!
+ @state.port\remove_handler @state.path if @state.port
+ print "del handler", @state.path
+
+ @state.port, @state.path = @inputs.port!, @inputs.path!
+ @state.port\add_handler @state.path, @\handler
+ print "add handler", @state.path
+
+ if @inputs.io\dirty!
+ @out\set @inputs.io!
+
+ destroy: => if @state then @state.port\remove_handler @state.path
+
+ 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
+ Array #types, typ
+ else
+ typ
+
send = Constant.meta
meta:
name: 'send'
@@ -35,13 +143,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 +158,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 +182,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 +197,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 +220,7 @@ Constant.meta
value:
:connect
+ :listen
:send
:sync
+ 'recv!': recv_evt
diff --git a/alv-lib/time.moon b/alv-lib/time.moon
index 1f8e324..6b796ba 100644
--- a/alv-lib/time.moon
+++ b/alv-lib/time.moon
@@ -58,7 +58,7 @@ scale_time = Constant.meta
and the stream be passed as a dynamic definition in `*clock*` instead.
- `scale` should be a num~ stream."
value: class extends Op
- pattern = -evt.clock + sig.num + -sig.str
+ pattern = -evt.clock + sig.num
setup: (inputs, scope) =>
{ clock, scale } = pattern\match inputs
super