MIDI PC/CC script: handle Bank Select messages

This commit is contained in:
Brent Baccala 2025-07-07 16:53:45 -04:00
parent 970d3f0f56
commit 01d221170f

View file

@ -13,9 +13,11 @@ Every track with "MIDI Program #" in its comments in activated when that PC is s
The program number can be comma-separated ranges, as in "MIDI Program 0-7,9" The program number can be comma-separated ranges, as in "MIDI Program 0-7,9"
Tracks can also be labeled "MIDI Bank # Program #", in which case both bank and program numbers have to match.
Each such track can have MIDI CC lines with arbitrary Lua code. Each such track can have MIDI CC lines with arbitrary Lua code.
Variables route (the current route) and value (the CC value) are defined. In such code, variables route (the current route) and value (the CC value) are defined.
The following are working MIDI CC lines. The following are working MIDI CC lines.
@ -39,7 +41,7 @@ MIDI CC 91: activate_processor_by_name(route, value, "Gigaverb")
A convenience function to select a preset: A convenience function to select a preset:
MIDI CC 93: load_preset(route, "plugin", "preset") MIDI CC 93: load_preset(route, "plugin", "preset")
Remember that a "MIDI Program #" line is required for MIDI CC lines to be processed Remember that a "MIDI Program #" (or "MIDI Bank # Program #") line is required for MIDI CC lines to be processed
]] ]]
} }
@ -113,35 +115,37 @@ end
-- it's a list of pairs, indexed by CC number, each pair a Route object and a Lua function -- it's a list of pairs, indexed by CC number, each pair a Route object and a Lua function
CC_functions = { } CC_functions = { }
function process_midi_messages() -- global variables to track current bank and program
for _,b in pairs (midiin) do bank_msb = 0
local t = b["time"] -- t = [ 1 .. n_samples ] bank_lsb = 0
local d = b["data"] -- midi-event data bank = 0
local event_type program = 0
local channel
if #d == 0 then -- return true/false if number (an integer) is in range (a string)
event_type = -1 -- format of range is a comma-separated list of items, each either START-STOP or VALUE
channel = -1 function match_number_range(number, range)
else local fieldstart = 1
event_type = d[1] >> 4 local match = false
channel = d[1] & 15 range = range .. ','
repeat
local r,_,start,stop = string.find(range, "^(%d+)-(%d+)", fieldstart)
if r and number >= tonumber(start) and number <= tonumber(stop) then
match = true
end end
if (event_type == 11) then r,_,value = string.find(range, "^(%d+)", fieldstart)
-- Continuous Controller message if r and number == tonumber(value) then
-- if any functions are registered for this CC, call them with their respective Route objects and the value of the controller match = true
local lst = CC_functions[d[2]]
if lst then
for _, tbl in ipairs(lst) do
local route = tbl[1]
local func = tbl[2]
func(route, d[3])
end end
end local nexti = string.find(range, ",", fieldstart)
end fieldstart = nexti + 1
if (event_type == 12) then until fieldstart > string.len(range)
-- Program Change message return match
end
function program_change()
-- Program Change or Bank Change message
-- program and bank are global variables
-- Parse/reparse the comment blocks -- Parse/reparse the comment blocks
local patch = d[2];
-- Clear the global table of CC functions; it'll be recreated during the parse -- Clear the global table of CC functions; it'll be recreated during the parse
CC_functions = { } CC_functions = { }
-- Run through all comment blocks on all routes looking for certain strings -- Run through all comment blocks on all routes looking for certain strings
@ -151,23 +155,33 @@ function process_midi_messages()
local route_comment = route:comment() local route_comment = route:comment()
local route_active = false local route_active = false
local local_CC_functions = { } local local_CC_functions = { }
-- Look for "MIDI Program" statements that match the patch number in the Program Change message -- Look for "MIDI Bank # Program #" statements that match both the program number and the bank number
local MIDI_Program_start,MIDI_Program_end,program_list = string.find(route_comment, "MIDI Program (%d[-%d,]*)", nextchar) while true do
if MIDI_Program_start then local MIDI_Program_start, MIDI_Program_end, bank_list, program_list
local fieldstart = 1 MIDI_Program_start,MIDI_Program_end,bank_list,program_list = string.find(route_comment, "MIDI Bank (%d[-%d,]*) Program (%d[-%d,]*)", nextchar)
program_list = program_list .. ',' if bank_list then
repeat route_active = route_active or (match_number_range(program, program_list) and match_number_range(bank, bank_list))
local r,_,start,stop = string.find(program_list, "^(%d+)-(%d+)", fieldstart) MIDI_Program_seen = true
if r and patch >= tonumber(start) and patch <= tonumber(stop) then nextchar = MIDI_Program_end + 1
route_active = true else
break
end end
r,_,program = string.find(program_list, "^(%d+)", fieldstart)
if r and patch == tonumber(program) then
route_active = true
end end
local nexti = string.find(program_list, ",", fieldstart) -- Look for "MIDI Program #" statements that match just the program number
fieldstart = nexti + 1 nextchar = 1
until fieldstart > string.len(program_list) while true do
local MIDI_Program_start, MIDI_Program_end, program_list
MIDI_Program_start,MIDI_Program_end,program_list = string.find(route_comment, "MIDI Program (%d[-%d,]*)", nextchar)
if program_list then
route_active = route_active or match_number_range(program, program_list)
MIDI_Program_seen = true
nextchar = MIDI_Program_end + 1
else
break
end
end
-- if at least one of either kind of line was seen, there is further handling of this track
if MIDI_Program_seen then
-- all tracks with a "MIDI Program" line present are set either active or inactive, depending on if they matched -- all tracks with a "MIDI Program" line present are set either active or inactive, depending on if they matched
-- all other tracks (no "MIDI Progam" line) are not affected (they never get to this point) -- all other tracks (no "MIDI Progam" line) are not affected (they never get to this point)
route:set_active(route_active, nil); route:set_active(route_active, nil);
@ -208,6 +222,49 @@ function process_midi_messages()
end end
end end
end end
end
function process_midi_messages()
for _,b in pairs (midiin) do
local t = b["time"] -- t = [ 1 .. n_samples ]
local d = b["data"] -- midi-event data
local event_type
local channel
if #d == 0 then
event_type = -1
channel = -1
else
event_type = d[1] >> 4
channel = d[1] & 15
end
if (event_type == 11) then
-- Continuous Controller message
local num = d[2]
local val = d[3]
-- handle Bank Select messages
if num == 0 or num == 32 then
if num == 0 then
bank_msb = val
else
bank_lsb = val
end
bank = 128 * bank_msb + bank_lsb
program_change()
end
-- if any functions are registered for this CC, call them with their respective Route objects and the value of the controller
local lst = CC_functions[num]
if lst then
for _, tbl in ipairs(lst) do
local route = tbl[1]
local func = tbl[2]
func(route, val)
end
end
end
if (event_type == 12) then
-- Program Change message
program = d[2]
program_change()
end end
end end
end end