|
|
|
10-02-2016, 10:56 AM
|
#1
|
Human being with feelings
Join Date: Apr 2010
Location: Cloud 37
Posts: 1,071
|
Humanize CC
So sometimes when I draw in CC data, I end up with some straight lines, and I'd rather they were a bit bumpy.
Could we add a CC humanize feature, so that my cc11 time selection could be 10% randomized, the same way velocity is?
|
|
|
10-02-2016, 11:39 AM
|
#2
|
Human being with feelings
Join Date: Jun 2015
Posts: 217
|
I'd add CC humanize, and by note change also, not on every CC.
|
|
|
10-09-2016, 04:20 AM
|
#3
|
Human being with feelings
Join Date: Jul 2009
Posts: 3,714
|
It would indeed be useful - and more intuitive - if CC humanization could be incorporated into the current "Humanize" function.
I find it curious that many of REAPER's niftiest functions are limited to notes, and exclude CCs. Several of my own FRs have also asked for CC equivalents of note functions.
Since this FR is unlikely to be implemented any time soon, here is a script that may help. Each time that the script is run, it randomizes the CC values a little:
Code:
--[[
ReaScript name: js_Humanize the values of selected CC events.lua
Version: 1.00
Author: juliansader
About:
# Description
Quick script to humanize (slightly randomize) the values of selected CC events.
]]
-- USER AREA
-- Settings that the user can cuztomize
randomType = "abs" -- "%", "percentage", "abs" or "absolute"
randomMaxChange = 1 -- Maximum change, as percentage or absolute value (integer)
-- End of USER AREA
-------------------
-----------------------------------
function newRandomValue(value, max)
if randomType == "%" or randomType:lower() == "percentage" then
value = value * (math.random(100-randomMaxChange, 100+randomMaxChange))/100
else
value = value + math.random(-randomMaxChange, randomMaxChange)
end
value = math.max(0, math.min(max, math.floor(value+0.5))) -- Ensure that value is within limits, and integer
return value
end
--------------------------------------
-- function main()
editor = reaper.MIDIEditor_GetActive()
if editor == nil then return end
take = reaper.MIDIEditor_GetTake(editor)
if not reaper.ValidatePtr2(0, take, "MediaItem_Take*") then return end
reaper.Undo_BeginBlock()
countCCs = 0
i = -1
repeat
i = reaper.MIDI_EnumSelCC(take, i)
if i ~= -1 then
ccOK, _, _, _, chanmsg, channel, msg2, msg3 = reaper.MIDI_GetCC(take, i)
if ccOK then
countCCs = countCCs + 1
local eventType = chanmsg>>4
-- Different CC types use different bytes to carry their values, so each must be handled separately
if eventType == 11 then -- 7-bit CC
reaper.MIDI_SetCC(take, i, nil, nil, nil, nil, nil, nil, newRandomValue(msg3, 127), true)
elseif eventType == 13 then -- Channel pressure
reaper.MIDI_SetCC(take, i, nil, nil, nil, nil, nil, newRandomValue(msg2, 127), nil, true)
elseif eventType == 14 then -- Pitch
reaper.MIDI_SetCC(take, i, nil, nil, nil, nil, nil, newRandomValue(msg2, 127), newRandomValue(msg3, 127), true)
end
end
end
until i == -1
reaper.Undo_EndBlock2(0, "Humanized the values of " .. tostring(countCCs) .. " CC events", -1)
|
|
|
10-12-2016, 05:37 AM
|
#4
|
Human being with feelings
Join Date: Apr 2010
Location: Cloud 37
Posts: 1,071
|
Oh wow, you've just introduced me to the whole new world of ReaScripts. Now I'm getting deep down the rabbit hole..
So... I copied this code into notepad, saved it as a .eel, load it into my action list, but when I try to run the action, I get an error.
Is this the wrong way to do it; I was following online ReaScript tutorials.
Last edited by Mr. PC; 10-12-2016 at 08:13 AM.
|
|
|
10-12-2016, 11:06 AM
|
#5
|
Human being with feelings
Join Date: Jul 2009
Posts: 3,714
|
Quote:
Originally Posted by Mr. PC
So... I copied this code into notepad, saved it as a .eel, load it into my action list, but when I try to run the action, I get an error.
|
It is actually a Lua script, not EEL, so if you save it as a .lua file, it should hopefully work OK.
|
|
|
06-23-2019, 03:47 AM
|
#6
|
Human being with feelings
Join Date: Jul 2009
Posts: 3,714
|
A request from another thread:
Quote:
Originally Posted by Dafarkias
Quick question about this https://forum.cockos.com/showthread.php?t=182146 script, it doesn't really work well with large amounts of CC data. What I want to do is use your "js_Insert linear or shaped ramps between selected CCs or pitches in lane under mouse.lua" script to basically virtualize half pedaling for recorded midi piano "64 Hold Pedal" data. It works great. But after that I want to run the (thousands of) CC's through a slight bit of randomization to complete the effect, but it kinda chokes and crashes most of the time.
Would you be interested, by chance, in compiling a CC randomization script using your blazing-fast chunk techniques and superior programming?
|
Here is an updated version of the humanize script that uses the new, fast MIDI_Get/SetAllEvts functions:
Code:
--[[
ReaScript name: js_Humanize the values of selected CC events.lua
Version: 2.00
Author: juliansader
Changelog:
- Much faster execution when many thousands of events are selected.
About:
# Description
Simple script to humanize (slightly randomize) the values of selected CC events or velocities.
]]
-- USER AREA
-- Settings that the user can customize
randomType = "abs" -- "%", "percentage", "abs" or "absolute"
randomMaxChange = 1 -- Maximum change, as percentage or absolute value (integer)
-- End of USER AREA
-------------------
local isRandomPercentage
-----------------------------------
function newRandomValue(value, min, max)
if isRandomPercentage then
value = value * (math.random(100-randomMaxChange, 100+randomMaxChange))/100
else
value = value + math.random(-randomMaxChange, randomMaxChange)
end
if value > max then value = max
elseif value < min then value = min
else value = (value+0.5)//1 -- Ensure that value is within limits, and integer
end
return value
end
--------------------------------------
-- function main()
isRandomPercentage = (randomType == "%" or randomType:lower():match("per"))
editor = reaper.MIDIEditor_GetActive()
if editor == nil then reaper.MB("Could not find any active MIDI editor.", "ERROR", 0) return end
take = reaper.MIDIEditor_GetTake(editor)
if not reaper.ValidatePtr2(0, take, "MediaItem_Take*") then reaper.MB("Active take is invalid.", "ERROR", 0) return end
item = reaper.GetMediaItemTake_Item(take)
if not reaper.ValidatePtr2(0, item, "MediaItem*") then reaper.MB("Could not determine valid item.", "ERROR", 0) return end
--reaper.Undo_BeginBlock()
local MIDIOK, MIDI = reaper.MIDI_GetAllEvts(take, "")
if not MIDIOK then reaper.MB("Could not load MIDI string.", "ERROR", 0) return end
local pos, savePos, countEvts = 1, 1, 0 -- pos is position in MIDI while parsing, savePos is next position from which substrings must be stored in tMIDI
local tMIDI = {} -- MIDI substrings will be stored in tMIDI, to be concatenated again later
while pos < #MIDI do
offset, flags, msg, pos = string.unpack("i4Bs4", MIDI, pos)
if flags&1 == 1 then
local eventType = (msg:byte(1))>>4
-- Different CC types use different bytes to carry their values, so each must be handled separately
if eventType == 11 then -- 7-bit CC
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg+1) .. string.char(newRandomValue(msg:byte(3), 0, 127)) .. msg:sub(4,nil)
savePos = pos
countEvts = countEvts + 1
elseif eventType == 13 then -- Channel pressure
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg) .. string.char(newRandomValue(msg:byte(2), 0, 127)) .. msg:sub(3,nil)
savePos = pos
countEvts = countEvts + 1
elseif eventType == 14 then -- Pitch
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg) .. string.char(newRandomValue(msg:byte(2), 0, 127))
.. string.char(newRandomValue(msg:byte(3), 0, 127))
.. msg:sub(4,nil)
savePos = pos
countEvts = countEvts + 1
elseif eventType == 9 and msg:byte(3) ~= 0 then -- Note-on velocity
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg+1) .. string.char(newRandomValue(msg:byte(3), 1, 127)) .. msg:sub(4,nil)
savePos = pos
countEvts = countEvts + 1
end
end
end
tMIDI[#tMIDI+1] = MIDI:sub(savePos, nil)
reaper.MIDI_SetAllEvts(take, table.concat(tMIDI))
--reaper.Undo_EndBlock2(0, "Randomized the values of " .. tostring(countEvts) .. " events", -1)
reaper.Undo_OnStateChange_Item(0, "Randomized the values of " .. tostring(countEvts) .. " events", item)
|
|
|
06-29-2019, 03:24 AM
|
#7
|
Human being with feelings
Join Date: Feb 2019
Location: Southern Vermont
Posts: 864
|
You are the bees knees!
Thank you!
[Postscript] There are a lot of awesome programmers in this forum I probably owe more than gratitude for their work, but at my earliest convenience I will attempt to PayPal you a few beers. It's the least I can do.
Last edited by Dafarkias; 06-29-2019 at 10:18 AM.
|
|
|
07-02-2019, 11:26 AM
|
#8
|
Human being with feelings
Join Date: Feb 2019
Location: Southern Vermont
Posts: 864
|
Quote:
Originally Posted by juliansader
A request from another thread:
Here is an updated version of the humanize script that uses the new, fast MIDI_Get/SetAllEvts functions:
|
Quick question if you don't mind...
I'm trying to slightly tweak this script so that it will randomize midi CC over time within a certain allotted range, but I'm getting an error message:
Randomize Value of Selected CC (by Time).lua:88: bad argument #1 to 'char' (value out of range)
Here's the code:
Code:
--[[
ReaScript name: js_Humanize the values of selected CC events.lua
Version: 2.00
Author: juliansader
Changelog:
- Much faster execution when many thousands of events are selected.
About:
# Description
Simple script to humanize (slightly randomize) the values of selected CC events or velocities.
]]
local isRandomPercentage
local randomType = "absolute"
local percent = 0
local range = 0
local number = 0
::incorrect::
local _, dummy = reaper.GetUserInputs( "Randomize Chance:", 1, "", "1-100%" )
if dummy ~= "1-100%" then dummy = tonumber(dummy)
if type(dummy) ~= 'number' then reaper.MB( "Ensure that input is 1-100%", "[error]", 0 ) goto incorrect end
if dummy < 1 or dummy > 100 then reaper.MB( "Ensure that input is 1-100%", "[error]", 0 ) goto incorrect end
percent = dummy
else reaper.MB( "Script canceled.", "[message]", 0 ) goto exit end
::incorrect2::
_, dummy = reaper.GetUserInputs( "Randomize Range:", 1, "", "1-126" )
if dummy ~= "1-126" then dummy = tonumber(dummy)
if type(dummy) ~= 'number' then reaper.MB( "Ensure that input 1-126", "[error]", 0 ) goto incorrect2 end
if dummy < 1 or dummy > 126 then reaper.MB( "Ensure that input 1-126", "[error]", 0 ) goto incorrect2 end
range = dummy
else reaper.MB( "Script canceled.", "[message]", 0 ) goto exit end
-----------------------------------
function newRandomValue(value, min, max)
if math.random(0, 100) <= percent
then
if math.random(0, 100) > 50
then
if number < (range/2) and (value+number+1) < max
then
number = number + 1
end
else
if number > (0-(range/2)) and (value+number-1) > min
then
number = number - 1
end
end
end
return (value+number)
end
--------------------------------------
-- function main()
isRandomPercentage = (randomType == "%" or randomType:lower():match("per"))
editor = reaper.MIDIEditor_GetActive()
if editor == nil then reaper.MB("Could not find any active MIDI editor.", "ERROR", 0) return end
take = reaper.MIDIEditor_GetTake(editor)
if not reaper.ValidatePtr2(0, take, "MediaItem_Take*") then reaper.MB("Active take is invalid.", "ERROR", 0) return end
item = reaper.GetMediaItemTake_Item(take)
if not reaper.ValidatePtr2(0, item, "MediaItem*") then reaper.MB("Could not determine valid item.", "ERROR", 0) return end
--reaper.Undo_BeginBlock()
local MIDIOK, MIDI = reaper.MIDI_GetAllEvts(take, "")
if not MIDIOK then reaper.MB("Could not load MIDI string.", "ERROR", 0) return end
local pos, savePos, countEvts = 1, 1, 0 -- pos is position in MIDI while parsing, savePos is next position from which substrings must be stored in tMIDI
local tMIDI = {} -- MIDI substrings will be stored in tMIDI, to be concatenated again later
while pos < #MIDI do
offset, flags, msg, pos = string.unpack("i4Bs4", MIDI, pos)
if flags&1 == 1 then
local eventType = (msg:byte(1))>>4
-- Different CC types use different bytes to carry their values, so each must be handled separately
if eventType == 11 then -- 7-bit CC
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg+1) .. string.char(newRandomValue(msg:byte(3), 0, 127)) .. msg:sub(4,nil)
savePos = pos
countEvts = countEvts + 1
elseif eventType == 13 then -- Channel pressure
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg) .. string.char(newRandomValue(msg:byte(2), 0, 127)) .. msg:sub(3,nil)
savePos = pos
countEvts = countEvts + 1
elseif eventType == 14 then -- Pitch
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg) .. string.char(newRandomValue(msg:byte(2), 0, 127))
.. string.char(newRandomValue(msg:byte(3), 0, 127))
.. msg:sub(4,nil)
savePos = pos
countEvts = countEvts + 1
elseif eventType == 9 and msg:byte(3) ~= 0 then -- Note-on velocity
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg+1) .. string.char(newRandomValue(msg:byte(3), 1, 127)) .. msg:sub(4,nil)
savePos = pos
countEvts = countEvts + 1
end
end
end
tMIDI[#tMIDI+1] = MIDI:sub(savePos, nil)
reaper.MIDI_SetAllEvts(take, table.concat(tMIDI))
--reaper.Undo_EndBlock2(0, "Randomized the values of " .. tostring(countEvts) .. " events", -1)
reaper.Undo_OnStateChange_Item(0, "Randomized the values of " .. tostring(countEvts) .. " events", item)
::exit::
[ADD., 7/2/19]
Argghhhhh!
I figured out why I was getting the message right after posting this... Darnit. Sorry!
The issue was in the return value of the NewRandomValue function...
Corrected code:
Code:
function newRandomValue(value, min, max)
if math.random(0, 100) <= percent
then
if math.random(0, 100) > 50
then
if number < (range/2)
then
number = number + 1
end
else
if number > (0-(range/2))
then
number = number - 1
end
end
end
if (value+number) >= max then return max
elseif (value+number) <= min then return min
else return (value+number) end
end
Last edited by Dafarkias; 07-02-2019 at 11:50 AM.
|
|
|
11-30-2019, 03:28 AM
|
#9
|
Human being with feelings
Join Date: Apr 2016
Location: Beijing, China
Posts: 215
|
I have a question regarding humanization with CC curves.
I've been looking for ways to humanize CC positions as well as curve tension. I noticed that the way you alter the original CC value is by using string.sub() function to directly replace the original value with new string.char(). However if we want to alter all other parameters or creating new events, it might be necessary to add new events using string.pack(). What would be the format for packing event data into MIDI event message, with the new meta-event of CC curves?
|
|
|
09-19-2020, 10:43 PM
|
#10
|
Human being with feelings
Join Date: Feb 2013
Location: Chicago, IL
Posts: 33
|
Wow, this is amazing...really. Thank you for this! This community is really something else.
|
|
|
04-11-2021, 12:46 AM
|
#11
|
Human being with feelings
Join Date: Jan 2021
Location: the sweet spot
Posts: 22
|
Quote:
Originally Posted by Dafarkias
Code:
--[[
ReaScript name: js_Humanize the values of selected CC events.lua
Version: 2.00
Author: juliansader
Changelog:
- Much faster execution when many thousands of events are selected.
About:
# Description
Simple script to humanize (slightly randomize) the values of selected CC events or velocities.
]]
local isRandomPercentage
local randomType = "absolute"
local percent = 0
local range = 0
local number = 0
::incorrect::
local _, dummy = reaper.GetUserInputs( "Randomize Chance:", 1, "", "1-100%" )
if dummy ~= "1-100%" then dummy = tonumber(dummy)
if type(dummy) ~= 'number' then reaper.MB( "Ensure that input is 1-100%", "[error]", 0 ) goto incorrect end
if dummy < 1 or dummy > 100 then reaper.MB( "Ensure that input is 1-100%", "[error]", 0 ) goto incorrect end
percent = dummy
else reaper.MB( "Script canceled.", "[message]", 0 ) goto exit end
::incorrect2::
_, dummy = reaper.GetUserInputs( "Randomize Range:", 1, "", "1-126" )
if dummy ~= "1-126" then dummy = tonumber(dummy)
if type(dummy) ~= 'number' then reaper.MB( "Ensure that input 1-126", "[error]", 0 ) goto incorrect2 end
if dummy < 1 or dummy > 126 then reaper.MB( "Ensure that input 1-126", "[error]", 0 ) goto incorrect2 end
range = dummy
else reaper.MB( "Script canceled.", "[message]", 0 ) goto exit end
-----------------------------------
function newRandomValue(value, min, max)
if math.random(0, 100) <= percent
then
if math.random(0, 100) > 50
then
if number < (range/2) and (value+number+1) < max
then
number = number + 1
end
else
if number > (0-(range/2)) and (value+number-1) > min
then
number = number - 1
end
end
end
return (value+number)
end
--------------------------------------
-- function main()
isRandomPercentage = (randomType == "%" or randomType:lower():match("per"))
editor = reaper.MIDIEditor_GetActive()
if editor == nil then reaper.MB("Could not find any active MIDI editor.", "ERROR", 0) return end
take = reaper.MIDIEditor_GetTake(editor)
if not reaper.ValidatePtr2(0, take, "MediaItem_Take*") then reaper.MB("Active take is invalid.", "ERROR", 0) return end
item = reaper.GetMediaItemTake_Item(take)
if not reaper.ValidatePtr2(0, item, "MediaItem*") then reaper.MB("Could not determine valid item.", "ERROR", 0) return end
--reaper.Undo_BeginBlock()
local MIDIOK, MIDI = reaper.MIDI_GetAllEvts(take, "")
if not MIDIOK then reaper.MB("Could not load MIDI string.", "ERROR", 0) return end
local pos, savePos, countEvts = 1, 1, 0 -- pos is position in MIDI while parsing, savePos is next position from which substrings must be stored in tMIDI
local tMIDI = {} -- MIDI substrings will be stored in tMIDI, to be concatenated again later
while pos < #MIDI do
offset, flags, msg, pos = string.unpack("i4Bs4", MIDI, pos)
if flags&1 == 1 then
local eventType = (msg:byte(1))>>4
-- Different CC types use different bytes to carry their values, so each must be handled separately
if eventType == 11 then -- 7-bit CC
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg+1) .. string.char(newRandomValue(msg:byte(3), 0, 127)) .. msg:sub(4,nil)
savePos = pos
countEvts = countEvts + 1
elseif eventType == 13 then -- Channel pressure
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg) .. string.char(newRandomValue(msg:byte(2), 0, 127)) .. msg:sub(3,nil)
savePos = pos
countEvts = countEvts + 1
elseif eventType == 14 then -- Pitch
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg) .. string.char(newRandomValue(msg:byte(2), 0, 127))
.. string.char(newRandomValue(msg:byte(3), 0, 127))
.. msg:sub(4,nil)
savePos = pos
countEvts = countEvts + 1
elseif eventType == 9 and msg:byte(3) ~= 0 then -- Note-on velocity
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg+1) .. string.char(newRandomValue(msg:byte(3), 1, 127)) .. msg:sub(4,nil)
savePos = pos
countEvts = countEvts + 1
end
end
end
tMIDI[#tMIDI+1] = MIDI:sub(savePos, nil)
reaper.MIDI_SetAllEvts(take, table.concat(tMIDI))
--reaper.Undo_EndBlock2(0, "Randomized the values of " .. tostring(countEvts) .. " events", -1)
reaper.Undo_OnStateChange_Item(0, "Randomized the values of " .. tostring(countEvts) .. " events", item)
::exit::
[...]
Code:
function newRandomValue(value, min, max)
if math.random(0, 100) <= percent
then
if math.random(0, 100) > 50
then
if number < (range/2)
then
number = number + 1
end
else
if number > (0-(range/2))
then
number = number - 1
end
end
end
if (value+number) >= max then return max
elseif (value+number) <= min then return min
else return (value+number) end
end
|
Hi Dafarkias, I am deeply interested by this script but I keep failing at having it work!
I've saved it with as a .lua file (with the latter correction), imported it in the "Midi editor" section of the action list and given it a keyboard shortcut.
When I select CC events and press the shortcut, I'm asked to confirm that radomise chance is "1-100%", I click "OK" and get the message "Script canceled."
Am I doing something wrong?
Thanks.
|
|
|
04-11-2021, 01:48 PM
|
#12
|
Human being with feelings
Join Date: Feb 2019
Location: Southern Vermont
Posts: 864
|
Yeah. It's strange, a little.
First input you enter the percentage chance in which a note will be randomized, e.g., 50
Then you enter the total amount of drift you want to be randomize, e.g., 10
Then the script will randomize the selected notes by the percentage given, either plus one or negative one, but within the given range.
So there would be a 50% that any given note would be increased or decreased by an amount no less than -5 and no greater than 5, but never by an incremental jump of more than 1.
This creates a smoother humanization
|
|
|
04-12-2021, 01:52 AM
|
#13
|
Human being with feelings
Join Date: Jan 2021
Location: the sweet spot
Posts: 22
|
It works indeed! A thousands thanks!
(I was stupidly inputting "1-100%" instead of any value inside this range...)
|
|
|
04-14-2021, 08:08 AM
|
#14
|
Human being with feelings
Join Date: Nov 2016
Posts: 8
|
This is exactly what I was looking for! Big thank you!!
Let's say I wanted to make an action to apply the script at probability 50%, value 10 without it asking me for the numbers. How would you suggest I edit the script?
|
|
|
04-14-2021, 05:15 PM
|
#15
|
Human being with feelings
Join Date: Feb 2019
Location: Southern Vermont
Posts: 864
|
Feel free to PM me. I'd prefer not to continue to distract this thread (which is something I have a bad habit doing)
|
|
|
02-15-2022, 10:38 PM
|
#16
|
Human being with feelings
Join Date: Nov 2012
Posts: 178
|
Quote:
Originally Posted by juliansader
It would indeed be useful - and more intuitive - if CC humanization could be incorporated into the current "Humanize" function.
I find it curious that many of REAPER's niftiest functions are limited to notes, and exclude CCs. Several of my own FRs have also asked for CC equivalents of note functions.
Since this FR is unlikely to be implemented any time soon, here is a script that may help. Each time that the script is run, it randomizes the CC values a little:
Code:
--[[
ReaScript name: js_Humanize the values of selected CC events.lua
Version: 1.00
Author: juliansader
About:
# Description
Quick script to humanize (slightly randomize) the values of selected CC events.
]]
-- USER AREA
-- Settings that the user can cuztomize
randomType = "abs" -- "%", "percentage", "abs" or "absolute"
randomMaxChange = 1 -- Maximum change, as percentage or absolute value (integer)
-- End of USER AREA
-------------------
-----------------------------------
function newRandomValue(value, max)
if randomType == "%" or randomType:lower() == "percentage" then
value = value * (math.random(100-randomMaxChange, 100+randomMaxChange))/100
else
value = value + math.random(-randomMaxChange, randomMaxChange)
end
value = math.max(0, math.min(max, math.floor(value+0.5))) -- Ensure that value is within limits, and integer
return value
end
--------------------------------------
-- function main()
editor = reaper.MIDIEditor_GetActive()
if editor == nil then return end
take = reaper.MIDIEditor_GetTake(editor)
if not reaper.ValidatePtr2(0, take, "MediaItem_Take*") then return end
reaper.Undo_BeginBlock()
countCCs = 0
i = -1
repeat
i = reaper.MIDI_EnumSelCC(take, i)
if i ~= -1 then
ccOK, _, _, _, chanmsg, channel, msg2, msg3 = reaper.MIDI_GetCC(take, i)
if ccOK then
countCCs = countCCs + 1
local eventType = chanmsg>>4
-- Different CC types use different bytes to carry their values, so each must be handled separately
if eventType == 11 then -- 7-bit CC
reaper.MIDI_SetCC(take, i, nil, nil, nil, nil, nil, nil, newRandomValue(msg3, 127), true)
elseif eventType == 13 then -- Channel pressure
reaper.MIDI_SetCC(take, i, nil, nil, nil, nil, nil, newRandomValue(msg2, 127), nil, true)
elseif eventType == 14 then -- Pitch
reaper.MIDI_SetCC(take, i, nil, nil, nil, nil, nil, newRandomValue(msg2, 127), newRandomValue(msg3, 127), true)
end
end
end
until i == -1
reaper.Undo_EndBlock2(0, "Humanized the values of " .. tostring(countCCs) .. " CC events", -1)
|
Is this a destructive script or an FX plugin? I don't want to actually change my CC values
|
|
|
06-22-2022, 06:14 AM
|
#17
|
Human being with feelings
Join Date: Sep 2018
Posts: 34
|
I've been waiting to have this feature in Reaper for years, where you could set how much the notes to humanize both in value and in time, and it would apply the humanization to all the selected notes across different MIDI items, like we have in the case of notes (which isn't possible with the scripts above).
Hope the team (or someone great at scripting) can implement this soon.
Last edited by Ikary; 06-22-2022 at 06:34 AM.
|
|
|
06-22-2022, 06:36 AM
|
#18
|
Human being with feelings
Join Date: Sep 2018
Posts: 34
|
Quote:
Originally Posted by juliansader
A request from another thread:
Here is an updated version of the humanize script that uses the new, fast MIDI_Get/SetAllEvts functions:
Code:
--[[
ReaScript name: js_Humanize the values of selected CC events.lua
Version: 2.00
Author: juliansader
Changelog:
- Much faster execution when many thousands of events are selected.
About:
# Description
Simple script to humanize (slightly randomize) the values of selected CC events or velocities.
]]
-- USER AREA
-- Settings that the user can customize
randomType = "abs" -- "%", "percentage", "abs" or "absolute"
randomMaxChange = 1 -- Maximum change, as percentage or absolute value (integer)
-- End of USER AREA
-------------------
local isRandomPercentage
-----------------------------------
function newRandomValue(value, min, max)
if isRandomPercentage then
value = value * (math.random(100-randomMaxChange, 100+randomMaxChange))/100
else
value = value + math.random(-randomMaxChange, randomMaxChange)
end
if value > max then value = max
elseif value < min then value = min
else value = (value+0.5)//1 -- Ensure that value is within limits, and integer
end
return value
end
--------------------------------------
-- function main()
isRandomPercentage = (randomType == "%" or randomType:lower():match("per"))
editor = reaper.MIDIEditor_GetActive()
if editor == nil then reaper.MB("Could not find any active MIDI editor.", "ERROR", 0) return end
take = reaper.MIDIEditor_GetTake(editor)
if not reaper.ValidatePtr2(0, take, "MediaItem_Take*") then reaper.MB("Active take is invalid.", "ERROR", 0) return end
item = reaper.GetMediaItemTake_Item(take)
if not reaper.ValidatePtr2(0, item, "MediaItem*") then reaper.MB("Could not determine valid item.", "ERROR", 0) return end
--reaper.Undo_BeginBlock()
local MIDIOK, MIDI = reaper.MIDI_GetAllEvts(take, "")
if not MIDIOK then reaper.MB("Could not load MIDI string.", "ERROR", 0) return end
local pos, savePos, countEvts = 1, 1, 0 -- pos is position in MIDI while parsing, savePos is next position from which substrings must be stored in tMIDI
local tMIDI = {} -- MIDI substrings will be stored in tMIDI, to be concatenated again later
while pos < #MIDI do
offset, flags, msg, pos = string.unpack("i4Bs4", MIDI, pos)
if flags&1 == 1 then
local eventType = (msg:byte(1))>>4
-- Different CC types use different bytes to carry their values, so each must be handled separately
if eventType == 11 then -- 7-bit CC
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg+1) .. string.char(newRandomValue(msg:byte(3), 0, 127)) .. msg:sub(4,nil)
savePos = pos
countEvts = countEvts + 1
elseif eventType == 13 then -- Channel pressure
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg) .. string.char(newRandomValue(msg:byte(2), 0, 127)) .. msg:sub(3,nil)
savePos = pos
countEvts = countEvts + 1
elseif eventType == 14 then -- Pitch
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg) .. string.char(newRandomValue(msg:byte(2), 0, 127))
.. string.char(newRandomValue(msg:byte(3), 0, 127))
.. msg:sub(4,nil)
savePos = pos
countEvts = countEvts + 1
elseif eventType == 9 and msg:byte(3) ~= 0 then -- Note-on velocity
tMIDI[#tMIDI+1] = MIDI:sub(savePos, pos-#msg+1) .. string.char(newRandomValue(msg:byte(3), 1, 127)) .. msg:sub(4,nil)
savePos = pos
countEvts = countEvts + 1
end
end
end
tMIDI[#tMIDI+1] = MIDI:sub(savePos, nil)
reaper.MIDI_SetAllEvts(take, table.concat(tMIDI))
--reaper.Undo_EndBlock2(0, "Randomized the values of " .. tostring(countEvts) .. " events", -1)
reaper.Undo_OnStateChange_Item(0, "Randomized the values of " .. tostring(countEvts) .. " events", item)
|
This implementation will do for me until we have an implementation like the one I described on my post above, but does anybody know how to tweak this so that it applies the humanization to all the selected notes across different MIDI items (instead of just the selected notes on the active MIDI item)?
Thanks in advance
|
|
|
12-22-2023, 10:01 AM
|
#19
|
Human being with feelings
Join Date: May 2022
Posts: 198
|
Hi there. +1 for this FR.
Quote:
Originally Posted by Ikary
This implementation will do for me until we have an implementation like the one I described on my post above, but does anybody know how to tweak this so that it applies the humanization to all the selected notes across different MIDI items (instead of just the selected notes on the active MIDI item)?
Thanks in advance
|
This is exactly what I'm looking for.
|
|
|
01-26-2024, 03:30 AM
|
#20
|
Human being with feelings
Join Date: Aug 2015
Location: Florence, Italy
Posts: 463
|
+1 for me too, I'm using some scripts but it is not the same as a native option.
I don't understand why whe can humanize notes and velocities but not midi CCs.
|
|
|
Thread Tools |
|
Display Modes |
Linear Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -7. The time now is 12:08 PM.
|