aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-02-13 20:28:13 +0000
committers-ol <s-ol@users.noreply.github.com>2020-02-13 20:28:13 +0000
commit42d4109398fd5cf2416ef076af3c2ddea16a5070 (patch)
tree4ef4d9c116a7fd423fcb62d531f97871d5f9d6e0 /lib
parentfix tagging (diff)
downloadalive-42d4109398fd5cf2416ef076af3c2ddea16a5070.tar.gz
alive-42d4109398fd5cf2416ef076af3c2ddea16a5070.zip
add launchcontrol midi
Diffstat (limited to 'lib')
-rw-r--r--lib/midi/core.moon90
-rw-r--r--lib/midi/init.moon (renamed from lib/midi.moon)59
-rw-r--r--lib/midi/launchctl.moon66
3 files changed, 158 insertions, 57 deletions
diff --git a/lib/midi/core.moon b/lib/midi/core.moon
new file mode 100644
index 0000000..ea49a2c
--- /dev/null
+++ b/lib/midi/core.moon
@@ -0,0 +1,90 @@
+import RtMidiIn, RtMidiOut, RtMidi from require 'luartmidi'
+import band, bor, lshift, rshift from require 'bit32'
+
+MIDI = {
+ [0x9]: 'note-on'
+ [0x8]: 'note-off'
+
+ [0xa]: 'after-key'
+ [0xd]: 'after-channel'
+
+ [0xb]: 'control-change'
+ [0xe]: 'pitch-bend'
+ [0xc]: 'program-change'
+}
+
+rMIDI = {v,k for k,v in pairs MIDI}
+
+class Input
+ new: (name) =>
+ @input = RtMidiIn RtMidi.Api.UNIX_JACK
+
+ id = nil
+ for port=1,@input\getportcount!
+ if name == @input\getportname port
+ id = port
+ break
+
+ @input\openport id
+
+ @listeners = {}
+
+ tick: =>
+ while true
+ delta, bytes = @input\getmessage!
+ break unless delta
+
+ { status, a, b } = bytes
+ chan = band status, 0xf
+ status = MIDI[rshift status, 4]
+ @dispatch status, chan, a, b
+
+ dispatch: (status, chan, a, b) =>
+ L\trace "dispatching MIDI event #{status} CH#{chan} #{a} #{b}"
+ for mask, handler in pairs @listeners
+ match = true
+ match and= status == mask.status if mask.status
+ match and= chan == mask.chan if mask.chan
+ match and= a == mask.a if mask.a
+ if match
+ handler status, chan, a, b
+
+ -- register a handler
+ -- mask is { :status, :chan, :a } (all keys optional)
+ attach: (mask, handler) =>
+ @listeners[mask] = handler
+
+ detach: (mask) =>
+ @listeners[mask] = nil
+
+class Output
+ new: (name) =>
+ @output = RtMidiOut RtMidi.Api.UNIX_JACK
+
+ id = nil
+ for port=1,@output\getportcount!
+ if name == @output\getportname port
+ id = port
+ break
+
+ @output\openport id
+
+ send: (status, chan, a, b) =>
+ status = bor (lshift rMIDI[status], 4), chan
+ @output\sendmessage status, a, b
+
+class InOut
+ new: (inp, out) =>
+ @inp = Input inp
+ @out = Output out
+
+ tick: (...) => @inp\tick ...
+ attach: (...) => @inp\attach ...
+ detach: (...) => @inp\detach ...
+ send: (...) => @out\send ...
+
+{
+ :Input
+ :Output
+ :InOut
+}
diff --git a/lib/midi.moon b/lib/midi/init.moon
index 5aebc0b..f522b6c 100644
--- a/lib/midi.moon
+++ b/lib/midi/init.moon
@@ -1,62 +1,7 @@
import Const, Op from require 'core'
-import RtMidiIn, RtMidiOut, RtMidi from require 'luartmidi'
-import band, rshift from require 'bit32'
+import Input from require 'lib.midi.core'
-MIDI = {
- [0x9]: 'note-on'
- [0x8]: 'note-off'
-
- [0xa]: 'after-key'
- [0xd]: 'after-channel'
-
- [0xb]: 'control-change'
- [0xe]: 'pitch-bend'
- [0xc]: 'program-change'
-}
-
-class Dispatcher
- new: (name) =>
- @input = RtMidiIn RtMidi.Api.UNIX_JACK
-
- id = nil
- for port=1,@input\getportcount!
- if name == @input\getportname port
- id = port
- break
-
- @input\openport id
-
- @listeners = {}
-
- tick: =>
- while true
- delta, bytes = @input\getmessage!
- break unless delta
-
- { status, a, b } = bytes
- chan = band status, 0xf
- status = MIDI[rshift status, 4]
- @dispatch status, chan, a, b
-
- dispatch: (status, chan, a, b) =>
- L\trace "dispatching MIDI event #{status} CH#{chan} #{a} #{b}"
- for mask, handler in pairs @listeners
- match = true
- match and= status == mask.status if mask.status
- match and= chan == mask.chan if mask.chan
- match and= a == mask.a if mask.a
- if match
- handler status, chan, a, b
-
- -- register a handler
- -- mask is { :status, :chan, :a } (all keys optional)
- attach: (mask, handler) =>
- @listeners[mask] = handler
-
- detach: (mask) =>
- @listeners[mask] = nil
-
-dispatch = Dispatcher 'system:midi_capture_2'
+dispatch = Input 'system:midi_capture_6'
class gate extends Op
@doc: "(midi/gate note [chan]) - gate from note-on and note-off messages"
diff --git a/lib/midi/launchctl.moon b/lib/midi/launchctl.moon
new file mode 100644
index 0000000..c7d7b06
--- /dev/null
+++ b/lib/midi/launchctl.moon
@@ -0,0 +1,66 @@
+import Const, Op from require 'core'
+import InOut from require 'lib.midi.core'
+import bor, lshift from require 'bit32'
+
+launch = InOut 'system:midi_capture_6', 'system:midi_playback_6'
+
+color = (r, g) -> bor 12, r, (lshift g, 4)
+
+class gate_seq extends Op
+ @doc: "(launctl/gate-seq i start chan [steps]) - Gate-Sequencer
+
+returns true or false for the i-th step steps (buttons starting from start)."
+
+ destroy: =>
+ launch\detach @mask if @mask
+
+ new: =>
+ @steps = {}
+ @value = false
+
+ setup: (@i, start, chan, steps=(Const.num 8)) =>
+ launch\detach @mask if @mask
+
+ @start = start\getc 'num'
+ @chan = chan\getc 'num'
+ steps = steps\getc 'num'
+
+ while steps > #@steps
+ table.insert @steps, false
+ while steps < #@steps
+ table.remove @steps
+
+ @mask = launch\attach { status: 'note-on', chan: @chan }, (_, _, note, _) -> @toggle note
+
+ toggle: (note) =>
+ i = note - @start
+ val = @steps[i+1]
+ if val != nil
+ @steps[i+1] = not val
+ curr_i = (@i\get 'num') % #@steps
+ launch\send 'note-on', @chan, note, light @steps[i+1], i == curr_i
+
+ 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, 0
+ when 'S ' then 0, 1
+ when 'SA' then 0, 3
+
+ update: (dt) =>
+ launch\tick!
+
+ @i\update dt
+ curr_i = (@i\get 'num') % #@steps
+ prev_i = (curr_i - 1) % #@steps
+
+ launch\send 'note-on', @chan, (@start + curr_i), light @steps[curr_i+1], true
+ launch\send 'note-on', @chan, (@start + prev_i), light @steps[prev_i+1], false
+
+ @value = @steps[curr_i+1]
+
+{
+ 'gate-seq': gate_seq
+}