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 | |
| 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
| -rw-r--r-- | README.md | 3 | ||||
| -rw-r--r-- | alv-lib/_midi.moon | 148 | ||||
| -rw-r--r-- | alv-lib/midi.moon | 39 | ||||
| -rw-r--r-- | flake.nix | 45 |
4 files changed, 172 insertions, 63 deletions
@@ -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 @@ -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"; |
