diff options
| author | s-ol <s+removethis@s-ol.nu> | 2022-02-05 23:19:18 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2025-03-02 14:24:49 +0000 |
| commit | 971ccdb31b5a49e50ad5ee8ea452348a4c9f357e (patch) | |
| tree | fd98a339aacc12e190ac7008f4fa82f8f2cc692e /alv-lib | |
| parent | builtins: small bugfixes (diff) | |
| download | alive-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.moon | 148 | ||||
| -rw-r--r-- | alv-lib/midi.moon | 39 |
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 |
