aboutsummaryrefslogtreecommitdiffstats
path: root/lib/midi/launchctl.moon
blob: 37ccb8223d2eb3f7865d9779dd656502e2e07ebc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import Value, Op, Input, match 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
  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.

range can be one of:
- 'raw' [ 0 - 128[
- 'uni' [ 0 - 1[ (default)
- 'bip' [-1 - 1[
- 'rad' [ 0 - tau[
- 'deg' [ 0 - 360[
- (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

      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
  'cc-seq': cc_seq
}