Go Back   Cockos Incorporated Forums > REAPER Forums > REAPER General Discussion Forum

Reply
 
Thread Tools Display Modes
Old 08-22-2016, 03:24 AM   #1
dream_of_the_night
Human being with feelings
 
Join Date: Mar 2013
Posts: 86
Default Midi editor: how to move notes back of "x" milliseconds?

As written in the title: how to move back some notes back of "x" milliseconds.
I know that there is the function "nudge" for midi items, but this move all the midi item back, while I need to move back just certain notes and leave others on beat.

Any help?
Thanks!
dream_of_the_night is offline   Reply With Quote
Old 08-22-2016, 04:44 AM   #2
hopi
Human being with feelings
 
hopi's Avatar
 
Join Date: Oct 2008
Location: Right Hear
Posts: 13,616
Default

good question and I recently was dealing with the same need...

could not get the nudge to work on selected notes, but I wish it did...

so I did it manually by turning off the snap to grid, zooming way the hell in and moving the notes until they played as I wanted...

Now I could imagine another way, that would use the nudge function:

duplicate the track...
select all the notes you want to nudge
invert the selection and delete all the other notes
now you have a midi item that can be nudged

go back to the original item and mute or remove the notes that you used in the nudged item...

now you could also use free item positioning to keep both these midi items on the same track if you like...

or you could re-combine them into one item
__________________
...should be fixed for the next build... http://tinyurl.com/cr7o7yl
https://soundcloud.com/hopikiva/angel-rain
hopi is offline   Reply With Quote
Old 08-22-2016, 05:19 AM   #3
dream_of_the_night
Human being with feelings
 
Join Date: Mar 2013
Posts: 86
Default

Thanks!
That's a good workaround...but it's a little time consuming
dream_of_the_night is offline   Reply With Quote
Old 08-22-2016, 08:33 AM   #4
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 2,153
Default

Quote:
Originally Posted by dream_of_the_night View Post
As written in the title: how to move back some notes back of "x" milliseconds.
I know that there is the function "nudge" for midi items, but this move all the midi item back, while I need to move back just certain notes and leave others on beat.
Is the BPM constant? If it is, you can convert the milliseconds to ticks and use the Properties popup window to change the position of the selected notes. (For example, enter "-0.0.480" or "+0.0.480" in the Position field to move the notes back or forward 1 eighth note, respectively.)

EDIT:
What to do if the BPM changes? I don't know if there is a better, native method, but sometimes it is easier to just write a quick script:

Code:
-- Quick script that is intended to nudge selected notes in the active take
--    by a user-defined number of milliseconds
-- WARNING: Do not use if there are overlapping notes in the take, since 
--    ReaScripts are not compatible with such notes.

reaper.Undo_BeginBlock()

editor = reaper.MIDIEditor_GetActive()
take = reaper.MIDIEditor_GetTake(editor)
reaper.MIDI_Sort(take)

repeat
    OKorCancel, userTime = reaper.GetUserInputs("Nudge notes by millisecs", 1, "Time (in milliseconds)", "0")
until OKorCancel == false or (OKorCancel == true and type(tonumber(userTime)) == "number")

if OKorCancel == false then 
    return(0) 
else
    nudgeTime = tonumber(userTime) / 1000
end

i = reaper.MIDI_EnumSelNotes(take, -1)
while i ~= -1 do
    noteOK, selected, _, noteStartPPQ, noteEndPPQ, _, _, _ = reaper.MIDI_GetNote(take, i)
    if noteOK and selected then
        noteStartTime = reaper.MIDI_GetProjTimeFromPPQPos(take, noteStartPPQ)
        newNoteStartPPQ = reaper.MIDI_GetPPQPosFromProjTime(take, noteStartTime + nudgeTime)
        newNoteEndPPQ = noteEndPPQ + (newNoteStartPPQ - noteStartPPQ)
        reaper.MIDI_SetNote(take, i, nil, nil, newNoteStartPPQ, newNoteEndPPQ, nil, nil, nil, true)
    end
    i = reaper.MIDI_EnumSelNotes(take, i)
end

reaper.MIDI_Sort(take)

reaper.Undo_EndBlock("Nudge notes by milliseconds", -1)
EDIT 2: BTW, if you use the MIDI Inspector while working in the MIDI editor, you can choose to display note position info in time or seconds instead of the usual measure:beat:ticks.

Last edited by juliansader; 08-22-2016 at 11:47 AM.
juliansader is offline   Reply With Quote
Old 08-22-2016, 07:08 PM   #5
hopi
Human being with feelings
 
hopi's Avatar
 
Join Date: Oct 2008
Location: Right Hear
Posts: 13,616
Default

nice one julian....

so if that script works as I believe it would, is it possible have one where the user has a slider of entry field to set the number of milisecs [left or right] while working ... in other words much like the normal nudge window?

I know so little about script creation code... sorry for the dumbness
__________________
...should be fixed for the next build... http://tinyurl.com/cr7o7yl
https://soundcloud.com/hopikiva/angel-rain
hopi is offline   Reply With Quote
Old 08-22-2016, 11:59 PM   #6
dream_of_the_night
Human being with feelings
 
Join Date: Mar 2013
Posts: 86
Default

Thank you!
But because I'm a little dumb with computers I can't get the script work, probably because when I donwnloaded the file it was saved .txt and not .lua so that I cannot load the script in the action list. :/
dream_of_the_night is offline   Reply With Quote
Old 08-23-2016, 12:12 AM   #7
vanhaze
Human being with feelings
 
vanhaze's Avatar
 
Join Date: Jul 2012
Location: Netherlands
Posts: 4,662
Default

If you're on mac:

Click on the script.
Hit CMD key plus I key.
In "Name & Extension" area, change .txt for .lua
Then click "use .lua" in the window that appears.
vanhaze is offline   Reply With Quote
Old 08-23-2016, 12:13 AM   #8
dream_of_the_night
Human being with feelings
 
Join Date: Mar 2013
Posts: 86
Default

I0m on windows 10
dream_of_the_night is offline   Reply With Quote
Old 08-23-2016, 12:17 AM   #9
vanhaze
Human being with feelings
 
vanhaze's Avatar
 
Join Date: Jul 2012
Location: Netherlands
Posts: 4,662
Default

Right mouse click on the script.
choose "rename"
change .txt to .lua
confirm that you want to rename.
vanhaze is offline   Reply With Quote
Old 08-23-2016, 12:24 AM   #10
dream_of_the_night
Human being with feelings
 
Join Date: Mar 2013
Posts: 86
Default

It doesn't work the same, it saves the new file ".lua.text"
dream_of_the_night is offline   Reply With Quote
Old 08-23-2016, 12:28 AM   #11
dream_of_the_night
Human being with feelings
 
Join Date: Mar 2013
Posts: 86
Default

Solved using "notepad"
dream_of_the_night is offline   Reply With Quote
Old 08-23-2016, 12:38 AM   #12
vanhaze
Human being with feelings
 
vanhaze's Avatar
 
Join Date: Jul 2012
Location: Netherlands
Posts: 4,662
Default

Good for you !
vanhaze is offline   Reply With Quote
Old 08-23-2016, 03:21 AM   #13
dream_of_the_night
Human being with feelings
 
Join Date: Mar 2013
Posts: 86
Default

Quote:
Originally Posted by juliansader View Post
Is the BPM constant? If it is, you can convert the milliseconds to ticks and use the Properties popup window to change the position of the selected notes. (For example, enter "-0.0.480" or "+0.0.480" in the Position field to move the notes back or forward 1 eighth note, respectively.)

EDIT:
What to do if the BPM changes? I don't know if there is a better, native method, but sometimes it is easier to just write a quick script:

Code:
-- Quick script that is intended to nudge selected notes in the active take
--    by a user-defined number of milliseconds
-- WARNING: Do not use if there are overlapping notes in the take, since 
--    ReaScripts are not compatible with such notes.

reaper.Undo_BeginBlock()

editor = reaper.MIDIEditor_GetActive()
take = reaper.MIDIEditor_GetTake(editor)
reaper.MIDI_Sort(take)

repeat
    OKorCancel, userTime = reaper.GetUserInputs("Nudge notes by millisecs", 1, "Time (in milliseconds)", "0")
until OKorCancel == false or (OKorCancel == true and type(tonumber(userTime)) == "number")

if OKorCancel == false then 
    return(0) 
else
    nudgeTime = tonumber(userTime) / 1000
end

i = reaper.MIDI_EnumSelNotes(take, -1)
while i ~= -1 do
    noteOK, selected, _, noteStartPPQ, noteEndPPQ, _, _, _ = reaper.MIDI_GetNote(take, i)
    if noteOK and selected then
        noteStartTime = reaper.MIDI_GetProjTimeFromPPQPos(take, noteStartPPQ)
        newNoteStartPPQ = reaper.MIDI_GetPPQPosFromProjTime(take, noteStartTime + nudgeTime)
        newNoteEndPPQ = noteEndPPQ + (newNoteStartPPQ - noteStartPPQ)
        reaper.MIDI_SetNote(take, i, nil, nil, newNoteStartPPQ, newNoteEndPPQ, nil, nil, nil, true)
    end
    i = reaper.MIDI_EnumSelNotes(take, i)
end

reaper.MIDI_Sort(take)

reaper.Undo_EndBlock("Nudge notes by milliseconds", -1)
EDIT 2: BTW, if you use the MIDI Inspector while working in the MIDI editor, you can choose to display note position info in time or seconds instead of the usual measure:beat:ticks.
Thank you very much, the script works nicely and just what I was looking for!

Can I share your script in other forums?
dream_of_the_night is offline   Reply With Quote
Old 08-24-2016, 10:43 PM   #14
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 2,153
Default

Quote:
Originally Posted by hopi View Post
so if that script works as I believe it would, is it possible have one where the user has a slider of entry field to set the number of milisecs [left or right] while working ... in other words much like the normal nudge window
This would indeed be possible. (It will require a GUI with a slider, though, which is somewhat laborious to do in Lua.) The normal nudge window does not have a slider, however - would a slider offer an advantage?

Quote:
Originally Posted by dream_of_the_night View Post
Thank you very much, the script works nicely and just what I was looking for!

Can I share your script in other forums?
You are welcome to share it anywhere!

(BTW, the script preserves the notes' *beats* (PPQ) lengths, not their *time* (millisecond) lengths. If it would be more appropriate to preserve their time lengths instead, it would be easy to adapt the script.)
juliansader is offline   Reply With Quote
Old 08-26-2016, 08:15 AM   #15
dream_of_the_night
Human being with feelings
 
Join Date: Mar 2013
Posts: 86
Default

The script works just as it should: I use it to move back true legato samples
dream_of_the_night is offline   Reply With Quote
Old 09-22-2016, 07:51 AM   #16
TheWhistler
Human being with feelings
 
TheWhistler's Avatar
 
Join Date: Nov 2010
Location: Deep in the pocket...
Posts: 1,030
Default Mouse-Modifier

This is one of the functions I do miss a bit from Cubase.
You could choose a tool (from a toolbox) which works like a mouse-modifier.
The cursor turned into a shoe and the notes could be kicked around at a small amount of time.
I never worried about that amount of time, it always worked great for me.

Would a script be capable of that (or two scripts for left/right) ?
TheWhistler is online now   Reply With Quote
Old 02-20-2018, 03:38 PM   #17
Xaviez
Human being with feelings
 
Join Date: May 2016
Posts: 14
Default

Quote:
Originally Posted by juliansader View Post
(BTW, the script preserves the notes' *beats* (PPQ) lengths, not their *time* (millisecond) lengths. If it would be more appropriate to preserve their time lengths instead, it would be easy to adapt the script.)
First off, sorry to everyone for the massive necro!

Second, thank you @juliansader for your massive contributions to the Reaper community, I use a couple of your scripts and they rock!

Now regarding this script and what you mentioned in my quote above:
The script nearly does what I want it to, and very conveniently too!
But, if you look at this GIF below, here I have 3 notes and I would want to extend the last two notes so they start 125ms earlier for the legato transition, problem is it moves the notes rather than extend them;


So this way the second note ends too early and there's no legato transition between note 2 and 3.

You mention it being easy to change how the script preserves the notes, would it be possible to have it preserve the note end entirely and merely stretch/nudge the note start?
Xaviez is offline   Reply With Quote
Old 02-20-2018, 05:26 PM   #18
hopi
Human being with feelings
 
hopi's Avatar
 
Join Date: Oct 2008
Location: Right Hear
Posts: 13,616
Default

Julian.... thanks for that lua [yet again you are so kind]

I am wondering if it would be hard to adapt a version of it that would work in the Inline Editor?

The poor little orphan Inline Editor ...so kewl and soooo much in need of some serious DEV love
__________________
...should be fixed for the next build... http://tinyurl.com/cr7o7yl
https://soundcloud.com/hopikiva/angel-rain
hopi is offline   Reply With Quote
Old 05-15-2018, 05:11 AM   #19
Infidel
Human being with feelings
 
Infidel's Avatar
 
Join Date: May 2011
Posts: 148
Default

+1 bump!!!!!!!
Infidel is offline   Reply With Quote
Old 05-15-2018, 11:44 AM   #20
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 2,153
Default

Quote:
Originally Posted by hopi View Post
I am wondering if it would be hard to adapt a version of it that would work in the Inline Editor?
Sorry that I didn't see this post before.

It would be easy to adapt the script for the inline MIDI editor. The only problem is that scripts don't know which inline editor is focused, so it is not straightforward to run scripts for the inline editor from the Actions list. (Please bump the FR: ReaScript: API functions to get and set focus on an item in the arrange view.) The script must therefore either work with
1) the editor under the mouse, or
2) the selected item(s).

Here are both alternatives:
Code:
-- Quick script that is intended to nudge selected notes in
--    ALL OPEN INLINE MIDI EDITORS IN ALL SELECTED ITEMS
--    by a user-defined number of milliseconds
-- WARNING: Do not use if there are overlapping notes in the take, since 
--    ReaScripts are not compatible with such notes.


repeat
    OKorCancel, userTime = reaper.GetUserInputs("Nudge notes by millisecs", 1, "Time (in milliseconds)", "0")
until OKorCancel == false or (OKorCancel == true and type(tonumber(userTime)) == "number")

if OKorCancel == false then 
    reaper.defer(function() end) -- Prevent unnecessary undo points
    return(0) 
else
    nudgeTime = tonumber(userTime) / 1000
end


reaper.Undo_BeginBlock2(0)

for i = 0, reaper.CountSelectedMediaItems(0)-1 do
    item = reaper.GetSelectedMediaItem(0, i)
    for t = 0, reaper.CountTakes(item)-1 do
        take = reaper.GetTake(item, t)
        if reaper.BR_IsMidiOpenInInlineEditor(take) then     
            reaper.MIDI_Sort(take)
            n = reaper.MIDI_EnumSelNotes(take, -1)
            while n ~= -1 do
                noteOK, selected, _, noteStartPPQ, noteEndPPQ, _, _, _ = reaper.MIDI_GetNote(take, n)
                if noteOK and selected then
                    noteStartTime = reaper.MIDI_GetProjTimeFromPPQPos(take, noteStartPPQ)
                    newNoteStartPPQ = reaper.MIDI_GetPPQPosFromProjTime(take, noteStartTime + nudgeTime)
                    newNoteEndPPQ = newNoteStartPPQ + (noteEndPPQ - noteStartPPQ)
                    reaper.MIDI_SetNote(take, n, nil, nil, newNoteStartPPQ, newNoteEndPPQ, nil, nil, nil, true)
                end
                n = reaper.MIDI_EnumSelNotes(take, n)
            end
            reaper.MIDI_Sort(take)
        end -- if reaper.BR_IsMidiOpenInInlineEditor(take)
    end -- for t = 0, reaper.CountTakes(item)-1
end -- for i = 0, reaper.CountSelectedMediaItems(0)-1


reaper.Undo_EndBlock2(0, "Nudge notes by milliseconds", -1)
Code:
-- Quick script that is intended to nudge selected notes in the 
--    INLINE (OR MAIN) MIDI EDITOR UNDER THE MOUSE
--    by a user-defined number of milliseconds
-- WARNING: Do not use if there are overlapping notes in the take, since 
--    ReaScripts are not compatible with such notes.

reaper.defer(function() end) -- Prevent unnecessary undo points

if not reaper.APIExists("SN_FocusMIDIEditor") then reaper.MB("This script requires a recent version of the SWS extension", "ERROR", 0) return end -- Old versions of SWS have bug in Context function

window, segment, details = reaper.BR_GetMouseCursorContext()
    if window ~= "midi_editor" then return end
editor, inlineEditor = reaper.BR_GetMouseCursorContext_MIDI()
if inlineEditor then
    take = reaper.BR_GetMouseCursorContext_Take()
else
    take = reaper.MIDIEditor_GetTake(editor) -- Also works on main MIDI editor
end


repeat
    OKorCancel, userTime = reaper.GetUserInputs("Nudge notes by millisecs", 1, "Time (in milliseconds)", "0")
until OKorCancel == false or (OKorCancel == true and type(tonumber(userTime)) == "number")

if OKorCancel == false then 
    return(0) 
else
    nudgeTime = tonumber(userTime) / 1000
end


reaper.Undo_BeginBlock2(0)

reaper.MIDI_Sort(take)
i = reaper.MIDI_EnumSelNotes(take, -1)
while i ~= -1 do
    noteOK, selected, _, noteStartPPQ, noteEndPPQ, _, _, _ = reaper.MIDI_GetNote(take, i)
    if noteOK and selected then
        noteStartTime = reaper.MIDI_GetProjTimeFromPPQPos(take, noteStartPPQ)
        newNoteStartPPQ = reaper.MIDI_GetPPQPosFromProjTime(take, noteStartTime + nudgeTime)
        newNoteEndPPQ = newNoteStartPPQ + (noteEndPPQ - noteStartPPQ)
        reaper.MIDI_SetNote(take, i, nil, nil, newNoteStartPPQ, newNoteEndPPQ, nil, nil, nil, true)
    end
    i = reaper.MIDI_EnumSelNotes(take, i)
end

reaper.MIDI_Sort(take)

reaper.Undo_EndBlock2(0, "Nudge notes by milliseconds", -1)

Quote:
The poor little orphan Inline Editor ...so kewl and soooo much in need of some serious DEV love
Indeed. I have tried to adapt my CC editing scripts for the inline MIDI editor, and also uploaded some channel and CC lane scripts.
juliansader is offline   Reply With Quote
Old 09-14-2018, 05:26 AM   #21
FlowDenny
Human being with feelings
 
FlowDenny's Avatar
 
Join Date: Jan 2008
Location: LI, NY
Posts: 15
Default

Julian,
Thank you so much for this script! I'm a Cinematic Strings user and this script is a part of my daily MIDI editing workflow!

I'm trying to make an action with a modified MPL script for setting fixed velocity for selected notes, and your MS MIDI Nudge. I've tried to modify your script with no luck. I'm still trying to learn how to script properly.

Is there a way to make your script nudge the selected MIDI note to a fixed MS value without asking for a prompt and having to input the desired amount?

Thanks for your time!
FlowDenny is offline   Reply With Quote
Old 09-14-2018, 01:23 PM   #22
hopi
Human being with feelings
 
hopi's Avatar
 
Join Date: Oct 2008
Location: Right Hear
Posts: 13,616
Default

Julian... in the second script for the Inline Editor...

when I have the item selected and the Inline Editor open and run that script nothing happens... I am expecting to get the entry field but I don't

Am I missing something???

I have the item selected and some of the notes selected...
__________________
...should be fixed for the next build... http://tinyurl.com/cr7o7yl
https://soundcloud.com/hopikiva/angel-rain
hopi is offline   Reply With Quote
Old 09-14-2018, 01:28 PM   #23
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 2,153
Default

Quote:
Originally Posted by hopi View Post
Julian... in the second script for the Inline Editor...

when I have the item selected and the Inline Editor open and run that script nothing happens... I am expecting to get the entry field but I don't
Try the first script -- the second one is for the editor under the mouse.

Code:
-- Quick script that is intended to nudge selected notes in the 
--    INLINE (OR MAIN) MIDI EDITOR UNDER THE MOUSE
juliansader is offline   Reply With Quote
Old 09-14-2018, 01:31 PM   #24
juliansader
Human being with feelings
 
Join Date: Jul 2009
Posts: 2,153
Default

Quote:
Originally Posted by FlowDenny View Post
Is there a way to make your script nudge the selected MIDI note to a fixed MS value without asking for a prompt and having to input the desired amount?
Sure, just replace these lines:
Code:
repeat
    OKorCancel, userTime = reaper.GetUserInputs("Nudge notes by millisecs", 1, "Time (in milliseconds)", "0")
until OKorCancel == false or (OKorCancel == true and type(tonumber(userTime)) == "number")

if OKorCancel == false then 
    reaper.defer(function() end) -- Prevent unnecessary undo points
    return(0) 
else
    nudgeTime = tonumber(userTime) / 1000
end
with (X is time in milliseconds)
Code:
nudgeTime = X/1000
juliansader is offline   Reply With Quote
Old 09-14-2018, 01:41 PM   #25
FlowDenny
Human being with feelings
 
FlowDenny's Avatar
 
Join Date: Jan 2008
Location: LI, NY
Posts: 15
Default

Quote:
Originally Posted by juliansader View Post
Sure, just replace these lines:
Code:
repeat
    OKorCancel, userTime = reaper.GetUserInputs("Nudge notes by millisecs", 1, "Time (in milliseconds)", "0")
until OKorCancel == false or (OKorCancel == true and type(tonumber(userTime)) == "number")

if OKorCancel == false then 
    reaper.defer(function() end) -- Prevent unnecessary undo points
    return(0) 
else
    nudgeTime = tonumber(userTime) / 1000
end
with (X is time in milliseconds)
Code:
nudgeTime = X/1000
Thank you for such a quick reply and sharing such great work!!!
It works perfect. Now I can create 3 different actions(with shortcuts) that adjust the MIDI notes velocity AND proper delay for CSS's legato! This is going to save me so much time and make working with CSS even better!!!

I also tried sending you a PM. I wanted to verify your donation info please.

Last edited by FlowDenny; 09-14-2018 at 01:50 PM. Reason: Unable to send user PM
FlowDenny is offline   Reply With Quote
Old 09-14-2018, 02:09 PM   #26
hopi
Human being with feelings
 
hopi's Avatar
 
Join Date: Oct 2008
Location: Right Hear
Posts: 13,616
Default

thanks Julian.... yeah that works OK now

funny how reaper sometimes brings up the correct set of actions for the Inline Editor when it is open and selected and other times brings up the actions for the Main instead...

EDIT: never mind ... seems to work OK now... maybe ghosts in the machine
__________________
...should be fixed for the next build... http://tinyurl.com/cr7o7yl
https://soundcloud.com/hopikiva/angel-rain

Last edited by hopi; 09-14-2018 at 02:18 PM.
hopi 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 07:36 AM.


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