From 7cb862f6f6079509dafd466fff83c719cb2fd89e Mon Sep 17 00:00:00 2001 From: s-ol Date: Wed, 25 Mar 2020 11:43:29 +0100 Subject: new core.base.match, update lib --- lib/midi/core.moon | 70 +++++++++++-------------- lib/midi/launchctl.moon | 133 +++++++++++++++++++++++++++++++++++------------- 2 files changed, 128 insertions(+), 75 deletions(-) (limited to 'lib/midi') diff --git a/lib/midi/core.moon b/lib/midi/core.moon index d1930eb..e520bbf 100644 --- a/lib/midi/core.moon +++ b/lib/midi/core.moon @@ -1,4 +1,4 @@ -import Value, IO, Op, Registry, Input, Error, match from require 'core.base' +import ValueStream, IOStream, Op, Input, Error, val from require 'core.base' import RtMidiIn, RtMidiOut, RtMidi from require 'luartmidi' bit = do @@ -30,27 +30,23 @@ find_port = (Klass, name) -> \openport id -class MidiPort extends IO - new: (@inp, @out) => - @messages = {} - - tick: => - if @inp - @messages = while true - delta, bytes = @inp\getmessage! - break unless delta +class MidiPort extends IOStream + new: => super 'midi/port' - { status, a, b } = bytes - chan = band status, 0xf - status = MIDI[rshift status, 4] - { :status, :chan, :a, :b, port: @ } + setup: (inp, out) => + @inp = inp and find_port RtMidiIn, inp + @out = out and find_port RtMidiOut, out - dirty: => #@messages > 0 + tick: => + return unless @inp + while true + delta, bytes = @inp\getmessage! + break unless delta - receive: => - coroutine.wrap -> - for msg in *@messages - coroutine.yield msg + { status, a, b } = bytes + chan = band status, 0xf + status = MIDI[rshift status, 4] + @add { :status, :chan, :a, :b } send: (status, chan, a, b) => assert @out, Error 'type', "#{@} is not an output or bidirectional port" @@ -59,15 +55,15 @@ class MidiPort extends IO @out\sendmessage status, a, b class PortOp extends Op - new: => super 'midi/port' + new: (...) => + super ... + @out or= MidiPort! tick: (inp, out) => - if (inp and inp\dirty!) or (out and out\dirty!) - inp = inp and find_port RtMidiIn, inp! - out = out and find_port RtMidiOut, out! - @out\set MidiPort inp, out + { :inp, :out } = @unwrap_all! + @out\setup inp, out -input = Value.meta +input = ValueStream.meta meta: name: 'input' summary: "Create a MIDI input port." @@ -75,12 +71,10 @@ input = Value.meta value: class extends PortOp setup: (inputs) => - { name } = match 'str', inputs - super name: Input.value name - - tick: => super @inputs.name + name = val.str\match inputs + super inp: Input.hot name -output = Value.meta +output = ValueStream.meta meta: name: 'output' summary: "Create a MIDI output port." @@ -88,12 +82,10 @@ output = Value.meta value: class extends PortOp setup: (inputs) => - { name } = match 'str', inputs - super name: Input.value name + name = val.str\match inputs + super out: Input.hot name - tick: => super nil, @inputs.name - -inout = Value.meta +inout = ValueStream.meta meta: name: 'inout' summary: "Create a bidirectional MIDI port." @@ -101,12 +93,10 @@ inout = Value.meta value: class extends PortOp setup: (inputs) => - { inp, out } = match 'str str', inputs + { inp, out } = (val.str + val.str)\match inputs super - inp: Input.value inp - out: Input.value out - - tick: => super @inputs.inp, @inputs.out + inp: Input.hot inp + out: Input.hot out apply_range = (range, val) -> if range\type! == 'str' diff --git a/lib/midi/launchctl.moon b/lib/midi/launchctl.moon index 37ccb82..1e33cc4 100644 --- a/lib/midi/launchctl.moon +++ b/lib/midi/launchctl.moon @@ -1,15 +1,15 @@ -import Value, Op, Input, match from require 'core.base' +import ValueStream, EventStream, Op, Input, val, evt from require 'core.base' import apply_range, bit from require 'lib.midi.core' import bor, lshift from bit unpack or= table.unpack color = (r, g) -> bit.bor 12, r, (bit.lshift g, 4) -cc_seq = Value.meta +cc_seq = ValueStream.meta meta: name: 'cc-seq' summary: "MIDI CC-Sequencer." - examples: { '(launchctl/cc-seq port i start chan [steps [range]])' } + examples: { '(launchctl/cc-seq [port] i start chan [steps [range]])' } description: " returns the value for the i-th step steps (buttons starting from start). steps defaults to 8. @@ -23,22 +23,21 @@ range can be one of: - (num) [ 0 - num[" value: class extends Op - new: => super 'num' - - setup: (inputs) => - { port, i, start, - chan, steps, range } = match 'midi/port num num num num? any?', inputs + num = val.num + pattern = -evt['midi/port'] + num + num + num + -num + -(val.str + num) + setup: (inputs, scope) => + { port, i, start, chan, steps, range } = pattern\match inputs super - port: Input.io port - i: Input.value i - start: Input.value start - chan: Input.value chan - steps: Input.value steps or Value.num 8 - range: Input.value range or Value.str 'uni' + port: Input.hot port or scope\get '*ctrl*' + i: Input.hot i + start: Input.hot start + chan: Input.hot chan + steps: Input.hot steps or ValueStream.num 8 + range: Input.hot range or ValueStream.str 'uni' - if not @out\unwrap! - @out\set apply_range @inputs.range, 0 + @state or= {} + @out or= ValueStream 'num', apply_range @inputs.range, 0 tick: => { :port, :i, :start, :chan, :steps, :range } = @inputs @@ -52,7 +51,7 @@ range can be one of: curr_i = i! % #@state if port\dirty! changed = false - for msg in port!\receive! + for msg in *port! if msg.status == 'control-change' and msg.chan == chan! rel_i = msg.a - start! if rel_i >= 0 and rel_i < #@state @@ -62,29 +61,28 @@ range can be one of: else @out\set apply_range range, @state[curr_i+1] -gate_seq = Value.meta +gate_seq = ValueStream.meta meta: name: 'gate-seq' summary: "MIDI Gate-Sequencer." - examples: { '(launchctl/gate-seq port i start chan [steps])' } + examples: { '(launchctl/gate-seq [port] i start chan [steps])' } description: " -returns `true` or `false` for the `i`-th note-button (MIDI-notes starting from +Send `true` or `false` for the `i`-th note-button (MIDI-notes starting from `start`). `steps` defaults to 8." value: class extends Op - new: => - super 'bool', false - @state = {} - - setup: (inputs) => - { port, i, start, chan, steps } = match 'midi/port num num num num?', inputs + pattern = -evt['midi/port'] + val.num + val.num + val.num + -val.num + setup: (inputs, scope) => + @out or= ValueStream 'bool' + @state or= {} + { port, i, start, chan, steps } = pattern\match inputs super - port: Input.io port - i: Input.value i - start: Input.value start - chan: Input.value chan - steps: Input.value steps or Value.num 8 + port: Input.hot port or scope\get '*ctrl*' + i: Input.hot i + start: Input.hot start + chan: Input.hot chan + steps: Input.hot steps or ValueStream.num 8 light = (set, active) -> set = if set then 'S' else ' ' @@ -96,8 +94,8 @@ returns `true` or `false` for the `i`-th note-button (MIDI-notes starting from when 'SA' then 3, 1 display: (i, active) => - { :port, :start, :chan } = @unwrap_all! - port\send 'note-on', chan, (start + i), light @state[i+1], active + start, chan = @inputs.start!, @inputs.chan! + @inputs.port.stream\send 'note-on', chan, (start + i), light @state[i+1], active tick: => { :port, :i, :start, :chan, :steps } = @inputs @@ -111,7 +109,7 @@ returns `true` or `false` for the `i`-th note-button (MIDI-notes starting from curr_i = i! % #@state if port\dirty! - for msg in port!\receive! + for msg in *port! if msg.status == 'note-on' and msg.chan == chan! rel_i = msg.a - start! if rel_i >= 0 and rel_i < #@state @@ -126,7 +124,72 @@ returns `true` or `false` for the `i`-th note-button (MIDI-notes starting from @out\set @state[curr_i+1] +trig_seq = ValueStream.meta + meta: + name: 'trig-seq' + summary: "MIDI Trigger-Sequencer." + examples: { '(launchctl/trig-seq [port] i start chan [steps])' } + description: " +Send bangs for the `i`-th note-button (MIDI-notes starting from `start`). +`steps` defaults to 8." + + value: class extends Op + pattern = -evt['midi/port'] + val.num + val.num + val.num + -val.num + setup: (inputs, scope) => + @out or= EventStream 'bang' + @state or= {} + { port, i, start, chan, steps } = pattern\match inputs + + super + port: Input.hot port or scope\get '*ctrl*' + i: Input.hot i + start: Input.hot start + chan: Input.hot chan + steps: Input.hot steps or ValueStream.num 8 + + light = (set, active) -> + set = if set then 'S' else ' ' + active = if active then 'A' else ' ' + color switch set .. active + when ' ' then 0, 0 + when ' A' then 1, 1 + when 'S ' then 1, 0 + when 'SA' then 3, 1 + + display: (i, active) => + start, chan = @inputs.start!, @inputs.chan! + @inputs.port.stream\send 'note-on', chan, (start + i), light @state[i+1], active + + tick: => + { :port, :i, :start, :chan, :steps } = @inputs + + if steps\dirty! + while steps! > #@state + table.insert @state, false + while steps! < #@state + table.remove @state + + curr_i = i! % #@state + + if port\dirty! + for msg in *port! + if msg.status == 'note-on' and msg.chan == chan! + rel_i = msg.a - start! + if rel_i >= 0 and rel_i < #@state + @state[rel_i+1] = not @state[rel_i+1] + @display rel_i, rel_i == curr_i + + if i\dirty! + prev_i = (curr_i - 1) % #@state + + @display curr_i, true + @display prev_i, false + + if @state[curr_i+1] + @out\add true + { - 'gate-seq': gate_seq 'cc-seq': cc_seq + 'gate-seq': gate_seq + 'trig-seq': trig_seq } -- cgit v1.2.3