Here's my take on it - I'm using note length and note start positions to determine if notes belong to a chord.
Rules are simple:
1. Notes have to overlap in length. I use relatively big 55% for allowable overlap range because chords take all shapes and sizes
2. Notes have to overlap in start position. Because they can have different lengths we take shorter note length as determining factor for allowable start position overlap. Again, I'm taking big 33% here cause that's far as I've seen chords go, lol.
Here's the script for your testing pleasure. If you got any ideas or you stumble upon MIDI material that isn't working with this script write here, let's make this real.
Code:
function msg(s)
reaper.ShowConsoleMsg(tostring(s) .. "\n")
end
-- Play with this to catch non-quantized notes -------------------------------------------------------------------------
local g_minLengthOverlap = 0.45
local g_minStartPosOverlap = 0.33
-- Main ----------------------------------------------------------------------------------------------------------------
function Main ()
local doUndo = false
local undoMsg = "Select all chord notes for selected notes"
local midiEditor = reaper.MIDIEditor_GetActive()
if midiEditor ~= nil then
local take = reaper.MIDIEditor_GetTake(midiEditor)
if take ~= nil and reaper.MIDI_EnumSelNotes(take, -1) ~= -1 then
-- Get selected notes
local selNotesData = {}
local idx = -1
while true do
idx = reaper.MIDI_EnumSelNotes(take, idx)
if idx == -1 then
break
end
local _, _, _, startPos, endPos = reaper.MIDI_GetNote(take, idx)
selNotesData[#selNotesData + 1] = {}
selNotesData[#selNotesData].id = idx
selNotesData[#selNotesData].startPos = startPos
selNotesData[#selNotesData].endPos = endPos
end
-- Search all notes and compare with selected notes for overlaps
local noteCount = ({reaper.MIDI_CountEvts(take, 0, 0, 0)})[2]
for i = 0, noteCount-1 do
local _, selected, muted, startPos, endPos, chan, pitch, vel = reaper.MIDI_GetNote(take, i)
local noteLength = endPos - startPos
-- Check for overlaps
local doSelect = false
if selected == false then
for _, selNote in ipairs(selNotesData) do
selNoteLength = selNote.endPos - selNote.startPos
local lengthOverlap = 0
local shorterLength = 0
if selNoteLength > noteLength then
lengthOverlap = noteLength / selNoteLength
shorterLength = noteLength
else
lengthOverlap = selNoteLength / noteLength
shorterLength = selNoteLength
end
if lengthOverlap >= g_minLengthOverlap then
startPosDiff = math.abs(selNote.startPos - startPos)
if startPosDiff <= (g_minStartPosOverlap * shorterLength) then
doSelect = true
end
end
end
end
if doSelect == true then
reaper.MIDI_SetNote(take, i, true, muted, startPos, endPos, chan, pitch, vel, true)
doUndo = true
end
end
end
if doUndo == true then
reaper.MIDI_Sort(take)
reaper.Undo_OnStateChangeEx2(0, undoMsg, 0, -1)
end
end
end
Main()
function NoUndoPoint () end -- Makes sure there is no necessary undo point created, see more
reaper.defer(NoUndoPoint) -- here: http://forum.cockos.com/showpost.php?p=1523953&postcount=67
If we want ripple editing in MIDI editor
(which is basically easy to code - just move later stuff forward), we want good chord detection because making user quantize material is dictatorship of DAW upon the user and shouldn't be a punishable offense, lol.
__________________
REAPER ReWorked: An elegant and self-sufficient all-around REAPER configuration
Other stuff