aboutsummaryrefslogtreecommitdiffstats
path: root/alv-lib
diff options
context:
space:
mode:
authors-ol <s+removethis@s-ol.nu>2022-02-05 23:19:18 +0000
committers-ol <s+removethis@s-ol.nu>2025-03-02 14:24:49 +0000
commit971ccdb31b5a49e50ad5ee8ea452348a4c9f357e (patch)
treefd98a339aacc12e190ac7008f4fa82f8f2cc692e /alv-lib
parentbuiltins: small bugfixes (diff)
downloadalive-971ccdb31b5a49e50ad5ee8ea452348a4c9f357e.tar.gz
alive-971ccdb31b5a49e50ad5ee8ea452348a4c9f357e.zip
lib: update midi
Updated luartmidi module with support for virtual ports and JACK
Diffstat (limited to 'alv-lib')
-rw-r--r--alv-lib/_midi.moon148
-rw-r--r--alv-lib/midi.moon39
2 files changed, 146 insertions, 41 deletions
diff --git a/alv-lib/_midi.moon b/alv-lib/_midi.moon
index eb0b742..8e8b507 100644
--- a/alv-lib/_midi.moon
+++ b/alv-lib/_midi.moon
@@ -1,4 +1,4 @@
-import Constant, T, Struct, Op, Input, T, Error, const from require 'alv.base'
+import Constant, T, Array, Struct, Op, Input, T, Error, const from require 'alv.base'
import RtMidiIn, RtMidiOut, RtMidi from require 'luartmidi'
bit = if _VERSION == 'Lua 5.4'
@@ -27,19 +27,35 @@ MIDI = {
rMIDI = {v,k for k,v in pairs MIDI}
-find_port = (Klass, name) ->
- with Klass RtMidi.Api.UNIX_JACK
- id = nil
- for port=1, \getportcount!
- if name == \getportname port
- id = port
- break
+get_port_names = (port) ->
+ [port\getportname i for i=1, port\getportcount!]
- \openport id
+find_port = (Klass, label, connect) ->
+ port = Klass "alv", RtMidi.Api.UNIX_JACK
+ names = get_port_names port
+
+ if connect == ''
+ port\openvirtualport label
+ return port
+
+ -- first exact matches
+ for i, name in ipairs names
+ if name == connect
+ port\openport i, label
+ return port, name
+
+ -- then pattern matches
+ for i, name in ipairs names
+ if name\match connect
+ port\openport i
+ return port, name
+
+ port\openvirtualport label
+ port
class InPort
- new: (@name) =>
- @port = find_port RtMidiIn, @name
+ new: (@name, connect) =>
+ @port, @connect = find_port RtMidiIn, @name, connect
@msgs = {}
poll: =>
@@ -51,32 +67,30 @@ class InPort
status = MIDI[rshift status, 4]
{ :status, :chan, :a, :b }
- __tostring: => "[#{@name}]"
- __tojson: => string.format '%q', tostring @
+ __tostring: => if @connect then "#{@name}@#{@connect}" else @name
class OutPort
- new: (@name) =>
- @port = find_port RtMidiOut, @name
+ new: (@name, connect) =>
+ @port, @connect = find_port RtMidiOut, @name, connect
send: (status, chan, a, b) =>
if 'string' == type 'status'
status = bor (lshift rMIDI[status], 4), chan
@port\sendmessage status, a, b
- __tostring: => "[#{@name}]"
- __tojson: => string.format '%q', tostring @
+ __tostring: => if @connect then "#{@name}@#{@connect}" else @name
class PortOp extends Op
setup: (inputs) =>
super inputs
- { :inp, :out } = @inputs
+ { :name, :inp, :out } = @inputs
- type = if inp and out
+ type = if inp != nil and out != nil
Struct in: T['midi/in'], out: T['midi/out']
- elseif inp
+ elseif inp != nil
T['midi/in']
- elseif out
+ elseif out != nil
T['midi/out']
else
error "no port opened"
@@ -85,28 +99,64 @@ class PortOp extends Op
@setup_out '~', type
tick: =>
- if @inputs.inp and @inputs.inp\dirty!
- @state.inp = InPort @inputs.inp!
+ { :name, :inp, :out } = @unwrap_all!
+
+ if inp and @inputs.inp\dirty!
+ @state.inp = InPort name, inp
- if @inputs.out and @inputs.out\dirty!
- @state.out = OutPort @inputs.out!
+ if out and @inputs.out\dirty!
+ @state.out = OutPort name, out
- { :inp, :out } = @state
- @out\set if inp and out
- { 'in': inp, :out }
+ @out\set if @state.inp and @state.out
+ { 'in': @state.inp, out: @state.out }
else
- inp or out
+ @state.inp or @state.out
+
+port_names = Constant.meta
+ meta:
+ name: 'port-names'
+ summary: "Get all MIDI port names."
+ examples: { '(midi/port-names direction)' }
+ description: '
+`direction` can be either `"in"` or `"out".
+Returns an array of strings.'
+
+ value: class extends Op
+ setup: (inputs) =>
+ dir = const.str\match inputs
+ super {}
+
+ dir = dir.result!
+ assert dir == "in" or dir == "out", Error 'argument', "'dir' has to be either 'in' or 'out'."
+
+ Klass = if dir == "in" then RtMidiIn else RtMidiOut
+ port = Klass "alv", RtMidi.Api.UNIX_JACK
+ names = get_port_names port
+
+ Type = Array #names, T.str
+
+ @setup_out '~', Type, names
input = Constant.meta
meta:
name: 'input'
summary: "Create a MIDI input port."
- examples: { '(midi/input name)' }
+ examples: { '(midi/input name [port])' }
+ desciprtion: "
+Create a MIDI input port called `name` and optionally connect
+it to an existing output `port`.
+`name` and `port` are both str= results.
+Use [midi/port-names][] to find valid values for `port`.
+
+`port` can either be the exact name of an existing port,
+or a [Lua pattern](https://www.lua.org/pil/20.2.html)."
value: class extends PortOp
setup: (inputs) =>
- name = const.str\match inputs
- super inp: Input.hot name
+ { name, connect } = (const.str * 2)\match inputs
+ super
+ name: Input.hot name
+ inp: Input.hot connect or Constant.str ''
poll: =>
@.out!\poll!
@@ -116,25 +166,44 @@ output = Constant.meta
meta:
name: 'output'
summary: "Create a MIDI output port."
- examples: { '(midi/output name)' }
+ examples: { '(midi/output name [port])' }
+ desciprtion: "
+Create a MIDI output port called `name` and optionally connect
+it to an existing input `port`.
+`name` and `port` are both str= results.
+Use [midi/port-names][] to find valid values for `port`.
+
+`port` can either be the exact name of an existing port,
+or a [Lua pattern](https://www.lua.org/pil/20.2.html)."
value: class extends PortOp
setup: (inputs) =>
- name = const.str\match inputs
- super out: Input.hot name
+ { name, connect } = (const.str * 2)\match inputs
+ super
+ name: Input.hot name
+ out: Input.hot connect or Constant.str ''
port = Constant.meta
meta:
name: 'port'
summary: "Create a bidirectional MIDI port."
- examples: { '(midi/port name)' }
+ examples: { '(midi/port name [in] [out])' }
+ desciprtion: "
+Create a bidirectional MIDI port called `name` and optionally connect
+it to the existing output port `in` and input port `out`.
+
+`name`, `in` and `out` are all str= results.
+Use [midi/port-names][] to find valid values for `in` and `out`.
+`in` and `out` can either be the exact name of an existing port,
+or a [Lua pattern](https://www.lua.org/pil/20.2.html)."
value: class extends PortOp
setup: (inputs) =>
- { inp, out } = (const.str + const.str)\match inputs
+ { name, inp, out } = (const.str * 3)\match inputs
super
- inp: Input.hot inp
- out: Input.hot out
+ name: Input.hot name
+ inp: Input.hot inp or Constant.str ''
+ out: Input.hot out or Constant.str ''
poll: =>
@.out!.in\poll!
@@ -159,6 +228,7 @@ apply_range = (range, val) ->
:input
:output
:port
+ :port_names
:apply_range
:bit
}
diff --git a/alv-lib/midi.moon b/alv-lib/midi.moon
index 2c02688..5bd51b7 100644
--- a/alv-lib/midi.moon
+++ b/alv-lib/midi.moon
@@ -1,5 +1,5 @@
import Constant, Op, Input, T, Struct, sig, evt from require 'alv.base'
-import input, output, port, apply_range from require 'alv-lib._midi'
+import input, output, port, port_names, apply_range from require 'alv-lib._midi'
import monotime from require 'system'
gate = Constant.meta
@@ -52,7 +52,7 @@ trig = Constant.meta
examples: { '(midi/trig [port] note [chan])' }
value: class extends Op
- pattern = -sig['midi/in'] + sig.num -sig.num
+ pattern = -sig['midi/in'] + sig.num + -sig.num
setup: (inputs, scope) =>
{ port, note, chan } = pattern\match inputs
@out = T.bang\mk_evt!
@@ -79,6 +79,39 @@ trig = Constant.meta
tick: =>
@out\set @inputs.internal!
+trigs = Constant.meta
+ meta:
+ name: 'trigs'
+ summary: "`bang`s from note-on messages."
+ examples: { '(midi/trigs [port] [chan])' }
+
+ value: class extends Op
+ pattern = -sig['midi/in'] + -sig.num
+ setup: (inputs, scope) =>
+ { port, chan } = pattern\match inputs
+ @out = T.num\mk_evt!
+ super
+ port: Input.cold port or scope\get '*midi*'
+ chan: Input.cold chan or Constant.num -1
+
+ internal: Input.hot T.num\mk_evt!
+
+ poll: =>
+ { :port, :chan, :internal } = @inputs
+
+ msgs = port!.msgs
+ for i = #msgs, 1, -1
+ msg = msgs[i]
+ if (chan! == -1 or msg.chan == chan!)
+ if msg.status == 'note-on'
+ internal.result\set msg.a
+ return true
+
+ false
+
+ tick: =>
+ @out\set @inputs.internal!
+
cc = Constant.meta
meta:
name: 'cc'
@@ -200,8 +233,10 @@ Constant.meta
:input
:output
:port
+ 'port-names': port_names
:gate
:trig
+ :trigs
:cc
'send-notes': send_notes