diff options
Diffstat (limited to 'lib/midi')
| -rw-r--r-- | lib/midi/core.moon | 70 | ||||
| -rw-r--r-- | lib/midi/launchctl.moon | 210 |
2 files changed, 150 insertions, 130 deletions
diff --git a/lib/midi/core.moon b/lib/midi/core.moon index c437259..4986811 100644 --- a/lib/midi/core.moon +++ b/lib/midi/core.moon @@ -1,4 +1,4 @@ -import IO, Op, Registry, Input, Error, match from require 'core.base' +import Value, IO, Op, Registry, Input, Error, match from require 'core.base' import RtMidiIn, RtMidiOut, RtMidi from require 'luartmidi' bit = do @@ -67,34 +67,46 @@ class PortOp extends Op out = out and find_port RtMidiOut, out! @out\set MidiPort inp, out -class input extends PortOp - @doc: "(midi/input name) - create a MIDI input port" - - setup: (inputs) => - { name } = match 'str', inputs - super name: Input.value name - - tick: => super @inputs.name - -class output extends PortOp - @doc: "(midi/output name) - create a MIDI output port" - - setup: (inputs) => - { name } = match 'str', inputs - super name: Input.value name - - tick: => super nil, @inputs.name - -class inout extends PortOp - @doc: "(midi/inout inname outname) - create a bidirectional MIDI port" - - setup: (inputs) => - { inp, out } = match 'str str', inputs - super - inp: Input.value inp - out: Input.value out - - tick: => super @inputs.inp, @inputs.out +input = Value.meta + meta: + name: 'input' + summary: "Create a MIDI input port." + examples: { '(midi/input name)' } + + value: class extends PortOp + setup: (inputs) => + { name } = match 'str', inputs + super name: Input.value name + + tick: => super @inputs.name + +output = Value.meta + meta: + name: 'output' + summary: "Create a MIDI output port." + examples: { '(midi/output name)' } + + value: class extends PortOp + setup: (inputs) => + { name } = match 'str', inputs + super name: Input.value name + + tick: => super nil, @inputs.name + +inout = Value.meta + meta: + name: 'inout' + summary: "Create a bidirectional MIDI port." + examples: { '(midi/inout name)' } + + value: class extends PortOp + setup: (inputs) => + { inp, out } = match 'str str', inputs + super + inp: Input.value inp + out: Input.value out + + tick: => super @inputs.inp, @inputs.out apply_range = (range, val) -> if range\type! == 'str' diff --git a/lib/midi/launchctl.moon b/lib/midi/launchctl.moon index 1084f66..37ccb82 100644 --- a/lib/midi/launchctl.moon +++ b/lib/midi/launchctl.moon @@ -5,9 +5,12 @@ import bor, lshift from bit unpack or= table.unpack color = (r, g) -> bit.bor 12, r, (bit.lshift g, 4) -class cc_seq extends Op - @doc: "(launctl/cc-seq port i start chan [steps [range]]) - CC-Sequencer - +cc_seq = Value.meta + meta: + name: 'cc-seq' + summary: "MIDI CC-Sequencer." + 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. @@ -19,104 +22,109 @@ range can be one of: - 'deg' [ 0 - 360[ - (num) [ 0 - num[" - new: => super 'num' - - setup: (inputs) => - { port, i, start, - chan, steps, range } = match 'midi/port num num num num? any?', 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' - - if not @out\unwrap! - @out\set apply_range @inputs.range, 0 - - tick: => - { :port, :i, :start, :chan, :steps, :range } = @inputs - - if steps\dirty! - while steps! > #@state - table.insert @state, 0 - while steps! < #@state - table.remove @state - - curr_i = i! % #@state - if port\dirty! - changed = false - for msg in port!\receive! - if msg.status == 'control-change' and msg.chan == chan! - rel_i = msg.a - start! - if rel_i >= 0 and rel_i < #@state - @state[rel_i+1] = msg.b - changed = rel_i == curr_i - @out\set apply_range range, @state[curr_i+1] if changed - else - @out\set apply_range range, @state[curr_i+1] - -class gate_seq extends Op - @doc: "(launctl/gate-seq port i start chan [steps]) - Gate-Sequencer - -returns true or false for the i-th step steps (buttons starting from start). -steps defaults to 8." - - new: => - super 'bool', false - @state = {} - - setup: (inputs) => - { port, i, start, chan, steps } = match 'midi/port num num num num?', 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 - - 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) => - { :port, :start, :chan } = @unwrap_all! - port\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!\receive! - 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 - - @out\set @state[curr_i+1] + value: class extends Op + new: => super 'num' + + setup: (inputs) => + { port, i, start, + chan, steps, range } = match 'midi/port num num num num? any?', 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' + + if not @out\unwrap! + @out\set apply_range @inputs.range, 0 + + tick: => + { :port, :i, :start, :chan, :steps, :range } = @inputs + + if steps\dirty! + while steps! > #@state + table.insert @state, 0 + while steps! < #@state + table.remove @state + + curr_i = i! % #@state + if port\dirty! + changed = false + for msg in port!\receive! + if msg.status == 'control-change' and msg.chan == chan! + rel_i = msg.a - start! + if rel_i >= 0 and rel_i < #@state + @state[rel_i+1] = msg.b + changed = rel_i == curr_i + @out\set apply_range range, @state[curr_i+1] if changed + else + @out\set apply_range range, @state[curr_i+1] + +gate_seq = Value.meta + meta: + name: 'gate-seq' + summary: "MIDI Gate-Sequencer." + examples: { '(launchctl/gate-seq port i start chan [steps])' } + description: " +returns `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 + + 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 + + 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) => + { :port, :start, :chan } = @unwrap_all! + port\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!\receive! + 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 + + @out\set @state[curr_i+1] { 'gate-seq': gate_seq |
