Go Back   Cockos Incorporated Forums > REAPER Forums > ReaScript, JSFX, REAPER Plug-in Extensions, Developer Forum

Reply
 
Thread Tools Display Modes
Old 12-07-2018, 12:18 PM   #1
Breeder
Human being with feelings
 
Breeder's Avatar
 
Join Date: Nov 2010
Posts: 2,436
Default Let's play - best way to detect chords in non-quantized material!

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

Last edited by Breeder; 09-03-2020 at 03:29 PM.
Breeder is offline   Reply With Quote
Old 12-07-2018, 12:32 PM   #2
Breeder
Human being with feelings
 
Breeder's Avatar
 
Join Date: Nov 2010
Posts: 2,436
Default

Remember, ripple editing has to have two functionalities before it can be used to rapidly edit rhythm:

1. Length editing


2. Position editing



This is easy to code. What is not easy is having selecting chords easy. Because, when you ripple edit MIDI to input rhythm you will probably always want to move full chords and this is the problem I'm presenting.

My personal use case is to user arrow keys to go through notes and modifier+arrow keys to move and shorten/lengthen notes so I want arrow keys to select full chords most of the time. It's hard to say without testing on real material so write your findings if you want
__________________
REAPER ReWorked: An elegant and self-sufficient all-around REAPER configuration
Other stuff

Last edited by Breeder; 09-03-2020 at 03:29 PM.
Breeder is offline   Reply With Quote
Reply

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT -7. The time now is 12:55 AM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.