aboutsummaryrefslogtreecommitdiffstats
path: root/lib/midi
diff options
context:
space:
mode:
Diffstat (limited to 'lib/midi')
-rw-r--r--lib/midi/core.moon17
-rw-r--r--lib/midi/init.moon44
-rw-r--r--lib/midi/launchctl.moon99
3 files changed, 114 insertions, 46 deletions
diff --git a/lib/midi/core.moon b/lib/midi/core.moon
index ea49a2c..891de0a 100644
--- a/lib/midi/core.moon
+++ b/lib/midi/core.moon
@@ -51,8 +51,10 @@ class Input
-- register a handler
-- mask is { :status, :chan, :a } (all keys optional)
+ -- returns mask for op convenience
attach: (mask, handler) =>
@listeners[mask] = handler
+ mask
detach: (mask) =>
@listeners[mask] = nil
@@ -83,8 +85,23 @@ class InOut
detach: (...) => @inp\detach ...
send: (...) => @out\send ...
+apply_range = (range, val) ->
+ if range.type == 'str'
+ switch range\unwrap!
+ when 'raw' then val
+ when 'uni' then val / 128
+ when 'bip' then val / 64 - 1
+ when 'rad' then val / 64 * math.pi
+ else
+ error "unknown range #{@range}"
+ elseif range.type == 'num'
+ val / 128 * range\unwrap!
+ else
+ error "range has to be a string or number"
+
{
:Input
:Output
:InOut
+ :apply_range
}
diff --git a/lib/midi/init.moon b/lib/midi/init.moon
index f522b6c..c9e2476 100644
--- a/lib/midi/init.moon
+++ b/lib/midi/init.moon
@@ -1,29 +1,29 @@
-import Const, Op from require 'core'
-import Input from require 'lib.midi.core'
+import Stream, Const, Op from require 'core'
+import Input, apply_range from require 'lib.midi.core'
-dispatch = Input 'system:midi_capture_6'
+dispatch = Input 'system:midi_capture_4'
class gate extends Op
@doc: "(midi/gate note [chan]) - gate from note-on and note-off messages"
- new: (...) =>
- super ...
-
destroy: =>
dispatch\detach @mask if @mask
setup: (note, chan) =>
dispatch\detach @mask if @mask
- note = note\getc 'num'
- chan = chan and chan\getc 'num'
+ note = note\const!\unwrap 'num'
+ chan = chan and chan\const!\unwrap 'num'
@value = false
@mask = dispatch\attach { :chan, a: note }, (status) ->
if status == 'note-on'
- @value = true
+ @out\set true
else if status == 'note-off'
- @value = false
+ @out\set false
+
+ @out = Stream 'bool', false
+ @out
update: (dt) => dispatch\tick!
@@ -43,26 +43,16 @@ range can be one of:
setup: (cc, chan, @range=Const.str'uni') =>
dispatch\detach @mask if @mask
- cc = cc\getc 'num'
- chan = chan and chan\getc 'num'
+ cc = cc\const!\unwrap 'num'
+ chan = chan and chan\const!\unwrap 'num'
- @mask = dispatch\attach { status: 'control-change', :chan, a: cc }, (_, _, _, val) -> @apply val
+ @mask = dispatch\attach { status: 'control-change', :chan, a: cc }, (_, _, _, val) ->
+ @out\set apply_range @range, val
- update: (dt) => dispatch\tick!
+ @out = Stream 'num', 0
+ @out
- apply: (val) =>
- @value = if @range.type == 'str'
- switch @range\get!
- when 'raw' then val
- when 'uni' then val / 128
- when 'bip' then val / 64 - 1
- when 'rad' then val / 64 * math.pi
- else
- error "unknown range #{@range}"
- elseif @range.type == 'num'
- val / 128 * @range\get!
- else
- error "range has to be a string or number"
+ update: (dt) => dispatch\tick!
{
:gate
diff --git a/lib/midi/launchctl.moon b/lib/midi/launchctl.moon
index c7d7b06..778a724 100644
--- a/lib/midi/launchctl.moon
+++ b/lib/midi/launchctl.moon
@@ -1,66 +1,127 @@
-import Const, Op from require 'core'
-import InOut from require 'lib.midi.core'
+import Stream, Const, Op from require 'core'
+import InOut, apply_range from require 'lib.midi.core'
import bor, lshift from require 'bit32'
-launch = InOut 'system:midi_capture_6', 'system:midi_playback_6'
+launch = InOut 'system:midi_capture_4', 'system:midi_playback_4'
color = (r, g) -> bor 12, r, (lshift g, 4)
+class cc_seq extends Op
+ @doc: "(launctl/cc-seq i start chan [steps [range]]) - CC-Sequencer
+
+returns the value for the i-th step steps (buttons starting from start).
+steps defaults to 8.
+
+range can be one of:
+- 'raw' [ 0 - 128[
+- 'uni' [ 0 - 1[ (default)
+- 'bip' [-1 - 1[
+- 'rad' [ 0 - tau[
+- (num) [ 0 - num["
+
+ destroy: =>
+ launch\detach @mask if @mask
+
+ new: =>
+ @steps = {}
+ @out = Stream 'num'
+
+ setup: (@i, start, chan, steps=(Const.num 8), @range=Const.str 'uni') =>
+ launch\detach @mask if @mask
+
+ @start = start\const!\unwrap 'num'
+ @chan = chan\const!\unwrap 'num'
+ steps = steps\const!\unwrap 'num'
+
+ while steps > #@steps
+ table.insert @steps, 0
+ while steps < #@steps
+ table.remove @steps
+
+ @mask = launch\attach { status: 'control-change', chan: @chan }, (_, _, cc, val) -> @change cc, val
+ @out
+
+ change: (cc, val) =>
+ i = cc - @start
+ if i < #@steps
+ @steps[i+1] = val
+
+ update: (dt) =>
+ launch\tick!
+
+ curr_i = (@i\unwrap 'num') % #@steps
+
+ @out\set apply_range @range, @steps[curr_i+1]
+
+
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)."
+returns true or false for the i-th step steps (buttons starting from start).
+steps defaults to 8."
destroy: =>
launch\detach @mask if @mask
new: =>
@steps = {}
- @value = false
+ @out = Stream 'bool'
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'
+ for i=1, #@steps
+ launch\send 'note-on', @chan, (@start+i), 0, color 0, 0
+
+ @start = start\const!\unwrap 'num'
+ @chan = chan\const!\unwrap 'num'
+ steps = steps\const!\unwrap '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
+ for i=1, steps
+ @display i
+
+ @mask = launch\attach { status: 'note-on', chan: @chan }, (_, _, note, _) ->
+ @toggle note
+
+ @out
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
+ curr_i = (@i\unwrap 'num') % #@steps
+ @display i, i == curr_i
+
+ display: (i, active) =>
+ launch\send 'note-on', @chan, (@start + i), light @steps[i+1], active
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
+ when ' A' then 1, 1
+ when 'S ' then 1, 0
+ when 'SA' then 3, 1
update: (dt) =>
launch\tick!
- @i\update dt
- curr_i = (@i\get 'num') % #@steps
+ curr_i = (@i\unwrap '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
+ @display curr_i, true
+ @display prev_i, false
- @value = @steps[curr_i+1]
+ @out\set @steps[curr_i+1]
{
'gate-seq': gate_seq
+ 'cc-seq': cc_seq
}