Old 04-30-2021, 05:58 AM   #1
daniellumertz
Human being with feelings
 
daniellumertz's Avatar
 
Join Date: Dec 2017
Location: Brazil
Posts: 2,011
Default Fast way to Delete MIDI Notes from specific channel

Hi People I am using in my MIDI Transfer script a function to delete MIDI Notes with the objective to solo a Channel.

The problem is that it is too slow for huge amount of MIDI it takes a lot of time.

In my test a item with 5600 MIDI notes took 10 second to run. I need to run this more than one time in the script so...

I am wondering if there is a better solution. Here the function I am using

Code:
 
function SoloChannel(take, ch) 
    local retval, notecnt, ccevtcnt, textsyxevtcnt = reaper.MIDI_CountEvts( take )
    --NOTES
    for i = notecnt, 1, -1  do
        local retval, selected, muted, startppqpos, endppqpos, chan, pitch, vel = reaper.MIDI_GetNote(take, i-1)
        if ch ~= 0 and chan ~= (ch-1) then
            reaper.MIDI_DeleteNote(take, i-1)
        end
    end
    --CC
    for i = ccevtcnt, 1, -1  do
        local retval, selected, muted, ppqpos, chanmsg, chan, msg2, msg3 = reaper.MIDI_GetCC( take, i-1 )
        if ch ~= 0 and chan ~= (ch-1) then
            reaper.MIDI_DeleteCC( take, i-1 )
        end
    end
end
daniellumertz is online now   Reply With Quote
Old 05-01-2021, 05:59 AM   #2
Malfunction
Human being with feelings
 
Malfunction's Avatar
 
Join Date: Sep 2020
Posts: 149
Default

Code:
function deletechannel(ch) 
    local gotAllOK
    local MIDIstring 
    gotAllOK, MIDIstring = reaper.MIDI_GetAllEvts(take, "") 
    MIDIlen = MIDIstring:len()  
    tableEvents = {}  
    stringPos = 1  
 
    while stringPos < MIDIlen do 
       offset, flags, ms, stringPos = string.unpack("i4Bs4", MIDIstring, stringPos) 
       if ms:len() == 3 and (ms:byte(1)>>4 == 9 or ms:byte(1)>>4 == 8) then 
        channel = ms:byte(1)&0x0F 
        if channel == ch-1 then ms="" end 
       end 
       table.insert(tableEvents, string.pack("i4Bs4", offset, flags, ms))  
    end 
    reaper.MIDI_SetAllEvts(take, table.concat(tableEvents)) 
 end 

hwnd = reaper.MIDIEditor_GetActive()
take = reaper.MIDIEditor_GetTake(hwnd)
deletechannel(1)
Malfunction is offline   Reply With Quote
Old 05-01-2021, 10:37 AM   #3
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 3,714
Default

As Malfunction advised, MIDI_Get/SetAllEvts is the fastest way to edit many thousands of events. However, if you prefer to use the older functions MIDI_Delete/SetNote/CC, then you should always use MIDI_DisableSort at the start, and MIDI_Sort at the end. This will speed up your script by orders of magnitude. (When using DisableSort, take into account that your note and CC indices will not change while you edit them.)
juliansader is offline   Reply With Quote
Old 05-01-2021, 08:14 PM   #4
daniellumertz
Human being with feelings
 
daniellumertz's Avatar
 
Join Date: Dec 2017
Location: Brazil
Posts: 2,011
Default

hey thanks people for the suggestions I made some bench here:
Code:
 
function SoloChannel(ch) 
    local retval, MIDIstring = reaper.MIDI_GetAllEvts(take, "") 
    local MIDIlen = MIDIstring:len()
    local tableEvents = {}
    local stringPos = 1
    --local pos=0 
    while stringPos < MIDIlen do 
        offset, flags, ms, stringPos = string.unpack("i4Bs4", MIDIstring, stringPos) -- Unpack the MIDI[stringPos] event 
        --pos=pos+offset -- For keeping track of the Postion of the notes in Ticks
        if ms:len() == 3 and (ms:byte(1)>>4 == 9 or ms:byte(1)>>4 == 8) then -- if ms:len == 3 means it have 3 messages(Notes, CC,Poly Aftertouch, Pitchbend )  (ms:byte(1)>>4 == 9 or ms:byte(1)>>4 == 8) note on or off
            local channel = ms:byte(1)&0x0F -- 0x0F = 0000 1111 in binary . ms is decimal. & is an and bitwise operation "have to have 1 in both to be 1". Will return channel as a decimal number
            if channel ~= ch-1 then ms="" end 
        end 
        table.insert(tableEvents, string.pack("i4Bs4", offset, flags, ms))
    end 
    reaper.MIDI_SetAllEvts(take, table.concat(tableEvents)) 
end 


function SoloChannelSort(take, ch) 
    reaper.MIDI_DisableSort( take )
    local retval, notecnt, ccevtcnt, textsyxevtcnt = reaper.MIDI_CountEvts( take )
    --NOTES
    for i = notecnt, 1, -1  do
        local retval, selected, muted, startppqpos, endppqpos, chan, pitch, vel = reaper.MIDI_GetNote(take, i-1)
        if ch ~= 0 and chan ~= (ch-1) then
            reaper.MIDI_DeleteNote(take, i-1)
        end
    end
    --CC
    for i = ccevtcnt, 1, -1  do
        local retval, selected, muted, ppqpos, chanmsg, chan, msg2, msg3 = reaper.MIDI_GetCC( take, i-1 )
        if ch ~= 0 and chan ~= (ch-1) then
            reaper.MIDI_DeleteCC( take, i-1 )
        end
    end
    reaper.MIDI_Sort( take )
end


function SoloChannelNoSort(take, ch) 
    local retval, notecnt, ccevtcnt, textsyxevtcnt = reaper.MIDI_CountEvts( take )
    --NOTES
    for i = notecnt, 1, -1  do
        local retval, selected, muted, startppqpos, endppqpos, chan, pitch, vel = reaper.MIDI_GetNote(take, i-1)
        if ch ~= 0 and chan ~= (ch-1) then
            reaper.MIDI_DeleteNote(take, i-1)
        end
    end
    --CC
    for i = ccevtcnt, 1, -1  do
        local retval, selected, muted, ppqpos, chanmsg, chan, msg2, msg3 = reaper.MIDI_GetCC( take, i-1 )
        if ch ~= 0 and chan ~= (ch-1) then
            reaper.MIDI_DeleteCC( take, i-1 )
        end
    end
end

function print(val)
    reaper.ShowConsoleMsg(tostring(val).."\n")
end

hwnd = reaper.MIDIEditor_GetActive()
take = reaper.MIDIEditor_GetTake(hwnd)
time1 = reaper.time_precise()
--SoloChannel(2) -- 0.028 s
--SoloChannelOriSort(take,2) -- 7s
SoloChannelOri(take,2) -- 95s

print(reaper.time_precise()-time1)
the results in a MIDI with 15k MIDI notes are
1. SoloChannel 0.028s +-
2. SoloChannelSort 7s
3 SoloChannelNoSort 98s
daniellumertz is online now   Reply With Quote
Old 05-01-2021, 08:51 PM   #5
daniellumertz
Human being with feelings
 
daniellumertz's Avatar
 
Join Date: Dec 2017
Location: Brazil
Posts: 2,011
Default

I wonder if there is a problem deleting all CC using this:

(because of CC that even Reaper don't let the user edit like CC123)
Code:
function DelCC(take, ch) 
    local retval, MIDIstring = reaper.MIDI_GetAllEvts(take, "") 
    local MIDIlen = MIDIstring:len()
    local tableEvents = {}
    local stringPos = 1
    --local pos=0 
    while stringPos < MIDIlen do 
        offset, flags, ms, stringPos = string.unpack("i4Bs4", MIDIstring, stringPos) -- Unpack the MIDI[stringPos] event 
        --pos=pos+offset -- For keeping track of the Postion of the notes in Ticks
        if ms:len() == 3 and ms:byte(1)>>4 == 11 then -- if ms:len == 3 means it have 3 messages(Notes, CC,Poly Aftertouch, Pitchbend )  (ms:byte(1)>>4 == 9 or ms:byte(1)>>4 == 8) note on or off
            ms=""
        end 
        table.insert(tableEvents, string.pack("i4Bs4", offset, flags, ms))
    end 
    reaper.MIDI_SetAllEvts(take, table.concat(tableEvents)) 
end
daniellumertz is online now   Reply With Quote
Old 05-02-2021, 01:23 PM   #6
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 3,714
Default

Quote:
Originally Posted by daniellumertz View Post
I wonder if there is a problem deleting all CC using this:

(because of CC that even Reaper don't let the user edit like CC123)
Yes, there will be a problem. The loop should end before the final All-Notes-Off CC.

If REAPER doesn't find an A-N-O CC at the end of a take, it will automatically insert one, but the new A-N-O will likely be at a different position (directly after the last event), thereby changing the source length of your take.
juliansader is offline   Reply With Quote
Old 05-02-2021, 05:58 PM   #7
daniellumertz
Human being with feelings
 
daniellumertz's Avatar
 
Join Date: Dec 2017
Location: Brazil
Posts: 2,011
Default

Ok I will make it quit the loop from MIDI CC 120 to match reaper preferences
daniellumertz is online now   Reply With Quote
Old 07-21-2021, 01:24 PM   #8
_Stevie_
Human being with feelings
 
_Stevie_'s Avatar
 
Join Date: Oct 2017
Location: Black Forest
Posts: 5,067
Default

A little follow up here, do we actually need MIDIsort for SetAllEvts?
__________________
My Reascripts forum thread | My Reascripts on GitHub
If you like or use my scripts, please support the Ukraine: Ukraine Crisis Relief Fund | DirectRelief | Save The Children | Razom
_Stevie_ is offline   Reply With Quote
Old 07-21-2021, 02:53 PM   #9
daniellumertz
Human being with feelings
 
daniellumertz's Avatar
 
Join Date: Dec 2017
Location: Brazil
Posts: 2,011
Default

In the function I made with Set All Events I haven't used MIDI Sort. So I don't think so (?)
daniellumertz is online now   Reply With Quote
Old 07-21-2021, 03:06 PM   #10
_Stevie_
Human being with feelings
 
_Stevie_'s Avatar
 
Join Date: Oct 2017
Location: Black Forest
Posts: 5,067
Default

Great, that's what I hoped
__________________
My Reascripts forum thread | My Reascripts on GitHub
If you like or use my scripts, please support the Ukraine: Ukraine Crisis Relief Fund | DirectRelief | Save The Children | Razom
_Stevie_ 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:28 AM.


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