Go Back   Cockos Incorporated Forums > REAPER Forums > REAPER Bug Reports

Reply
 
Thread Tools
Old 10-30-2023, 02:42 PM   #1
Edgemeal
Human being with feelings
 
Edgemeal's Avatar
 
Join Date: Apr 2016
Location: ASU`ogacihC
Posts: 4,138
Default [7.00+] GetFXEnvelope() - Get/Set Envelope + FX Container (SOLVED)

EDIT Solved in v7.05+dev1113 - November 13 2023!

Does GetFXEnvelope() work with FX inside Container? I was expecting this would create a Wet track envelope for the focused FX when the FX is inside a FX Container, but it doesn't, returns nil,..

Code:
_, trackidx, _, _, fxidx = reaper.GetTouchedOrFocusedFX(1) -- 1 to query currently focused FX
track = reaper.GetTrack(0, trackidx)
wet_idx = reaper.TrackFX_GetParamFromIdent(track, fxidx, ":wet") -- get wet param index
env = reaper.GetFXEnvelope(track, fxidx, wet_idx, true) -- create track envelope
reaper.TrackList_AdjustWindows(false)
reaper.ShowConsoleMsg(tostring(env) .. '\n')

Win10, REAPER 7.00+

Last edited by Edgemeal; 11-19-2023 at 04:16 PM.
Edgemeal is offline   Reply With Quote
Old 11-12-2023, 09:06 AM   #2
Edgemeal
Human being with feelings
 
Edgemeal's Avatar
 
Join Date: Apr 2016
Location: ASU`ogacihC
Posts: 4,138
Default

Any fix planned? or should I mark this as a "live with it" thing?

BTW, for script that needs to add (and get) envelope for last touched fx param in a Container I use this workaround...
1. Save track envelopes to a table.
2. Run action 41142 ( FX: Show/hide track envelope for last touched FX parameter ).
3. Loop thru track envelopes/table to find the newly added envelope.
Edgemeal is offline   Reply With Quote
Old 11-12-2023, 03:10 PM   #3
Justin
Administrator
 
Justin's Avatar
 
Join Date: Jan 2005
Location: NYC
Posts: 16,810
Default

Edit: making GetFXEnvelope etc support containers doesn't really make sense, since FX in the containers can't have envelopes.

BUT -- you can create a parameter map to the parent container, then get the envelope for that. Lemme write you some code to do this, one sec.

Last edited by Justin; 11-12-2023 at 03:23 PM.
Justin is offline   Reply With Quote
Old 11-12-2023, 05:33 PM   #4
Justin
Administrator
 
Justin's Avatar
 
Join Date: Jan 2005
Location: NYC
Posts: 16,810
Default

ok, here's some code that should be useful:
Code:

function get_fx_id_from_container_path(tr, idx1, ...) -- returns a fx-address from a list of 1-based IDs
  local sc,rv = reaper.TrackFX_GetCount(tr)+1, 0x2000000 + idx1
  for i,v in ipairs({...}) do
    local ccok, cc = reaper.TrackFX_GetNamedConfigParm(tr, rv, 'container_count')
    if ccok ~= true then return nil end
    rv = rv + sc * v
    sc = sc * (1+tonumber(cc))
  end
  return rv
end

function get_container_path_from_fx_id(tr, fxidx) -- returns a list of 1-based IDs from a fx-address
  if fxidx & 0x2000000 then
    local ret = { }
    local n = reaper.TrackFX_GetCount(tr)
    local curidx = (fxidx - 0x2000000) % (n+1)
    local remain = math.floor((fxidx - 0x2000000) / (n+1))
    if curidx < 1 then return nil end -- bad address

    local addr, addr_sc = curidx + 0x2000000, n + 1
    while true do
      local ccok, cc = reaper.TrackFX_GetNamedConfigParm(tr, addr, 'container_count')
      if not ccok then return nil end -- not a container
      ret[#ret+1] = curidx
      n = tonumber(cc)
      if remain <= n then if remain > 0 then ret[#ret+1] = remain end return ret end
      curidx = remain % (n+1)
      remain = math.floor(remain / (n+1))
      if curidx < 1 then return nil end -- bad address
      addr = addr + addr_sc * curidx
      addr_sc = addr_sc * (n+1)
    end
  end
  return { fxid+1 }
end


function fx_map_parameter(tr, fxidx, parmidx) -- maps a parameter to the top level parent, returns { fxidx, parmidx }
  local path = get_container_path_from_fx_id(tr, fxidx)
  if not path then return nil end
  while #path > 1 do
    fxidx = path[#path]
    table.remove(path)
    local cidx = get_fx_id_from_container_path(tr,table.unpack(path))
    if cidx == nil then return nil end
    local i, found = 0, nil
    while true do
      local rok, r = reaper.TrackFX_GetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_index",i))
      if not rok then break end
      if tonumber(r) == fxidx - 1 then
        rok, r = reaper.TrackFX_GetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_parm",i))
        if not rok then break end
        if tonumber(r) == parmidx then found = true parmidx = i break end
      end
      i = i + 1
    end
    if not found then
      -- add a new mapping
      local rok, r = reaper.TrackFX_GetNamedConfigParm(tr,cidx,"container_map.add")
      if not rok then return nil end
      r = tonumber(r)
      reaper.TrackFX_SetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_index",r),tostring(fxidx - 1))
      reaper.TrackFX_SetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_parm",r),tostring(parmidx))
      parmidx = r
    end
  end
  return fxidx, parmidx
end



-- example use
track = reaper.GetTrack(0,0);
id = get_fx_id_from_container_path(track, 2, 1, 2, 1) -- test hierarchy
fxidx,fxparm = fx_map_parameter(track,id,5)
env = reaper.GetFXEnvelope(track,fxidx,fxparm,true)
reaper.TrackList_AdjustWindows(true)

(this is more work than it should be -- we should make GetFXEnvelope() support container addreses, and probably add an API to auto-map parameters, but for now this will work for 7.0x at least...)
Justin is offline   Reply With Quote
Old 11-12-2023, 09:20 PM   #5
Edgemeal
Human being with feelings
 
Edgemeal's Avatar
 
Join Date: Apr 2016
Location: ASU`ogacihC
Posts: 4,138
Default

Quote:
Originally Posted by Justin View Post
ok, here's some code that should be useful:

(this is more work than it should be -- we should make GetFXEnvelope() support container addreses, and probably add an API to auto-map parameters, but for now this will work for 7.0x at least...)
Thanks!
Yes, API support would be awesome!
Edgemeal is offline   Reply With Quote
Old 11-13-2023, 05:05 AM   #6
Meo-Ada Mespotine
Human being with feelings
 
Meo-Ada Mespotine's Avatar
 
Join Date: May 2017
Location: Somewhere over the Rainbow
Posts: 6,966
Default

Quote:
Originally Posted by Justin View Post
ok, here's some code that should be useful:
Code:

function get_fx_id_from_container_path(tr, idx1, ...) -- returns a fx-address from a list of 1-based IDs
  local sc,rv = reaper.TrackFX_GetCount(tr)+1, 0x2000000 + idx1
  for i,v in ipairs({...}) do
    local ccok, cc = reaper.TrackFX_GetNamedConfigParm(tr, rv, 'container_count')
    if ccok ~= true then return nil end
    rv = rv + sc * v
    sc = sc * (1+tonumber(cc))
  end
  return rv
end

function get_container_path_from_fx_id(tr, fxidx) -- returns a list of 1-based IDs from a fx-address
  if fxidx & 0x2000000 then
    local ret = { }
    local n = reaper.TrackFX_GetCount(tr)
    local curidx = (fxidx - 0x2000000) % (n+1)
    local remain = math.floor((fxidx - 0x2000000) / (n+1))
    if curidx < 1 then return nil end -- bad address

    local addr, addr_sc = curidx + 0x2000000, n + 1
    while true do
      local ccok, cc = reaper.TrackFX_GetNamedConfigParm(tr, addr, 'container_count')
      if not ccok then return nil end -- not a container
      ret[#ret+1] = curidx
      n = tonumber(cc)
      if remain <= n then if remain > 0 then ret[#ret+1] = remain end return ret end
      curidx = remain % (n+1)
      remain = math.floor(remain / (n+1))
      if curidx < 1 then return nil end -- bad address
      addr = addr + addr_sc * curidx
      addr_sc = addr_sc * (n+1)
    end
  end
  return { fxid+1 }
end


function fx_map_parameter(tr, fxidx, parmidx) -- maps a parameter to the top level parent, returns { fxidx, parmidx }
  local path = get_container_path_from_fx_id(tr, fxidx)
  if not path then return nil end
  while #path > 1 do
    fxidx = path[#path]
    table.remove(path)
    local cidx = get_fx_id_from_container_path(tr,table.unpack(path))
    if cidx == nil then return nil end
    local i, found = 0, nil
    while true do
      local rok, r = reaper.TrackFX_GetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_index",i))
      if not rok then break end
      if tonumber(r) == fxidx - 1 then
        rok, r = reaper.TrackFX_GetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_parm",i))
        if not rok then break end
        if tonumber(r) == parmidx then found = true parmidx = i break end
      end
      i = i + 1
    end
    if not found then
      -- add a new mapping
      local rok, r = reaper.TrackFX_GetNamedConfigParm(tr,cidx,"container_map.add")
      if not rok then return nil end
      r = tonumber(r)
      reaper.TrackFX_SetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_index",r),tostring(fxidx - 1))
      reaper.TrackFX_SetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_parm",r),tostring(parmidx))
      parmidx = r
    end
  end
  return fxidx, parmidx
end



-- example use
track = reaper.GetTrack(0,0);
id = get_fx_id_from_container_path(track, 2, 1, 2, 1) -- test hierarchy
fxidx,fxparm = fx_map_parameter(track,id,5)
env = reaper.GetFXEnvelope(track,fxidx,fxparm,true)
reaper.TrackList_AdjustWindows(true)

(this is more work than it should be -- we should make GetFXEnvelope() support container addreses, and probably add an API to auto-map parameters, but for now this will work for 7.0x at least...)
Would it be possible to add these functions to official API of Reaper?
Even if it's only working with a csv of the container-index-numbers?
Meo-Ada Mespotine is offline   Reply With Quote
Old 11-13-2023, 05:30 AM   #7
Suzuki
Human being with feelings
 
Suzuki's Avatar
 
Join Date: Jul 2022
Location: Japan
Posts: 904
Default

Great! It works for take FX inside container with a bit of modification. I also vote for API though.

Code:
function get_take_fx_id_from_container_path(tr, idx1, ...) -- returns a fx-address from a list of 1-based IDs
  local sc,rv = reaper.TakeFX_GetCount(tr)+1, 0x2000000 + idx1
  for i,v in ipairs({...}) do
    local ccok, cc = reaper.TakeFX_GetNamedConfigParm(tr, rv, 'container_count')
    if ccok ~= true then return nil end
    rv = rv + sc * v
    sc = sc * (1+tonumber(cc))
  end
  return rv
end

function get_container_path_from_take_fx_id(tr, fxidx) -- returns a list of 1-based IDs from a fx-address
  if fxidx & 0x2000000 then
    local ret = { }
    local n = reaper.TakeFX_GetCount(tr)
    local curidx = (fxidx - 0x2000000) % (n+1)
    local remain = math.floor((fxidx - 0x2000000) / (n+1))
    if curidx < 1 then return nil end -- bad address
    local addr, addr_sc = curidx + 0x2000000, n + 1
    while true do
      local ccok, cc = reaper.TakeFX_GetNamedConfigParm(tr, addr, 'container_count')
      if not ccok then return nil end -- not a container
      ret[#ret+1] = curidx
      n = tonumber(cc)
      if remain <= n then if remain > 0 then ret[#ret+1] = remain end return ret end
      curidx = remain % (n+1)
      remain = math.floor(remain / (n+1))
      if curidx < 1 then return nil end -- bad address
      addr = addr + addr_sc * curidx
      addr_sc = addr_sc * (n+1)
    end
  end
  return { fxid+1 }
end

function take_fx_map_parameter(tr, fxidx, parmidx) -- maps a parameter to the top level parent, returns { fxidx, parmidx }
  local path = get_container_path_from_take_fx_id(tr, fxidx)
  if not path then return nil end
  while #path > 1 do
    fxidx = path[#path]
    table.remove(path)
    local cidx = get_take_fx_id_from_container_path(tr,table.unpack(path))
    if cidx == nil then return nil end
    local i, found = 0, nil
    while true do
      local rok, r = reaper.TakeFX_GetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_index",i))
      if not rok then break end
      if tonumber(r) == fxidx - 1 then
        rok, r = reaper.TakeFX_GetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_parm",i))
        if not rok then break end
        if tonumber(r) == parmidx then found = true parmidx = i break end
      end
      i = i + 1
    end
    if not found then
      -- add a new mapping
      local rok, r = reaper.TakeFX_GetNamedConfigParm(tr,cidx,"container_map.add")
      if not rok then return nil end
      r = tonumber(r)
      reaper.TakeFX_SetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_index",r),tostring(fxidx - 1))
      reaper.TakeFX_SetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_parm",r),tostring(parmidx))
      parmidx = r
    end
  end
  return fxidx, parmidx
end

--------
local track = r.GetSelectedTrack2(0, 0, true)
local retval, trackidx, itemidx, takeidx, fxidx, parm = r.GetTouchedOrFocusedFX(0)
local item = r.GetMediaItem(0, itemidx)
local take = r.GetMediaItemTake(item, takeidx)
local path = get_container_path_from_take_fx_id(take, fxidx)
if path then
  fxidx, parm = take_fx_map_parameter(take, fxidx, parm)
end
local env = r.TakeFX_GetEnvelope(take, fxidx, parm, true)
r.TrackList_AdjustWindows(false)
Suzuki is offline   Reply With Quote
Old 11-13-2023, 08:48 AM   #8
Justin
Administrator
 
Justin's Avatar
 
Join Date: Jan 2005
Location: NYC
Posts: 16,810
Default

making these APIs work for containers, and also some very helpful attributes for FX_GetNamedConfigParm for the next builds to make this stuff a lot more convenient
Justin is offline   Reply With Quote
Old 06-16-2025, 11:04 AM   #9
saxmand
Human being with feelings
 
saxmand's Avatar
 
Join Date: Dec 2023
Location: Denmark
Posts: 722
Default

Trying to find head and tail in containers.

Not sure if I missed something in the code examples Justin shared, but could get the data I wanted.

I wanted to find the param and container pos of a mapped parameter that's inside a container (or multiple containers), but without creating the mapping if it didn't exist.

I believe I get this now constantly, by modifing the fx_map_parameter function. The function below returns the index of the root container and the index of the mapped parameter within the root container, in case it would be useful to others:

Code:
function fx_get_mapped_parameter(tr, fxidx, parmidx) -- maps a parameter to the top level parent, returns { fxidx, parmidx }
  local path = get_container_path_from_fx_id(tr, fxidx)
  local rootContainerPos = path[1] - 1
  if not path then return nil end
  while #path > 1 do
    fxidx = path[#path]
    table.remove(path)
    local cidx = get_fx_id_from_container_path(tr,table.unpack(path))
    if cidx == nil then return nil end
    local i, found = 0, nil
    while true do
      local rok, r = reaper.TrackFX_GetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_index",i))
      reaper.ShowConsoleMsg(r .. " r\n")
      if not rok then break end
      if tonumber(r) == fxidx - 1 then
        rok, r = reaper.TrackFX_GetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_parm",i))
        if not rok then break end
        if tonumber(r) == parmidx then found = true; parmidx = i; break end
      end
      i = i + 1
    end
    if not found then
      if not found then
        -- add a new mapping
        local rok, r = reaper.TrackFX_GetNamedConfigParm(tr,cidx,"container_map.add")
        reaper.ShowConsoleMsg(r .. "\n")
        if not rok then return nil end
        r = tonumber(r)
        reaper.TrackFX_SetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_index",r),tostring(fxidx - 1))
        reaper.TrackFX_SetNamedConfigParm(tr,cidx,string.format("param.%d.container_map.fx_parm",r),tostring(parmidx))
        parmidx = r
      end
    end
  end
  return rootContainerPos, parmidx
end
__________________
Trying to make Reaper the best DAW for film composing!
ReaPack - Script thread - Help me make more free scripts
saxmand is offline   Reply With Quote
Reply

Thread Tools

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 03:03 PM.


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