Code: Select all
--[[
Bass Coupler
Version 1.0 coded 11/15/2013
Especially for MachFive 3
---------------------------------
Coded by nnebeel in 12 hours of straight coding
Buy me a meal!
https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=S3Q3MWXAD7RSG
Apply this script to a part to cause only the lowest note to play. A Split Point knob is available. Notes above the split point will not play. This script is especially useful as a bass coupler during live organ performance with a single MIDI keyboard:
Part 1 (without a script)
Organ voice, which is generally centered on 8' (native) pitch
Part 2 (same input channel as part 1, but with Bass Coupler script)
Pedal voice, which is generally centered on 16' (8vb) pitch
As you perform, all notes will be played in part 1, but only the bottom note will be played in part 2.
Note: to better accommodate pianists' nonlegato touch, some organ bass couplers do not support legato chord changes. This script allows legato chord changes, which might take some getting used to for pianists (but is worth it in the long run).
]]
print("Buy me a meal! https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=S3Q3MWXAD7RSG")
local limit = Knob("Split Point", 127, 0, 127, true)
local heldNotes = {}
function findLow(array)
if #array == 0 then
return nil, nil, nil
else
local tempNote = 128
local tempLoc = 128
for i, v in ipairs(array) do
if v.note < tempNote then
tempNote = v.note
tempVel = v.velocity
tempLoc = i
end
end
return tempLoc, tempNote, tempVel
end
end
function findNote(array, note)
if #array == 0 then
print("Error: array does not exist. Cannot find note " .. note)
return false
else
for i, v in ipairs(array) do
if v.note == note then
return i
end
end
print("Error: note " .. note .. " not found in array")
return false
end
end
function limitTest(a, vel)
if a <= limit.value then
return vel
else
return 0
end
end
function onEvent(e)
-- NOTE OFF
if e.type == Event.NoteOff then
local lowLoc, lowNote, lowVel = findLow(heldNotes)
-- delete the note from the table
table.remove(heldNotes, findNote(heldNotes, e.note))
if e.note == lowNote then
if #heldNotes > 0 then
--find and release new low note
lowLoc, lowNote, lowVel = findLow(heldNotes)
heldNotes[lowLoc].type = Event.NoteOff
postEvent(heldNotes[lowLoc])
--play new low note
local tempVel = heldNotes[lowLoc].velocity
heldNotes[lowLoc].velocity = limitTest(e.note, e.velocity)
heldNotes[lowLoc].type = Event.NoteOn
postEvent(heldNotes[lowLoc])
heldNotes[lowLoc].velocity = tempVel
end
end
--post event
postEvent(e)
-- NOTE ON
elseif e.type == Event.NoteOn then
local lowLoc, lowNote, lowVel = findLow(heldNotes)
--add the new note to the table
table.insert(heldNotes, e)
local tempVel = e.velocity
if lowLoc == nil then
--play the new note
e.velocity = limitTest(e.note, e.velocity)
elseif e.note < lowNote then
--release the old low note
heldNotes[lowLoc].type = Event.NoteOff
postEvent(heldNotes[lowLoc])
--play the new note
e.velocity = limitTest(e.note, e.velocity)
else
--insert note into table
e.velocity = 0
end
--post event
postEvent(e)
e.velocity = tempVel
end
end