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

Reply
 
Thread Tools Display Modes
Old 06-24-2022, 01:26 PM   #1
wave
Human being with feelings
 
Join Date: Oct 2021
Posts: 33
Default Enable/disable FX instance oversampling for the whole project

Hi all,

With the new and very useful additional FX oversampling features, some users (including myself) want a project-wide control to enable or disable oversampling. This is actually an open feature request:

https://forums.cockos.com/showthread.php?t=266302

We don't know if/when the devs will implement such a feature. In the meantime, this script, which can simply be called from a toolbar button, is able to enable or disable track FX oversampling settings in a click for the whole project:

EDIT: the code below is deprecated. See https://forum.cockos.com/showpost.ph...36&postcount=5 for the updated version

Code:
EXTNAME = "OVERSAMPLING_TOGGLE"
EXTNAME_TOGGLE_KEY = "ENABLED"

OS_ENABLED = "NEXT_FX_OVERSAMPLE"
OS_DISABLED = ""
FXID = "FXID"

RPP_OBJ, RPP_PATH = reaper.EnumProjects(-1, '')

function GetLines(s)
    if s:sub(-1)~="\n"
    then
        s=s.."\n"
    end
    return s:gmatch("(.-)\n")
end

extname_keys = {}

get_ext_state_retval, is_enabled = reaper.GetProjExtState(RPP_OBJ, EXTNAME, EXTNAME_TOGGLE_KEY)
if is_enabled == "true"
then
    is_enabled = true
else
    is_enabled = false
    for i = 1, 128
    do
        get_ext_state_enum_retval, key, val = reaper.EnumProjExtState(RPP_OBJ,  EXTNAME, i)
        if get_ext_state_enum_retval
        then
            extname_keys[key] = val
        else
            break
        end
    end
end

for i = 0, reaper.CountTracks()-1
do
    track = reaper.GetTrack(0, i)
    get_track_state_chunk_retval, track_chunk = reaper.GetTrackStateChunk(track, '', true)
    track_chunk_new = ""
    next_fx_is_os = false
    next_fx_os_value = 0
    for l in GetLines(track_chunk)
    do
        if is_enabled
        and string.match(l, OS_ENABLED)
        then
            next_fx_is_os = true
            next_fx_os_value = l:gsub(OS_ENABLED .. " ", "")
            l = OS_DISABLED
        elseif not is_enabled
        and string.match(l, FXID)
        then
            if extname_keys[l]
            then
                reaper.SetProjExtState(RPP_OBJ, EXTNAME, l, "")
                l = OS_ENABLED .. " " .. extname_keys[l] .. "\n" .. l
            end
        end

        if next_fx_is_os
        and string.match(l, FXID)
        then
            reaper.SetProjExtState(RPP_OBJ, EXTNAME, l, next_fx_os_value)
            next_fx_is_os = false
            next_fx_os_value = 0
        end
  
        track_chunk_new = track_chunk_new .. l .. "\n"
    end
    set_track_state_chunk_retval = reaper.SetTrackStateChunk(track, track_chunk_new, true)
end

reaper.SetProjExtState(RPP_OBJ, EXTNAME, EXTNAME_TOGGLE_KEY, tostring(not is_enabled))
local is_new_value, filename, sec, cmd, mode, resolution, val = reaper.get_action_context()
bool_to_number={[true]=1, [false]=0}
reaper.SetToggleCommandState(sec, cmd, bool_to_number[not is_enabled])
reaper.RefreshToolbar2(sec, cmd)

How to use:


The script can be attached to a button in a toolbar.

The button will work as a toggle:
-when it is toggled, it means that FX oversampling is enabled in the project and every track FX that the user has set to be oversampled actually will be, both during playback and rendering.
-when it is not toggled, FX oversampling rate (x2, x4, etc.) is stored for every track FX where the user set it, and the oversampling is disabled for all those track FX. The exact oversampling rate of each concerned FX is restored when you toggle the button back again


Note 1: when you run it for the first time in a project, you might want to run it twice in order for the script to accurately show the current state. Personally, I did this on my project template and saved a new version of the template with this script's state in it.

Note 2: this does not aim to promote blind use of oversampling. The point is to allow users who know how certain FX behave when oversampled to keep an efficient playback experience by disabling FX oversampling overall, while re-enabling it right before rendering. This is exactly my own use case, which is what led to this script.


Feel free to give any feedback. I might implement your suggestion if it fits into my use case, otherwise any other kind soul of this forum could do it for you.


Thanks to X-Raym and Mespotine for their input https://forum.cockos.com/showthread.php?t=268021

Last edited by wave; 06-26-2022 at 10:01 AM.
wave is offline   Reply With Quote
Old 06-26-2022, 04:00 AM   #2
PitchSlap
Human being with feelings
 
PitchSlap's Avatar
 
Join Date: Jan 2008
Location: Vancouver, BC
Posts: 3,793
Default

Quote:
Originally Posted by wave View Post
Hi all,

With the new and very useful additional FX oversampling features, some users (including myself) want a project-wide control to enable or disable oversampling. This is actually an open feature request:

https://forums.cockos.com/showthread.php?t=266302

We don't know if/when the devs will implement such a feature. In the meantime, this script, which can simply be called from a toolbar button, is able to enable or disable track FX oversampling settings in a click for the whole project:

The script can be attached to a button in a toolbar.

The button will work as a toggle:
-when it is toggled, it means that FX oversampling is enabled in the project and every track FX that the user has set to be oversampled actually will be, both during playback and rendering.
-when it is not toggled, FX oversampling rate (x2, x4, etc.) is stored for every track FX where the user set it, and the oversampling is disabled for all those track FX. The exact oversampling rate of each concerned FX is restored when you toggle the button back again


Note 1: when you run it for the first time in a project, you might want to run it twice in order for the script to accurately show the current state. Personally, I did this on my project template and saved a new version of the template with this script's state in it.

Note 2: this does not aim to promote blind use of oversampling. The point is to allow users who know how certain FX behave when oversampled to keep an efficient playback experience by disabling FX oversampling overall, while re-enabling it right before rendering. This is exactly my own use case, which is what led to this script.


Feel free to give any feedback. I might implement your suggestion if it fits into my use case, otherwise any other kind soul of this forum could do it for you.
Thanks for this! It was a request I made as well.
Justin said that it wouldn't be implemented as you should always render with the OS settings you were listening to (though I raised point #2).

Btw, is this a LUA script? i.e. do we just save the code as .lua to load from a toolbar?
__________________
FRs: v5 Media Explorer Requests, Global Quantization, Session View
Win10 Pro 64-bit, Reaper 6(x64), AMD 3950x, Aorus X570 Master, 64GB DDR4 3600, PowerColor Red Devil 5700XT, EVO 970 2TB, 10TB HD, Define R6
PitchSlap is offline   Reply With Quote
Old 06-26-2022, 05:04 AM   #3
wave
Human being with feelings
 
Join Date: Oct 2021
Posts: 33
Default

Yes, this is a lua script, so it's loaded like any other. I.e. you can just copy-paste it in a .lua file, load the script through the Action List (New action > Load ReaScript). From there you can either run it through the Action List directly or create a toolbar button for it. I'd suggest the toolbar button approach because the button acts as a toggle and shows if oversampling is enabled or not.

Careful though, this behaves as expected regarding oversampling, but I noticed it has a weird side effect on FX automation, which gets deleted even when the FX is not concerned by oversampling at all. I'm fixing this ASAP.

I don't agree that you should always render with the oversampling settings you're listening to. Then why have an option to change the project rendering sample rate in the project render window?
If you can listen to it, it's better of course, but if you have a 150+ tracks project with complex routing and oversampling in the busses and master, good luck finding an off the shelf computer that can handle that. In those cases, the best you can do is previewing the behavior of the plugins you want to oversample separately and make sure the plugin supports the OS target rate.
wave is offline   Reply With Quote
Old 06-26-2022, 07:15 AM   #4
mpl
Human being with feelings
 
mpl's Avatar
 
Join Date: Oct 2013
Location: Moscow, Russia
Posts: 3,960
Default

1. This is verry bad to use chunking for FX chunk project-wide. Even couple of instances of huge FX like Kontakt or Reaktor can take a lot of time even on good machines. So firstly, I would request channging oversampling for FX / Fx chaing via API.

2.
Code:
for i = 1, 128
If you want to store source states of oversampling you`d better to use FX GUIDs rather than just random numbers, because user do whatever with the project between toogle on-off, then all [1..128] will be linked to a different FX.

3.
Code:
Careful though, this behaves as expected regarding oversampling, but I noticed it has a weird side effect on FX automation, which gets deleted even when the FX is not concerned by oversampling at all. I'm fixing this ASAP.
probably this
Code:
get_track_state_chunk_retval, track_chunk = reaper.GetTrackStateChunk(track, '', false)
mpl is offline   Reply With Quote
Old 06-26-2022, 09:57 AM   #5
wave
Human being with feelings
 
Join Date: Oct 2021
Posts: 33
Default

Thanks for your feedback, mpl.

Quote:
Originally Posted by mpl View Post
1. This is verry bad to use chunking for FX chunk project-wide. Even couple of instances of huge FX like Kontakt or Reaktor can take a lot of time even on good machines. So firstly, I would request channging oversampling for FX / Fx chaing via API.
Of course, running this through FX chunks is not the most efficient solution and is kind of ugly. But API access to FX oversampling isn't available yet and we can't know for sure it will be one day. I'm going to ask for the devs to provide it, though.

In the meantime, the point of this tool is using it only before rendering, not constantly switching it on and off. Taking that into account, this runs reasonably fast (a few seconds) on 150+ tracks projects (granted, I don't have Kontakt, and it would still be faster, cleaner and safer to have API functions instead).


Quote:
Originally Posted by mpl View Post
If you want to store source states of oversampling you`d better to use FX GUIDs rather than just random numbers, because user do whatever with the project between toogle on-off, then all [1..128] will be linked to a different FX.
That's exactly what I'm doing.
The "for i = 1, 128" loop only uses i as an index to go over all available ExtState keys with reaper.EnumProjExtState(rpp, extname, i). The actual ExtState key is the FX UID indeed


Quote:
Originally Posted by mpl View Post
probably this
Code:
get_track_state_chunk_retval, track_chunk = reaper.GetTrackStateChunk(track, '', false)
That was exactly it. I had to rewrite a piece of my code to adapt to the different output that "GetTrackStateChunk(track, '', true)", but now it's fixed, thanks!
I couldn't find any documentation on what that isundo boolean does when set to true or false; could you elaborate a little more into why setting it to true doesn't return FX automations?


Anyway, here's an updated version of the code, that doesn't mess with FX envelopes anymore. It should support enabling/disabling FX oversampling for VSTs, AUs and JSs:
Code:
EXTNAME = "OVERSAMPLING_TOGGLE"
EXTNAME_TOGGLE_KEY = "ENABLED"

OS_ENABLED = "NEXT_FX_OVERSAMPLE"
OS_DISABLED = ""
FXID = "FXID"

RPP_OBJ, RPP_PATH = reaper.EnumProjects(-1, '')

function GetLines(s)
    if s:sub(-1)~="\n"
    then
        s=s.."\n"
    end
    return s:gmatch("(.-)\n")
end

extname_keys = {}

get_ext_state_retval, is_enabled = reaper.GetProjExtState(RPP_OBJ, EXTNAME, EXTNAME_TOGGLE_KEY)
if is_enabled == "true"
then
    is_enabled = true
else
    is_enabled = false
    for i = 1, 128
    do
        get_ext_state_enum_retval, key, val = reaper.EnumProjExtState(RPP_OBJ, EXTNAME, i)
        if get_ext_state_enum_retval
        then
            extname_keys[key] = val
        else
            break
        end
    end
end

for i = 0, reaper.CountTracks()-1
do
    track = reaper.GetTrack(0, i)
    get_track_state_chunk_retval, track_chunk = reaper.GetTrackStateChunk(track, '', false)
    track_chunk_new = ""
    next_fx_is_os = false
    next_fx_os_value = 0
    for l in GetLines(track_chunk)
    do
        if is_enabled
        and string.match(l, OS_ENABLED)
        then
            next_fx_is_os = true
            next_fx_os_value = l:gsub(OS_ENABLED .. " ", "")
            l = OS_DISABLED
        elseif not is_enabled
        and string.match(l, FXID)
        and extname_keys[l]
        then
            reaper.SetProjExtState(RPP_OBJ, EXTNAME, l, "")
            matches = {#(track_chunk_new:match(".*<VST") or ""), #(track_chunk_new:match(".*<AU ") or ""), #(track_chunk_new:match(".*<JS ") or "")}
            table.sort(matches)
            pos = matches[#matches]
            track_chunk_new = string.sub(track_chunk_new, 1, pos-5) .. "\n" .. OS_ENABLED .. " " .. extname_keys[l] .. "\n" .. string.sub(track_chunk_new, pos-5) 
        end

        if next_fx_is_os
        and string.match(l, FXID)
        then
            reaper.SetProjExtState(RPP_OBJ, EXTNAME, l, next_fx_os_value)
            next_fx_is_os = false
            next_fx_os_value = 0
        end

        track_chunk_new = track_chunk_new .. l .. "\n"  
    end
    set_track_state_chunk_retval = reaper.SetTrackStateChunk(track, track_chunk_new, false)
end

reaper.SetProjExtState(RPP_OBJ, EXTNAME, EXTNAME_TOGGLE_KEY, tostring(not is_enabled))
local is_new_value, filename, sec, cmd, mode, resolution, val = reaper.get_action_context()
bool_to_number={[true]=1, [false]=0}
reaper.SetToggleCommandState(sec, cmd, bool_to_number[not is_enabled])
reaper.RefreshToolbar2(sec, cmd)
wave is offline   Reply With Quote
Old 06-26-2022, 03:29 PM   #6
mpl
Human being with feelings
 
mpl's Avatar
 
Join Date: Oct 2013
Location: Moscow, Russia
Posts: 3,960
Default

Quote:
Originally Posted by wave View Post
I couldn't find any documentation on what that isundo boolean does when set to true or false; could you elaborate a little more into why setting it to true doesn't return FX automations?
It seems (my guess, no info from devs) "performance hint" flag for GetTrack/Item/EnvelopeChunks means "take chunk without envelopes data to get non-envelope-related info faster".
mpl 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 09:22 AM.


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