diff options
| -rw-r--r-- | alv-lib/glsl.moon | 66 | ||||
| -rw-r--r-- | alv-lib/osc.moon | 128 | ||||
| -rw-r--r-- | alv-lib/time.moon | 2 |
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 |
