aboutsummaryrefslogtreecommitdiffstats
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
parentbuiltins: small bugfixes (diff)
downloadalive-971ccdb31b5a49e50ad5ee8ea452348a4c9f357e.tar.gz
alive-971ccdb31b5a49e50ad5ee8ea452348a4c9f357e.zip
lib: update midi
Updated luartmidi module with support for virtual ports and JACK
-rw-r--r--README.md3
-rw-r--r--alv-lib/_midi.moon148
-rw-r--r--alv-lib/midi.moon39
-rw-r--r--flake.nix45
4 files changed, 172 insertions, 63 deletions
diff --git a/README.md b/README.md
index e92a690..0b69e5f 100644
--- a/README.md
+++ b/README.md
@@ -17,8 +17,7 @@ processes. For more information, visit the full [online documentation][docs].
- [socket][socket]: `luarocks install luasocket`
- [system][system]: `luarocks install luasystem`
- [losc][losc]: `luarocks install losc` (optional)
-- [lua-rtmidi][rtmidi]: `luarocks install
- https://raw.githubusercontent.com/s-ol/lua-rtmidi/master/lua-rtmidi-dev-1.rockspec` (optional)
+- [lua-rtmidi][rtmidi]: `luarocks install lua-rtmidi` (optional)
- [busted][busted]: `luarocks install busted` (optional, for tests)
- [discount][discount]: `luarocks install discount` (optional, for docs)
- [ldoc][ldoc]: `luarocks install
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
diff --git a/flake.nix b/flake.nix
index 8d998cc..c2a3de8 100644
--- a/flake.nix
+++ b/flake.nix
@@ -17,15 +17,15 @@
version = "0.2.0-1";
knownRockspec = (pkgs.fetchurl {
- url = "mirror://luarocks//${pname}-${version}.rockspec";
- sha256 = "AzDZV9u6V71YNJFBfj3cR1COjFFWhGmsJkGsUMErSZs=";
+ url = "mirror://luarocks/${pname}-${version}.rockspec";
+ hash = "sha256-AzDZV9u6V71YNJFBfj3cR1COjFFWhGmsJkGsUMErSZs=";
}).outPath;
src = pkgs.fetchFromGitHub {
owner = "osch";
repo = pname;
rev = "v0.2.0";
- sha256 = "PamppWdV3cQMDK+t2V09/cNRskGuRNeuyvUODmopLaQ=";
+ hash = "sha256-PamppWdV3cQMDK+t2V09/cNRskGuRNeuyvUODmopLaQ=";
};
propagatedBuildInputs = [ lua ];
@@ -41,8 +41,8 @@
version = "0.2-2";
src = pkgs.fetchurl {
- url = "mirror://luarocks//${pname}-${version}.src.rock";
- sha256 = "Dp3bKIG4swrD4+1NNtRTgyj68Di2cSUlh1r7Z2Rkzn0=";
+ url = "mirror://luarocks/${pname}-${version}.src.rock";
+ hash = "sha256-Dp3bKIG4swrD4+1NNtRTgyj68Di2cSUlh1r7Z2Rkzn0=";
};
postUnpack = "sourceRoot=$sourceRoot/luarocks-fetch-gitrec-0.2";
@@ -60,8 +60,8 @@
version = "0.2-1";
src = pkgs.fetchurl {
- url = "mirror://luarocks//${pname}-${version}.src.rock";
- sha256 = "fD31FruqVriMecFcvSV4W7JRia38+bg7j3T5k5pFZec=";
+ url = "mirror://luarocks/${pname}-${version}.src.rock";
+ hash = "sha256-fD31FruqVriMecFcvSV4W7JRia38+bg7j3T5k5pFZec=";
};
postUnpack = "sourceRoot=$sourceRoot/lua-fltk4lua";
buildInputs = with pkgs; [ fltk libjpeg ];
@@ -79,8 +79,8 @@
version = "1.0.0-1";
src = pkgs.fetchurl {
- url = "mirror://luarocks//${pname}-${version}.src.rock";
- sha256 = "MArhj51V1awF5k2zToFYEXpS2c6o8bnNDn4wLhooHos=";
+ url = "mirror://luarocks/${pname}-${version}.src.rock";
+ hash = "sha256-MArhj51V1awF5k2zToFYEXpS2c6o8bnNDn4wLhooHos=";
};
postUnpack = "sourceRoot=$sourceRoot/losc";
@@ -99,13 +99,13 @@
version = "0.4-1";
knownRockspec = (pkgs.fetchurl {
- url = https://luarocks.org/discount-0.4-1.rockspec;
- sha256 = "0mc2mwkprf8li2v91vga77rwi0xhv989nxshi66z2d45lbl1dcpd";
+ url = mirror://luarocks/discount-0.4-1.rockspec;
+ hash = "sha256-7bIW6KKFNPGNiVB3m1DasIPI8znq7ZC2iBS5fCevglU=";
}).outPath;
src = pkgs.fetchurl {
- url = https://craigbarnes.gitlab.io/dist/lua-discount/lua-discount-0.4.tar.gz;
- sha256 = "1bfyrxjr26gbahawdynlbp48ma01gyd3b6xbljvxb2aavvfywc9m";
+ url = https://craigbarnes.gitlab.io/dist/lua-discount/lua-discount-0.4.tar.gz;
+ hash = "sha256-NTHu3d5KidW3pKubNZp/AaiKyF3U+sYVVOsZkWXP3q0=";
};
buildInputs = [ pkgs.discount ];
@@ -123,15 +123,15 @@
version = "scm-2";
knownRockspec = (pkgs.fetchurl {
- url = "mirror://luarocks//${pname}-${version}.rockspec";
- sha256 = "PHQhpQPfmlPhwIXoce5WZ+eoARmSecy1ac7Bfu4zg38=";
+ url = "mirror://luarocks/${pname}-${version}.rockspec";
+ hash = "sha256-PHQhpQPfmlPhwIXoce5WZ+eoARmSecy1ac7Bfu4zg38=";
}).outPath;
src = pkgs.fetchFromGitHub {
owner = "s-ol";
repo = "LDoc";
rev = "moonscript";
- sha256 = "3jieGp9++cWtLMKccP+xqrtdCiNG/9BYZlHmH1l8XV8=";
+ hash = "sha256-3jieGp9++cWtLMKccP+xqrtdCiNG/9BYZlHmH1l8XV8=";
};
propagatedBuildInputs = with luaPkgs; [
lua penlight markdown
@@ -146,16 +146,21 @@
lua-rtmidi = luaPkgs.buildLuarocksPackage rec {
pname = "lua-rtmidi";
- version = "dev-1";
+ version = "1.0.0-1";
+ # src = pkgs.fetchurl {
+ # url = "mirror://luarocks/${pname}-${version}.src.rock";
+ # hash = "sha256-DmSfrQRX8oziH+vvwq3KIdvjTX7P4zeKc6NeTygoU3A=";
+ # };
src = pkgs.fetchFromGitHub {
owner = "s-ol";
repo = "lua-rtmidi";
- rev = "master";
- sha256 = "iXckraQZf6smWlxD27ktBEFKNXLzzsZFpzx2MLRQJVM=";
+ rev = "v1.0.0";
+ hash = "sha256-DmSfrQRX8oziH+vvwq3KIdvjTX7P4zeKc6NeTygoU3A=";
};
+
buildInputs = with pkgs; [ stdenv.cc.cc.lib ];
- propagatedBuildInputs = with pkgs; [ lua alsa-lib pipewire.jack ];
+ propagatedBuildInputs = with pkgs; [ lua alsa-lib libjack2 ];
meta = {
homepage = "https://github.com/s-ol/lua-rtmidi";