Old 04-07-2016, 04:39 PM   #1
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default [SOLVED] MIDI CC can't terminate script?

I have a new MIDI script....

https://github.com/Lazzle/ReaMIDI/bl...M%20Delete.lua

.... that I want to assign to a CC on a controller keyboard. So with a keycommand it runs and then you press the keycommand and it remembers to terminate the script, as requested the first time.

With a MIDI CC, when it is running the second press doesn't do anything. So I can turn it on, but not off.

Am I missing something?

Last edited by snooks; 05-04-2016 at 08:12 AM.
snooks is offline   Reply With Quote
Old 04-08-2016, 05:37 AM   #2
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

Quick test script. If you assign a keycommand and a MIDI CC to this script you'll see that you can't terminate it with the MIDI CC, but you can with the KC. The behaviour should really be the same.

Code:
a=0

function loop()
  a=a+1
  if a>100 then a=100 end
  reaper.defer(loop)
end

function exit()
  if terminating==true then reaper.ShowConsoleMsg("exiting.. \n") end
end

terminating=false
reaper.atexit(exit)
terminating=true

reaper.ShowConsoleMsg("\nstarting\n")

loop()
snooks is offline   Reply With Quote
Old 05-02-2016, 03:35 PM   #3
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

* bump *

This is a bug-like thing, right?
snooks is offline   Reply With Quote
Old 05-03-2016, 11:08 AM   #4
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

i wish i could help you with your problem snooks, but i wanted to drop a comment in this thread...

i've been wanting this feature ever since i moved away from sequencing on my MPC into sequencing full time in REAPER. this is HUGE for people who want to compose and edit a time selected loop from the controller. mouse-free sequencing. thank you so much, i will be following your work.

someone please help this guy!
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   Reply With Quote
Old 05-03-2016, 12:21 PM   #5
spk77
Human being with feelings
 
Join Date: Aug 2012
Location: Finland
Posts: 2,668
Default

Maybe reaper.get_action_context() would help?


Test:
(terminates if MIDI CC 7 value is 0, otherwise defer)


Code:
local val = -1
local last_val = -1
reaper.ShowConsoleMsg("\nstarting\n")

function loop()
  is_new_value,filename,sectionID,cmdID,mode,resolution,val = reaper.get_action_context()
  if val ~= last_val then
    reaper.ShowConsoleMsg(tostring(val) .. "\n")
    last_val = val
  end
  if val > 0 then
    reaper.defer(loop)
  end
end

function exit()
  if terminating==true then reaper.ShowConsoleMsg("exiting.. \n") end
end

terminating=false
reaper.atexit(exit)
terminating=true


loop()
spk77 is offline   Reply With Quote
Old 05-03-2016, 12:25 PM   #6
spk77
Human being with feelings
 
Join Date: Aug 2012
Location: Finland
Posts: 2,668
Default

More about "reaper.get_action_context":
http://forum.cockos.com/showthread.php?t=173193
spk77 is offline   Reply With Quote
Old 05-04-2016, 04:52 AM   #7
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

Yes, that works.... thanks spk77! For just toggling with learnt MIDI CCs or notes this is all that is needed....
Code:
reaper.ShowConsoleMsg("\nstarting\n")

function loop()
  if reaper.get_action_context() then return end
  reaper.defer(loop)
end

function exit()
  if terminating==true then reaper.ShowConsoleMsg("exiting.. \n") end
end

terminating=false
reaper.atexit(exit)
terminating=true

reaper.get_action_context() -- to clear "is_new_value" for checking in loop
loop()
@mccrabney: cheers for the shout, that's the new version uploaded if you want to test it out...

https://github.com/Lazzle/ReaMIDI/bl...M%20Delete.lua

... it leaves behind the input FX it adds at the moment because removing it by changing the track chunk causes glitches in audio and the remove FX thing in SWS doesn't work with input FX. Not a big deal, but it makes my janitorial genes a bit twitchy.
snooks is offline   Reply With Quote
Old 05-04-2016, 10:37 AM   #8
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

interesting. won't have an opportunity to test today but i look forward to it. i haven't seen a script that adds a js fx in this manner, very cool.

are additional instances of this js fx added if this script is triggered multiple times?

----

unrelated but i have another script suggestion that is kind of in line with this type of script: a script that selects the most recently recorded notes in a midi item.

for instance: you have a hi hat pattern in a midi item. you overdub in kicks and snares and want to quantize them but not the hi hats. ideally, the kick and snare notes would automatically be selected once overdub is turned off so they could be selectively quantized without having to be manually selected first.
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   Reply With Quote
Old 05-04-2016, 11:32 AM   #9
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

Quote:
Originally Posted by mccrabney View Post
interesting. won't have an opportunity to test today but i look forward to it. i haven't seen a script that adds a js fx in this manner, very cool.
That's inspired by eugen2777 who uses something similar to create a track for his retrospective record script. It does add a new layer of possibilities to scripting.
Quote:
are additional instances of this js fx added if this script is triggered multiple times?
Have no fear, it checks if one is there and adds one if not or re-enables the existing one if it exists, then disables it (again) on exit. The untidyness bit is having the disabled JS in the input FX bin when I'd really like to delete it. Maybe spk77 will swing by in a minute and reveal another bit of the API I haven't read though.
Quote:
unrelated but i have another script suggestion that is kind of in line with this type of script: a script that selects the most recently recorded notes in a midi item.

for instance: you have a hi hat pattern in a midi item. you overdub in kicks and snares and want to quantize them but not the hi hats. ideally, the kick and snare notes would automatically be selected once overdub is turned off so they could be selectively quantized without having to be manually selected first.
That would be useful, let me stew on it for a while.
snooks is offline   Reply With Quote
Old 05-05-2016, 10:46 AM   #10
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

hey snooks - i'm having strange behavior with this one.

i assigned it to a cc (fader on my quneo)
i recorded some midi notes to a midi file on a short loop
i then trigger the script, and i lose all transport control (unresponsive to kb or midi-assigned transport actions) and i cannot click around in REAPER. it's as if focus is stolen by some offscreen dialog box.

more testing will be posted in an edit or a reply...
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   Reply With Quote
Old 05-05-2016, 12:10 PM   #11
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

It's supposed to be triggered with a single CC message (or note) so a slider would turn it on and off lots of times. I don't know what effect that would have. Maybe it's something else, but learning a button sending a note/CC would be best.
snooks is offline   Reply With Quote
Old 05-05-2016, 03:53 PM   #12
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

gotcha. i tried using using another cc sending only 0 and 127 messages and got the same behavior. how have you learned this cc to your script -- absolute, relative, etc?
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   Reply With Quote
Old 05-05-2016, 04:33 PM   #13
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

The CC is learned in absolute mode and the button only sends 127. I've also tested with an APC40 Mini which sends notes. This is strange, it works here with no weirdness.

Is this a portable install in Program Files by any chance? I'm thinking that because it is trying to write to the resource path there might be some permissions issue there if the resource path is in a protected area.

edit: oh, yeah I attached a file to show the tooltip that should come up when it's active...
Attached Images
File Type: png dm-delete.PNG (1.6 KB, 709 views)
snooks is offline   Reply With Quote
Old 05-06-2016, 09:41 AM   #14
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

ugh, i'm an idiot, i somehow managed to save what looks like an html file as "DM Delete.lua"

sorry, snooks. let me try again.

edit, ok it's working. you are a rockstar.

some notes, coming from someone who used an MPC as primary midi sequencer from 2008-2014 before switching to REAPER as mentioned before (and who also beta tested extensively for the mpc1000/2500 3rd party "jj" os)

this behaves almost exactly the way i would hope, and i can do a lot of the rest myself. however, i would consider this perfect if instead of turning on and off with 127 values, it turned on at 127 and off at 0, so that "toggle" buttons or "hold and release" buttons could be used here.

i have assigned this script to a button on my launchpad that sends 127 when on and 0 when released. i envision holding the button down with one hand and deleting notes with the other hand. granted, i could remap value 0 messages to value 127 messages, but then you run into potential problems where the on/off gets out of sync with the launchpad. right now, this is the solution i'm using and it works OK. will report back if it becomes a problem, otherwise, i guess we're good!

i really like that this script doesn't turn on Overdub by default like it did in the MPC series.

you've made my year with this. THANK YOU. please point me toward any donation site or PM me a paypal address so i can tip you.
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.

Last edited by mccrabney; 05-06-2016 at 10:04 AM.
mccrabney is online now   Reply With Quote
Old 05-06-2016, 06:40 PM   #15
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

No worries, I've uploaded another version that should do what you want. I haven't tested it though so maybe it doesn't.
snooks is offline   Reply With Quote
Old 06-29-2016, 08:18 PM   #16
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

any chance you can make a version of this that SELECTS the note rather than DELETES it?? adding to previous selection so you could select notes as it loops and then selectively quantize them......
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   Reply With Quote
Old 06-30-2016, 08:39 AM   #17
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

Yes, no problemo - I'll do it over the next couple/few days.....
snooks is offline   Reply With Quote
Old 06-30-2016, 08:45 AM   #18
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

amazing, thanks. i might like this one even more than the original DM delete script...
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   Reply With Quote
Old 07-29-2016, 07:53 AM   #19
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

yo, checking in about the above request...

also a question -- for the original DM delete script, while the script is running, is there a way to block the Note On from reaching the VST? when i'm attempting to DM delete notes from a sequence, i hear the note that i'm deleting get triggered on NOTEON. in the world of MPCs, the NOTEON is blocked from triggering the sample, but rather acts as a "flag" to block/delete subsequent instances of that note from triggering noteon.
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   Reply With Quote
Old 07-29-2016, 09:32 AM   #20
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

Sorry, I totally forgot about that... here's an edited script, you change the line...
Code:
local action = edit_types.DELETE
... to edit_types.SELECT if you want to select instead of delete.

I think anyway, I haven't tested it yet... I will later if there are any problems.
Code:
-- D(rum) M(achine) Delete

-- run script and held notes will be deleted from 
-- either the active item in the MIDI editor or
-- on an item between the loop cursors of a the 
-- first selected track, which must be rec-armed.

-- run script again to get out of delete mode


local function DBG(str)
  --reaper.ShowConsoleMsg(str.."\n")
end

local edit_types={DELETE="Delete", SELECT="Select"}
local action = edit_types.DELETE;

jsfx={} --store details of helper effect
jsfx.name="Script Note Getter"
jsfx.fn="Script Note Getter"
--jsfx.body at end of script


function createJSEffect(fn,str)
  local file=io.open(reaper.GetResourcePath().."/Effects/"..fn, "w")
  file:write(str)
  file:close()
end


function deleteJSEffect(fx)
  os.remove(reaper.GetResourcePath().."/Effects/"..fx.fn)
end


--fx is table with .name and .fn (for filename)
function removeJSEffect(track,fx)
  local chunk,ok="",false
  ok,chunk=reaper.GetTrackStateChunk(track,chunk, false)
  local pattern="(BYPASS %d %d %d[%c%s].JS \""..fx.name..".-WAK %d)"
  local replacements
  chunk,replacements=string.gsub(chunk,pattern,"")
  if replacements>0 then
    ok=reaper.SetTrackStateChunk(tr,chunk,false)
    deleteJSEffect(fx)
    return true
  end
  return false
end


function getOrAddInputFx(track,fx,create_new)
  local idx=reaper.TrackFX_AddByName(track,fx.name,true,1)
  if idx==-1 or idx==nil then
    idx=reaper.TrackFX_AddByName(track,fx.fn,true,1)
    if (idx==nil or idx==-1) and create_new==true then
      createJSEffect(fx.fn,fx.body)
      idx=getOrAddInputFx(track,fx,false)
      return idx
    else 
      tr=nil
      return -1
    end
  end
  idx=idx|0x1000000
  reaper.TrackFX_SetEnabled(track, idx, true)
  return idx
end


local delete_ahead=0.15 --so that notes don't sound
function getNotes(tk,pitches,pp_qn,check_start,l_start)
  local ni, tr, it
  local midi={}
  cnt=0
  
  local ok, sel, mute, startpos, endpos, chan, pitch, vel=reaper.MIDI_GetNote(tk, 0)
  while ok do
    for i=1,#pitches,1 do
      if pitch==pitches[i] then
        startpos=reaper.MIDI_GetProjQNFromPPQPos(tk, startpos)
        endpos=reaper.MIDI_GetProjQNFromPPQPos(tk, endpos)
        if (startpos<=pp_qn+delete_ahead and endpos>=pp_qn) or 
            (check_start and startpos>=l_start and startpos<=l_start+delete_ahead) then
          local note={ type="note", idx=cnt, -- 6
                    select=sel,mute=mute, ostartpos=startpos, --12
                    startpos=startpos, endpos=endpos, len=endpos-startpos, 
                    pitch=pitch, chan=chan, vel=vel }
          midi[#midi+1]=note
        end
      end
    end
    cnt=cnt+1
    ok, sel, mute, startpos, endpos, chan, pitch, vel=reaper.MIDI_GetNote(tk, cnt)      
  end
  return midi
end


function dMedit()
  local is_new_value,_,_,_,_,_,val=reaper.get_action_context()
  if is_new_value and val==0 then return end
  -- tr, tk, helper_idx set in prep()
  -- get number of held notes from helper plugin
  nib=reaper.TrackFX_GetParam(tr, helper_idx, 1) --1=note in buffer
  if nib>0 then
    -- get loop points and check whether cursor is near the end
    -- so we can delete on return to start of loop without them
    -- playing
    local l_start, l_end=reaper.GetSet_LoopTimeRange(false, false, 0, 0, false)
    l_start=reaper.TimeMap2_timeToQN(0, l_start)
    l_end=reaper.TimeMap2_timeToQN(0, l_end)
    local pp=reaper.GetPlayPosition()
    local pp_qn=reaper.TimeMap2_timeToQN(0, pp)
    local check_start=l_end-pp_qn<delete_ahead and true or false
    
    --get held notes from buffer in helper plugin
    local pitches={}
    for i=1,nib,1 do
      reaper.TrackFX_SetParam(tr,helper_idx,2,i-1) --set Note# to i
      pitches[#pitches+1],_,_=reaper.TrackFX_GetParam(tr, helper_idx, 3)
    end 
    local notes=getNotes(tk,pitches,pp_qn,check_start,l_start)
      
    if #notes>0 then
      DBG("Start "..action.." notes...")
      reaper.Undo_BeginBlock() -- create undo point for these notes
      --always go backwards deleting notes
      for i=#notes,1,-1 do
        if action == edit_types.SELECT then
          reaper.MIDI_SetNote(tk, notes[i].idx, true, nil, nil, nil, nil, nil, nil, nil)
        else
          reaper.MIDI_DeleteNote(tk,notes[i].idx)
        end
      end
      reaper.Undo_EndBlock("DM "..action.." - ".. action.. " notes",4)
    end
    reaper.UpdateArrange()
  end
  reaper.defer(dMedit)
end


function getMidiEditorTrackTake()
  local ame=reaper.MIDIEditor_GetActive()
  local mode=reaper.MIDIEditor_GetMode(ame)
  if mode > -1 then -- we are in a MIDI editor, -1 if ME not focused
    tk=reaper.MIDIEditor_GetTake(ame)
    --check that it's an actual take (in case of empty MIDI editor)
    if not reaper.ValidatePtr(tk, 'MediaItem_Take*') then return nil,nil end
    tr=reaper.GetMediaItemTake_Track(tk) 
  else
    return nil,nil
  end
  return tr,tk
end


function prep()
  DBG("in prep")
  reaper.get_action_context() -- to clear is_new_value for checking
                              -- for new presses in main loop
  tr,tk=getMidiEditorTrackTake()
  if tk==nil then 
    if reaper.CountSelectedTracks()>0 then
        tr=reaper.GetSelectedTrack(0,0)
        local _,t_name=reaper.GetSetMediaTrackInfo_String(tr,"P_NAME"," ",false)
        local _, ts=reaper.GetTrackState(tr)
        if ts&64==64 and ts&2==2 then -- is rec armed and selected
        local num_items=reaper.CountTrackMediaItems(tr)
        if num_items>0 then
          local l_start,l_end=reaper.GetSet_LoopTimeRange(false,true,0,0,true)
          local cur=reaper.GetCursorPosition()
          local i=1
          while i<=num_items do
            local it=reaper.GetTrackMediaItem(tr,i-1)
            local i_start=reaper.GetMediaItemInfo_Value(it,"D_POSITION")
            if math.floor(l_start*10)==math.floor(i_start*10) then
              tk=reaper.GetActiveTake(it)
              i=num_items
            end
            i=i+1
          end
        end
      end
    end
  end
  if tk~=nil and reaper.BR_IsTakeMidi(tk) then
    helper_idx=getOrAddInputFx(tr,jsfx,true)
    DBG("helper_idx="..helper_idx)
    if helper_idx>-1 then
      reaper.TrackCtl_SetToolTip("!-!-!-!-!-!  Live "..action.." ACTIVE  !-!-!-!-!-!", 800,20, true)
      dMedit()
    else
      DBG("No effect added")
    end
  end
end


function cleanUp()
  if helper_idx~=nil then
    reaper.TrackCtl_SetToolTip("--- Live "..action.." Inactive ---", 800,20, true)
    DBG("exit")
    --removeJSEffect(tr,jsfx) --set chunk causes glitches
                              --so can't remove input FX
    reaper.TrackFX_SetEnabled(tr, helper_idx, false) --just disable it
  end

end



jsfx.body=[[
desc:Script Note Getter

slider1:0<0,1,1{On,Off}>Active (eats notes)
slider2:0<0,127,1>Notes in buffer
slider3:0<0,1000,1>Note # (for script)
slider4:0<0,127,1>Output Pitch
slider5:0<0,1,1>Trigger


@init
notebuf=0; //start pos of buffer
nb_width=3; //number of entries per note
buflen=0; //notes in buffer

function addRemoveNoteFromBuffer(m1,m2,m3)
( 
  s = m1&$xF0;
  c = m1&$xF; // channel
  n = m2;
  v = m3; // velocity
  
  init_buflen=buflen;
  
  i = -1;
  while // look for this note|channel already in the buffer
  (
    i = i+1;
    i < buflen && (notebuf[nb_width*i]|0 != n || notebuf[nb_width*i+1]|0 != c);
        );

    (s == $x90 && v > 0) ? // note-on, add to buffer
    ( 
      notebuf[nb_width*i] = n;
      notebuf[nb_width*i+1] = c;
      notebuf[nb_width*i+2] = v;
      i == buflen ? buflen = buflen+1;
    ) 
    : // note-off, remove from buffer
    (
      is_note_off=1;
      i < buflen ?
      (
         memcpy(notebuf+nb_width*i, notebuf+nb_width*(i+1),
                      nb_width*(buflen-i-1));  // delete the entry
         buflen = buflen-1;
       );
    );
    buflen==init_buflen ? -1; //return value for nothing added/removed
);


@slider
p=slider3*nb_width; //position in buffer
slider4=notebuf[p];


@block
while (midirecv(offset,msg1,msg2,msg3))
(  
  slider1 ? (
    midisend(offset,msg1,msg2,msg3);
  ):( //eating notes
    msg1|$x90==$x90 ? (
      addRemoveNoteFromBuffer(msg1,msg2,msg3);
    );
    msg1|$x80==$x80 ? (
      addRemoveNoteFromBuffer(msg1,msg2,msg3)==-1 ? (
        //no note in buffer, so allow trailing note-offs through
        midisend(offset,msg1,msg2,msg3);
      );
    );
    slider2=buflen;
  );
)
]]


script_init=true
reaper.atexit(cleanUp)
script_init=false
reaper.Undo_BeginBlock()
prep()
reaper.Undo_EndBlock("DM Delete",-1)
It shouldn't be allowing any input notes through after the script is active..... I'll have a look at that l8tr too.
snooks is offline   Reply With Quote
Old 07-29-2016, 12:26 PM   #21
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

thanks dude! awesome work, now to make space for a quantize button on my launchpad pro layout...
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   Reply With Quote
Old 07-29-2016, 05:18 PM   #22
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

No worries, could you explain what's happening with Note Ons getting through... is it ones you are pressing or ones in a sequence? If it's from the sequence there's a lookahead value in the script...
Code:
local delete_ahead=0.15 --so that notes don't sound
... that works for me, anything less than that and the odd Note On gets through from the sequence. Maybe that needs set a bit bigger on your system?
snooks is offline   Reply With Quote
Old 08-01-2016, 09:00 AM   #23
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

yeah let me try to explain further. difficult.

it's the one I'm pressing -- let's say there is a bunch of note 36s that I'm trying to delete. here are my steps:

1- trigger script
2- press/hold note 36
3- issue- note 36 is triggered at moment of step 2

this is not a functional issue, more of an "audio-cosmetic" issue. I'll need to confirm as I'm away from the equipment atm but I'm pretty sure this is that happens.
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   Reply With Quote
Old 08-01-2016, 03:40 PM   #24
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

It shouldn't do that, it should eat all MIDI from the track input and work or not eat all MIDI and not work. Unless there's some MIDI routing going on that's getting around the Input FX somehow...
snooks is offline   Reply With Quote
Old 08-01-2016, 04:42 PM   #25
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

Quote:
Originally Posted by snooks View Post
It shouldn't do that, it should eat all MIDI from the track input and work or not eat all MIDI and not work. Unless there's some MIDI routing going on that's getting around the Input FX somehow...
thanks, will do some analysis. i do have some nonstandard routing going on that may be at cause. sorry to raise concern.

I'm gonna have to assign this to a foot pedal in addition to a launchpad button.
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   Reply With Quote
Old 08-10-2016, 10:22 AM   #26
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

howdy, checking in again to mention that the newest version (with the SELECT option) doesn't seem to allow midi cc to terminate the script, as per the original question in this thread haha...it appears we've come full circle...tests OK with regular key commands, but not midi cc.
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   Reply With Quote
Old 08-11-2016, 01:06 AM   #27
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

Whoops, thanks for reporting. I'm MIDI-less for the next couple of days but I'll have a look to see what's happened.
snooks is offline   Reply With Quote
Old 08-13-2016, 01:43 PM   #28
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

Okay, I've had a look/play and it seems that possibly the controller you are using to trigger the script is no longer a two state CC button? It only exits with a zero value from the learned MIDI command at the moment. But the bit of code that checks for that is a bit limiting because we don't actually have to check for a zero value (just the next one will do). Here's a version with slightly less worse comments at the top and that line fixed enhanced...
Code:
-- D(rum) M(achine) Edit

-- run script and held notes will be edited in 
-- either the active item in the MIDI editor or
-- on an item between the loop cursors of a the 
-- first selected track, which must be rec-armed.

-- run script again to get out of edit mode

-- edit modes are:  delete
--              or  select


local function DBG(str)
  --reaper.ShowConsoleMsg(str.."\n")
end

local edit_types={DELETE="Delete", SELECT="Select"}
local action = edit_types.DELETE;

jsfx={} --store details of helper effect
jsfx.name="Script Note Getter"
jsfx.fn="Script Note Getter"
--jsfx.body at end of script


function createJSEffect(fn,str)
  local file=io.open(reaper.GetResourcePath().."/Effects/"..fn, "w")
  file:write(str)
  file:close()
end


function deleteJSEffect(fx)
  os.remove(reaper.GetResourcePath().."/Effects/"..fx.fn)
end


--fx is table with .name and .fn (for filename)
function removeJSEffect(track,fx)
  local chunk,ok="",false
  ok,chunk=reaper.GetTrackStateChunk(track,chunk, false)
  local pattern="(BYPASS %d %d %d[%c%s].JS \""..fx.name..".-WAK %d)"
  local replacements
  chunk,replacements=string.gsub(chunk,pattern,"")
  if replacements>0 then
    ok=reaper.SetTrackStateChunk(tr,chunk,false)
    deleteJSEffect(fx)
    return true
  end
  return false
end


function getOrAddInputFx(track,fx,create_new)
  local idx=reaper.TrackFX_AddByName(track,fx.name,true,1)
  if idx==-1 or idx==nil then
    idx=reaper.TrackFX_AddByName(track,fx.fn,true,1)
    if (idx==nil or idx==-1) and create_new==true then
      createJSEffect(fx.fn,fx.body)
      idx=getOrAddInputFx(track,fx,false)
      return idx
    else 
      tr=nil
      return -1
    end
  end
  idx=idx|0x1000000
  reaper.TrackFX_SetEnabled(track, idx, true)
  return idx
end


local delete_ahead=0.17 --so that notes don't sound
function getNotes(tk,pitches,pp_qn,check_start,l_start)
  local ni, tr, it
  local midi={}
  cnt=0
  
  local ok, sel, mute, startpos, endpos, chan, pitch, vel=reaper.MIDI_GetNote(tk, 0)
  while ok do
    for i=1,#pitches,1 do
      if pitch==pitches[i] then
        startpos=reaper.MIDI_GetProjQNFromPPQPos(tk, startpos)
        endpos=reaper.MIDI_GetProjQNFromPPQPos(tk, endpos)
        if (startpos<=pp_qn+delete_ahead and endpos>=pp_qn) or 
            (check_start and startpos>=l_start and startpos<=l_start+delete_ahead) then
          local note={ type="note", idx=cnt, -- 6
                    select=sel,mute=mute, ostartpos=startpos, --12
                    startpos=startpos, endpos=endpos, len=endpos-startpos, 
                    pitch=pitch, chan=chan, vel=vel }
          midi[#midi+1]=note
        end
      end
    end
    cnt=cnt+1
    ok, sel, mute, startpos, endpos, chan, pitch, vel=reaper.MIDI_GetNote(tk, cnt)      
  end
  return midi
end


function dMedit()
  local is_new_value,_,_,_,_,_,val=reaper.get_action_context()
  if is_new_value then return end
  -- tr, tk, helper_idx set in prep()
  -- get number of held notes from helper plugin
  nib=reaper.TrackFX_GetParam(tr, helper_idx, 1) --1=note in buffer
  if nib>0 then
    -- get loop points and check whether cursor is near the end
    -- so we can delete on return to start of loop without them
    -- playing
    local l_start, l_end=reaper.GetSet_LoopTimeRange(false, false, 0, 0, false)
    l_start=reaper.TimeMap2_timeToQN(0, l_start)
    l_end=reaper.TimeMap2_timeToQN(0, l_end)
    local pp=reaper.GetPlayPosition()
    local pp_qn=reaper.TimeMap2_timeToQN(0, pp)
    local check_start=l_end-pp_qn<delete_ahead and true or false
    
    --get held notes from buffer in helper plugin
    local pitches={}
    for i=1,nib,1 do
      reaper.TrackFX_SetParam(tr,helper_idx,2,i-1) --set Note# to i
      pitches[#pitches+1],_,_=reaper.TrackFX_GetParam(tr, helper_idx, 3)
    end 
    local notes=getNotes(tk,pitches,pp_qn,check_start,l_start)
      
    if #notes>0 then
      DBG("Start "..action.." notes...")
      reaper.Undo_BeginBlock() -- create undo point for these notes
      --always go backwards deleting notes
      for i=#notes,1,-1 do
        if action == edit_types.SELECT then
          reaper.MIDI_SetNote(tk, notes[i].idx, true, nil, nil, nil, nil, nil, nil, nil)
        else
          reaper.MIDI_DeleteNote(tk,notes[i].idx)
        end
      end
      reaper.Undo_EndBlock("DM "..action.." - ".. action.. " notes",4)
    end
    reaper.UpdateArrange()
  end
  reaper.defer(dMedit)
end


function getMidiEditorTrackTake()
  local ame=reaper.MIDIEditor_GetActive()
  local mode=reaper.MIDIEditor_GetMode(ame)
  if mode > -1 then -- we are in a MIDI editor, -1 if ME not focused
    tk=reaper.MIDIEditor_GetTake(ame)
    --check that it's an actual take (in case of empty MIDI editor)
    if not reaper.ValidatePtr(tk, 'MediaItem_Take*') then return nil,nil end
    tr=reaper.GetMediaItemTake_Track(tk) 
  else
    return nil,nil
  end
  return tr,tk
end


function prep()
  DBG("in prep")
  reaper.get_action_context() -- to clear is_new_value for checking
                              -- for new presses in main loop
  tr,tk=getMidiEditorTrackTake()
  if tk==nil then 
    if reaper.CountSelectedTracks()>0 then
        tr=reaper.GetSelectedTrack(0,0)
        local _,t_name=reaper.GetSetMediaTrackInfo_String(tr,"P_NAME"," ",false)
        local _, ts=reaper.GetTrackState(tr)
        if ts&64==64 and ts&2==2 then -- is rec armed and selected
        local num_items=reaper.CountTrackMediaItems(tr)
        if num_items>0 then
          local l_start,l_end=reaper.GetSet_LoopTimeRange(false,true,0,0,true)
          local cur=reaper.GetCursorPosition()
          local i=1
          while i<=num_items do
            local it=reaper.GetTrackMediaItem(tr,i-1)
            local i_start=reaper.GetMediaItemInfo_Value(it,"D_POSITION")
            if math.floor(l_start*10)==math.floor(i_start*10) then
              tk=reaper.GetActiveTake(it)
              i=num_items
            end
            i=i+1
          end
        end
      end
    end
  end
  if tk~=nil and reaper.BR_IsTakeMidi(tk) then
    helper_idx=getOrAddInputFx(tr,jsfx,true)
    DBG("helper_idx="..helper_idx)
    if helper_idx>-1 then
      reaper.TrackCtl_SetToolTip("!-!-!-!-!-!  Live "..action.." ACTIVE  !-!-!-!-!-!", 800,20, true)
      dMedit()
    else
      DBG("No effect added")
    end
  end
end


function cleanUp()
  if helper_idx~=nil then
    reaper.TrackCtl_SetToolTip("--- Live "..action.." Inactive ---", 800,20, true)
    DBG("exit")
    --removeJSEffect(tr,jsfx) --set chunk causes glitches
                              --so can't remove input FX
    reaper.TrackFX_SetEnabled(tr, helper_idx, false) --just disable it
  end

end



jsfx.body=[[
desc:Script Note Getter

slider1:0<0,1,1{On,Off}>Active (eats notes)
slider2:0<0,127,1>Notes in buffer
slider3:0<0,1000,1>Note # (for script)
slider4:0<0,127,1>Output Pitch
slider5:0<0,1,1>Trigger


@init
notebuf=0; //start pos of buffer
nb_width=3; //number of entries per note
buflen=0; //notes in buffer

function addRemoveNoteFromBuffer(m1,m2,m3)
( 
  s = m1&$xF0;
  c = m1&$xF; // channel
  n = m2;
  v = m3; // velocity
  
  init_buflen=buflen;
  
  i = -1;
  while // look for this note|channel already in the buffer
  (
    i = i+1;
    i < buflen && (notebuf[nb_width*i]|0 != n || notebuf[nb_width*i+1]|0 != c);
        );

    (s == $x90 && v > 0) ? // note-on, add to buffer
    ( 
      notebuf[nb_width*i] = n;
      notebuf[nb_width*i+1] = c;
      notebuf[nb_width*i+2] = v;
      i == buflen ? buflen = buflen+1;
    ) 
    : // note-off, remove from buffer
    (
      is_note_off=1;
      i < buflen ?
      (
         memcpy(notebuf+nb_width*i, notebuf+nb_width*(i+1),
                      nb_width*(buflen-i-1));  // delete the entry
         buflen = buflen-1;
       );
    );
    buflen==init_buflen ? -1; //return value for nothing added/removed
);


@slider
p=slider3*nb_width; //position in buffer
slider4=notebuf[p];


@block
while (midirecv(offset,msg1,msg2,msg3))
(  
  slider1 ? (
    midisend(offset,msg1,msg2,msg3);
  ):( //eating notes
    msg1|$x90==$x90 ? (
      addRemoveNoteFromBuffer(msg1,msg2,msg3);
    );
    msg1|$x80==$x80 ? (
      addRemoveNoteFromBuffer(msg1,msg2,msg3)==-1 ? (
        //no note in buffer, so allow trailing note-offs through
        midisend(offset,msg1,msg2,msg3);
      );
    );
    slider2=buflen;
  );
)
]]


script_init=true
reaper.atexit(cleanUp)
script_init=false
reaper.Undo_BeginBlock()
prep()
reaper.Undo_EndBlock("DM Delete",-1)
snooks is offline   Reply With Quote
Old 08-13-2016, 05:21 PM   #29
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

heh i had modified it from a two state button (127,0) to a repeated button (127 on, 127 off)

will test tomorrow, thanks again
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   Reply With Quote
Old 08-14-2016, 08:21 AM   #30
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

No probs btw it looks like learning with a note using helgoboss's ReaLearn means you can get the toggle off with note off now which isn't possible with native learn.
snooks is offline   Reply With Quote
Old 08-14-2016, 10:05 AM   #31
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

ok, that latest one is working great with 127 on, 0 off.

new bug that i've noticed -- the last note in the item/time selection is not deleted sometimes. possibly having to do with if the note has a noteoff after or at the time selection loop point / midi item end.

also, perhaps most importantly, this script only seems to work on notes recorded in channel 1. anything recorded to other channels is not "caught" by the js input fx, i assume. i have 8 tracks set to record channels 1-8 respectively, and this script only works on track/channel 1.

i confirmed this with a multichannel midi track. i switched between channels 1-8 while recording midi notes into an item, and then attempted to DM delete them by channel. only channel 1 worked.
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   Reply With Quote
Old 08-14-2016, 01:33 PM   #32
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

Quote:
Originally Posted by mccrabney View Post
ok, that latest one is working great with 127 on, 0 off.

new bug that i've noticed -- the last note in the item/time selection is not deleted sometimes. possibly having to do with if the note has a noteoff after or at the time selection loop point / midi item end.
That might be the look_ahead setting (formerly delete_ahead)...
Code:
local look_ahead=0.17 --so that notes don't sound
... try changing that to a higher value. The script is triggered X times a second whilst the project is playing so this looks ahead for an amount that should compensate for this.
Quote:
also, perhaps most importantly, this script only seems to work on notes recorded in channel 1. anything recorded to other channels is not "caught" by the js input fx, i assume. i have 8 tracks set to record channels 1-8 respectively, and this script only works on track/channel 1.

i confirmed this with a multichannel midi track. i switched between channels 1-8 while recording midi notes into an item, and then attempted to DM delete them by channel. only channel 1 worked.
Yes, it was single channel only. This version should be multi-channel. You need to delete the Script Note Getter script from your Reaper JS Effects folder so that it saves and loads the new version from the script first. Maybe make sure the any track doesn't have the old version of the Script Note Getter too.

In next post, it makes this one too long...
snooks is offline   Reply With Quote
Old 08-14-2016, 01:33 PM   #33
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

Code:
-- D(rum) M(achine) Edit

-- run script and held notes will be edited in 
-- either the active item in the MIDI editor or
-- on an item between the loop cursors of a the 
-- first selected track, which must be rec-armed.

-- run script again to get out of edit mode

-- edit modes are:  delete
--              or  select


local function DBG(str)
  --reaper.ShowConsoleMsg(str.."\n")
end

local edit_types={DELETE="Delete", SELECT="Select"}
local action = edit_types.DELETE;

jsfx={} --store details of helper effect
jsfx.name="Script Note Getter"
jsfx.fn="Script Note Getter"
--jsfx.body at end of script


function createJSEffect(fn,str)
  local file=io.open(reaper.GetResourcePath().."/Effects/"..fn, "w")
  file:write(str)
  file:close()
end


function deleteJSEffect(fx)
  os.remove(reaper.GetResourcePath().."/Effects/"..fx.fn)
end


--fx is table with .name and .fn (for filename)
function removeJSEffect(track,fx)
  local chunk,ok="",false
  ok,chunk=reaper.GetTrackStateChunk(track,chunk, false)
  local pattern="(BYPASS %d %d %d[%c%s].JS \""..fx.name..".-WAK %d)"
  local replacements
  chunk,replacements=string.gsub(chunk,pattern,"")
  if replacements>0 then
    ok=reaper.SetTrackStateChunk(tr,chunk,false)
    deleteJSEffect(fx)
    return true
  end
  return false
end


function getOrAddInputFx(track,fx,create_new)
  local idx=reaper.TrackFX_AddByName(track,fx.name,true,1)
  if idx==-1 or idx==nil then
    idx=reaper.TrackFX_AddByName(track,fx.fn,true,1)
    if (idx==nil or idx==-1) and create_new==true then
      createJSEffect(fx.fn,fx.body)
      idx=getOrAddInputFx(track,fx,false)
      return idx
    else 
      tr=nil
      return -1
    end
  end
  idx=idx|0x1000000
  reaper.TrackFX_SetEnabled(track, idx, true)
  return idx
end


local look_ahead=0.17 --so that notes don't sound
function getNotes(tk,pitches,pp_qn,check_start,l_start)
  local ni, tr, it
  local midi={}
  cnt=0
  
  local ok, sel, mute, startpos, endpos, chan, pitch, vel=reaper.MIDI_GetNote(tk, 0)
  while ok do
    for i=1,#pitches,1 do
      if pitch==pitches[i].note and chan==pitches[i].chan then
        startpos=reaper.MIDI_GetProjQNFromPPQPos(tk, startpos)
        endpos=reaper.MIDI_GetProjQNFromPPQPos(tk, endpos)
        if (startpos<=pp_qn+look_ahead and endpos>=pp_qn) or 
            (check_start and startpos>=l_start and startpos<=l_start+look_ahead) then
          local note={ type="note", idx=cnt, -- 6
                    select=sel,mute=mute, ostartpos=startpos, --12
                    startpos=startpos, endpos=endpos, len=endpos-startpos, 
                    pitch=pitch, chan=chan, vel=vel }
          midi[#midi+1]=note
        end
      end
    end
    cnt=cnt+1
    ok, sel, mute, startpos, endpos, chan, pitch, vel=reaper.MIDI_GetNote(tk, cnt)      
  end
  return midi
end


function dMedit()
  local is_new_value,_,_,_,_,_,val=reaper.get_action_context()
  if is_new_value then return end
  -- tr, tk, helper_idx set in prep()
  -- get number of held notes from helper plugin
  nib=reaper.TrackFX_GetParam(tr, helper_idx, 1) --1=note in buffer
  if nib>0 then
    -- get loop points and check whether cursor is near the end
    -- so we can delete on return to start of loop without them
    -- playing
    local l_start, l_end=reaper.GetSet_LoopTimeRange(false, false, 0, 0, false)
    l_start=reaper.TimeMap2_timeToQN(0, l_start)
    l_end=reaper.TimeMap2_timeToQN(0, l_end)
    local pp=reaper.GetPlayPosition()
    local pp_qn=reaper.TimeMap2_timeToQN(0, pp)
    local check_start=l_end-pp_qn<look_ahead and true or false
    
    --get held notes from buffer in helper plugin
    local pitches={}
    for i=1,nib,1 do
      reaper.TrackFX_SetParam(tr,helper_idx,2,i-1) --set Note# to i
      pitches[#pitches+1] = {}
      pitches[#pitches].note,_,_=reaper.TrackFX_GetParam(tr, helper_idx, 3)
      pitches[#pitches].chan = reaper.TrackFX_GetParam(tr, helper_idx, 4)
    end 
    local notes=getNotes(tk,pitches,pp_qn,check_start,l_start)
      
    if #notes>0 then
      DBG("Start "..action.." notes...")
      reaper.Undo_BeginBlock() -- create undo point for these notes
      --always go backwards deleting notes
      for i=#notes,1,-1 do
        if action == edit_types.SELECT then
          reaper.MIDI_SetNote(tk, notes[i].idx, true, nil, nil, nil, nil, nil, nil, nil)
        else
          reaper.MIDI_DeleteNote(tk,notes[i].idx)
        end
      end
      reaper.Undo_EndBlock("DM "..action.." - ".. action.. " notes",4)
    end
    reaper.UpdateArrange()
  end
  reaper.defer(dMedit)
end


function getMidiEditorTrackTake()
  local ame=reaper.MIDIEditor_GetActive()
  local mode=reaper.MIDIEditor_GetMode(ame)
  if mode > -1 then -- we are in a MIDI editor, -1 if ME not focused
    tk=reaper.MIDIEditor_GetTake(ame)
    --check that it's an actual take (in case of empty MIDI editor)
    if not reaper.ValidatePtr(tk, 'MediaItem_Take*') then return nil,nil end
    tr=reaper.GetMediaItemTake_Track(tk) 
  else
    return nil,nil
  end
  return tr,tk
end


function prep()
  DBG("in prep")
  reaper.get_action_context() -- to clear is_new_value for checking
                              -- for new presses in main loop
  tr,tk=getMidiEditorTrackTake()
  if tk==nil then 
    if reaper.CountSelectedTracks()>0 then
        tr=reaper.GetSelectedTrack(0,0)
        local _,t_name=reaper.GetSetMediaTrackInfo_String(tr,"P_NAME"," ",false)
        local _, ts=reaper.GetTrackState(tr)
        if ts&64==64 and ts&2==2 then -- is rec armed and selected
        local num_items=reaper.CountTrackMediaItems(tr)
        if num_items>0 then
          local l_start,l_end=reaper.GetSet_LoopTimeRange(false,true,0,0,true)
          local cur=reaper.GetCursorPosition()
          local i=1
          while i<=num_items do
            local it=reaper.GetTrackMediaItem(tr,i-1)
            local i_start=reaper.GetMediaItemInfo_Value(it,"D_POSITION")
            if math.floor(l_start*10)==math.floor(i_start*10) then
              tk=reaper.GetActiveTake(it)
              i=num_items
            end
            i=i+1
          end
        end
      end
    end
  end
  if tk~=nil and reaper.BR_IsTakeMidi(tk) then
    helper_idx=getOrAddInputFx(tr,jsfx,true)
    DBG("helper_idx="..helper_idx)
    if helper_idx>-1 then
      reaper.TrackCtl_SetToolTip("!-!-!-!-!-!  Live "..action.." ACTIVE  !-!-!-!-!-!", 800,20, true)
      dMedit()
    else
      DBG("No effect added")
    end
  end
end


function cleanUp()
  if helper_idx~=nil then
    reaper.TrackCtl_SetToolTip("--- Live "..action.." Inactive ---", 800,20, true)
    DBG("exit")
    --removeJSEffect(tr,jsfx) --set chunk causes glitches
                              --so can't remove input FX
    reaper.TrackFX_SetEnabled(tr, helper_idx, false) --just disable it
  end

end



jsfx.body=[[
desc:Script Note Getter

slider1:0<0,1,1{On,Off}>Active (eats notes)
slider2:0<0,127,1>Notes in buffer
slider3:0<0,1000,1>Note # (for script)
slider4:0<0,127,1>Output Pitch
slider5:0<0,15,1>Output channel
slider6:0<0,1,1>Trigger


@init
notebuf=0; //start pos of buffer
nb_width=3; //number of entries per note
buflen=0; //notes in buffer

function addRemoveNoteFromBuffer(m1,m2,m3)
( 
  s = m1&$xF0;
  c = m1&$xF; // channel
  n = m2;
  v = m3; // velocity
  
  init_buflen=buflen;
  
  i = -1;
  while // look for this note|channel already in the buffer
  (
    i = i+1;
    i < buflen && (notebuf[nb_width*i]|0 != n || notebuf[nb_width*i+1]|0 != c);
        );

    (s == $x90 && v > 0) ? // note-on, add to buffer
    ( 
      notebuf[nb_width*i] = n;
      notebuf[nb_width*i+1] = c;
      notebuf[nb_width*i+2] = v;
      i == buflen ? buflen = buflen+1;
    ) 
    : // note-off, remove from buffer
    (
      is_note_off=1;
      i < buflen ?
      (
         memcpy(notebuf+nb_width*i, notebuf+nb_width*(i+1),
                      nb_width*(buflen-i-1));  // delete the entry
         buflen = buflen-1;
       );
    );
    buflen==init_buflen ? -1; //return value for nothing added/removed
);


@slider
p=slider3*nb_width; //position in buffer
slider4=notebuf[p]; // note
slider5=notebuf[p+1]; // channel


@block
while (midirecv(offset,msg1,msg2,msg3))
(  
  slider1 ? (
    midisend(offset,msg1,msg2,msg3);
  ):( //eating notes
    msg1&$xF0==$x90 ? (
      addRemoveNoteFromBuffer(msg1,msg2,msg3);
    );
    msg1&$xF0==$x80 ? (
      addRemoveNoteFromBuffer(msg1,msg2,msg3)==-1 ? (
        //no note in buffer, so allow trailing note-offs through
        midisend(offset,msg1,msg2,msg3);
      );
    );
    slider2=buflen;
  );
)
]]


script_init=true
reaper.atexit(cleanUp)
script_init=false
reaper.Undo_BeginBlock()
prep()
reaper.Undo_EndBlock("DM Edit",-1)

Last edited by snooks; 08-14-2016 at 02:55 PM. Reason: forgot to change one delete_ahead to look_ahead
snooks is offline   Reply With Quote
Old 08-14-2016, 03:27 PM   #34
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

multichannel works great, thanks!

Quote:
Originally Posted by snooks View Post
That might be the look_ahead setting (formerly delete_ahead)...
Code:
local look_ahead=0.17 --so that notes don't sound
... try changing that to a higher value. The script is triggered X times a second whilst the project is playing so this looks ahead for an amount that should compensate for this.
cool, tested this a bunch. tried .17, .5, 1, 5, and 10. i saw the same behavior displayed below in this screencap:

http://imgur.com/a/79dBj

(i have no idea why imgur won't let me embed this)

note that this only applies on notes that end or pass the end of the midi item. moving the end of the note into the range of dmdelete allows the script to act on it.
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   Reply With Quote
Old 08-15-2016, 04:50 AM   #35
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

Hmm, this is a tricky one... the notes extending beyond the boundary have an end position before the start position. Although the end position is at a PPQ position of zero - the start of the item - when checking for this, deleting any other note also deletes this note.

Here's a test script that outputs the contents of the MIDI item so you can see yourself...

Code:
local function DBG(str)
  reaper.ShowConsoleMsg(str.."\n")
end


function outputMidi(tk)
  local idx=0
  local ok, sel, mute, startpos, endpos, chan, pitch, vel=reaper.MIDI_GetNote(tk, 0)
  while ok do
    DBG("idx: "..idx.."  startpos: "..startpos.."  endpos: "..endpos)
    idx=idx+1
    ok, sel, mute, startpos, endpos, chan, pitch, vel=reaper.MIDI_GetNote(tk, idx)
  end

end


function getMidiEditorTrackTake()
  local ame=reaper.MIDIEditor_GetActive()
  local mode=reaper.MIDIEditor_GetMode(ame)
  if mode > -1 then -- we are in a MIDI editor, -1 if ME not focused
    tk=reaper.MIDIEditor_GetTake(ame)
    --check that it's an actual take (in case of empty MIDI editor)
    if not reaper.ValidatePtr(tk, 'MediaItem_Take*') then return nil,nil end
    tr=reaper.GetMediaItemTake_Track(tk) 
  else
    return nil,nil
  end
  return tr,tk
end


function start()
  tr,tk=getMidiEditorTrackTake()
  if tk==nil then 
    if reaper.CountSelectedTracks()>0 then
        tr=reaper.GetSelectedTrack(0,0)
        was_recording = reaper.GetPlayState(0)&4 == 4 and true or false
        if was_recording then reaper.Main_OnCommand(1013, -1) end
        local _,t_name=reaper.GetSetMediaTrackInfo_String(tr,"P_NAME"," ",false)
        local _, ts=reaper.GetTrackState(tr)
        if ts&64==64 and ts&2==2 then -- is rec armed and selected
        local num_items=reaper.CountTrackMediaItems(tr)
        if num_items>0 then
          local l_start,l_end=reaper.GetSet_LoopTimeRange(false,true,0,0,true)
          local cur=reaper.GetCursorPosition()
          local i=1
          while i<=num_items do
            local it=reaper.GetTrackMediaItem(tr,i-1)
            local i_start=reaper.GetMediaItemInfo_Value(it,"D_POSITION")
            if math.floor(l_start*10)==math.floor(i_start*10) then
              tk=reaper.GetActiveTake(it)
              i=num_items
            end
            i=i+1
          end
        end
        
      end
    end
  end
  if tk~=nil and reaper.BR_IsTakeMidi(tk) then
    DBG("\n\nMIDI Contents\n-------------")
    outputMidi(tk)
  end
end

start()
The only thing that sets the end position of the note correctly is touching the note in the MIDI editor, you don't need to move it. Trying to 'touch' the note via a script doesn't work, it deletes the notes.

Calling MIDI_Sort also deletes the notes, maybe because it does that with invalid notes. Unless there's anything anyone can think about I think the solution lies with the devs perhaps making MIDI_Sort check for end positions of 0 PPQ for notes that start after that time and doing whatever the MIDI editor does when you touch one of the notes.
snooks is offline   Reply With Quote
Old 08-15-2016, 07:21 AM   #36
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

yeesh that's no fun.... one of those blurred lines between BR and FR. were i to make a thread addressed to the devs, describing and linking to this issue, where would you suggest posting?

issues like this is why i find myself often gluing track midi items to 1 per track

as a clumsy workaround, i wonder what'd happen if the script could check for such anomaly notes, and if yes, could add x amount of empty space to the midi item after the note? would that help, or would it just add duration to the note til the noteoff reached item end again?

when I've done this manually, i feel that I've seen both behaviors so I'm unclear as to what would happen
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   Reply With Quote
Old 08-15-2016, 12:18 PM   #37
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

I suppose a general bug here is that when notes like these exist, extending the unlooped item extends the note indefinitely. That's definitely not what was recorded, so it's defo a bug. Scripts not being able to do anything with these notes is just another symptom of the issue.

I think not recording the notes at the beginning of the loop and leaving the ends of the notes beyond the item boundary would be the way forward too.

I don't think extending the item via a script would result in consistently different behaviour from doing it via GUI. I'd give it a bash if I was fairly confident that it would, but I don't think it's worth trying.
snooks is offline   Reply With Quote
Old 08-16-2016, 08:19 AM   #38
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

excellent. I'll try to file the BR and will link you to it here so you can add any details i omitted. meanwhile i think I'm gonna do the thing i didn't want to do and start creating project long midi items per midi track and editing everything using arrange synced midi items.

i do a lot of midi editing by slicing and copying midi items from arrange view so this means a lot of regluing all items on track for me, but whatever works (edit, didn't work, deleting "held-over" notes in the beginning deletes noteoff for "end-of-loop" notes, creating infinitely long notes.

edit, ugh this noteoff/loopend behavior is terrible...
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.

Last edited by mccrabney; 08-16-2016 at 09:28 AM.
mccrabney is online now   Reply With Quote
Old 08-16-2016, 04:23 PM   #39
snooks
Banned
 
Join Date: Sep 2015
Posts: 1,650
Default

Yeah, it could do with being fixed. It would be good if we could get some way to remove Input FX natively too, which would complete the picture with using temporary JSFX with scripts.
snooks is offline   Reply With Quote
Old 08-16-2016, 06:38 PM   #40
mccrabney
Human being with feelings
 
mccrabney's Avatar
 
Join Date: Aug 2015
Posts: 3,671
Default

will test tomorrow to see how this effects the "Select" function. if that works OK, one could just follow it up with a "delete" action once desired notes are selected.
__________________
mccrabney scripts: MIDI edits from the Arrange screen ala jjos/MPC sequencer
|sis - - - anacru| isn't what we performed: pls no extra noteons in loop recording
| - - - - - anacru|sis <==this is what we actually performed.
mccrabney is online now   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 07:57 AM.


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