Old 05-19-2020, 06:25 AM   #1
Archie
Human being with feelings
 
Archie's Avatar
 
Join Date: Oct 2017
Location: Russia
Posts: 131
Default Passing Lua Tables to another script

Hi
Please tell me, is it possible to save a table in a lua script and get it in another script?

SWS byGUID functions work very slowly, and the desire to directly transfer the table to another script, for some reason _G does not return in another script.
Thank.
__________________
=================================
ReaPack| Archie-ReaScript: Discussion | Donate |
Archie is offline   Reply With Quote
Old 05-19-2020, 06:58 AM   #2
Meo-Ada Mespotine
Human being with feelings
 
Meo-Ada Mespotine's Avatar
 
Join Date: Apr 2020
Location: Leipzig
Posts: 279
Default

Possible but difficult and full of potential bug-obstacles.
And I'm not sure, whether this will give the speed-benefits you hope for.

Edit: the approach would be to take all table-entries and store them in a file, and reread them into the other file and recreate the table from the data. As far as I know, Lua itself has no way of transferring tables directly.
__________________
Use you/she/her, when contacting me, please. Thanks :) Not mentoring via PMs, sorry.
Ultraschall-API - 1111 ReaScript functions for Reaper - Reaper Internals - Developerdocs4Reaper
Meo-Ada Mespotine is offline   Reply With Quote
Old 05-19-2020, 07:25 AM   #3
Archie
Human being with feelings
 
Archie's Avatar
 
Join Date: Oct 2017
Location: Russia
Posts: 131
Default

Quote:
Originally Posted by Meo-Ada Mespotine View Post
Edit: the approach would be to take all table-entries and store them in a file, and reread them into the other file and recreate the table from the data. As far as I know, Lua itself has no way of transferring tables directly.
This is happening now. To save data to a file, you need to use "SWS GUID, ByGUID", and the function "SWS ByGUID" is very slow, and "userdata" save to a file is not yet possible.

And I wonder if there is a common Reaper (want to see this) table to calculate all the items in the first script, write tracks to the table and pass this table with "userdata" to another script.
__________________
=================================
ReaPack| Archie-ReaScript: Discussion | Donate |

Last edited by Archie; 05-19-2020 at 07:33 AM.
Archie is offline   Reply With Quote
Old 05-19-2020, 07:41 AM   #4
Archie
Human being with feelings
 
Archie's Avatar
 
Join Date: Oct 2017
Location: Russia
Posts: 131
Default

For example

In the first script

Code:
    local t = {};
    for i = 1,reaper.CountMediaItems(0)do;
        local item = reaper.GetMediaItem(0,i-1);
        t[#t+1]=item;
    end;
    reaper.SetTable(t)
In second script
Code:
    tbl = reaper.GetTable()
    
    t = tbl.t
__________________
=================================
ReaPack| Archie-ReaScript: Discussion | Donate |
Archie is offline   Reply With Quote
Old 05-19-2020, 08:30 AM   #5
mpl
Human being with feelings
 
mpl's Avatar
 
Join Date: Oct 2013
Location: Moscow, Russia
Posts: 2,831
Default

What do you want to do at the end? Maybe data shouldn't be transferred exactly as lua table.
__________________
SoundCloud | MPL Scripts discussion | ReaPack | Donate
mpl is offline   Reply With Quote
Old 05-19-2020, 08:45 AM   #6
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default

See this.
amagalma is offline   Reply With Quote
Old 05-19-2020, 09:07 AM   #7
Archie
Human being with feelings
 
Archie's Avatar
 
Join Date: Oct 2017
Location: Russia
Posts: 131
Default

Quote:
Originally Posted by mpl View Post
What do you want to do at the end? Maybe data shouldn't be transferred exactly as lua table.
For example, here is the script.
It mutes of all items , i.e. solo the item and saves the by "BR_GetMediaItemGUID" in "SetProjExtState".
When toggle, it restores by "BR_GetMediaItemByGUID" from "EnumProjExtState".
But the "BR_GetMediaItemByGUID" function is very slow and I would like to save directly to the table.
in other words, save the table in another deferred script, and when restoring, also call this table and restore everything back using it.
And there are a lot of such examples.


Code:
    --==== FUNCTION MODULE FUNCTION ======================= FUNCTION MODULE FUNCTION ============== FUNCTION MODULE FUNCTION ======
    local P,F,L,A=reaper.GetResourcePath()..'/Scripts/Archie-ReaScripts/Functions','/Arc_Function_lua.lua'; L,A=pcall(dofile,P..F);
    if not L then reaper.RecursiveCreateDirectory(P,0);reaper.MB('Missing file / Отсутствует файл!\n\n'..P..F,"Error",0);return;end;
    if not A.VersionArc_Function_lua("2.8.0",P,"")then A.no_undo() return end;local Arc=A; -- =====================================
    --==== FUNCTION MODULE FUNCTION ====▲=▲=▲============== FUNCTION MODULE FUNCTION ============== FUNCTION MODULE FUNCTION ======
    
    
    
    ------------------------------------------
    local function cleanProjExtState(extname);
        for i = 1,math.huge do;
            local retval,key,value = reaper.EnumProjExtState(0,extname,0);
            if retval then;
                reaper.SetProjExtState(0,extname,key,'');
            else;
                break;
            end;
        end;
    end;
    ------------------------------------------
    local function anyItemMute();
        local CountItem = reaper.CountMediaItems(0);
        for i = 1,CountItem do;
            local item = reaper.GetMediaItem(0,i-1);
            local mute = reaper.GetMediaItemInfo_Value(item,'B_MUTE');
            if mute > 0 then return true end;
        end;
        return false;
    end;
    ------------------------------------------
    
    
    
    local is_new_value,filename,sectionID,cmdID,mode,resolution,val = reaper.get_action_context();
    local extname = filename:match('.*[\\/](.+)$');
    local title1 = 'Solo Item';
    local title2 = 'UnSolo Item';
    local UNDO = nil;
    
    Arc.HelpWindowWhenReRunning(2,'',false,extname);
    
    --------------------------------------------------
    local CountItem = reaper.CountMediaItems(0);
    if CountItem == 0 then;
        cleanProjExtState(extname);
        if Arc.GetSetToggleButtonOnOff(0,0)==1 then;
            Arc.GetSetToggleButtonOnOff(0,1);
        end;
        Arc.no_undo()return;
    end;
    --------------------------------------------------
    
    
    
    local retval,key,val = reaper.EnumProjExtState(0,extname,0);
    local anyItMute = anyItemMute();
    if retval and not anyItMute then;
        cleanProjExtState(extname);
        if Arc.GetSetToggleButtonOnOff(0,0)==1 then;
            Arc.GetSetToggleButtonOnOff(0,1);
        end;
        retval = nil;
    end;
    if retval then;
        -----
        for i = 1,math.huge do;
            local retval,key,value = reaper.EnumProjExtState(0,extname,0);
            if retval then;
                reaper.SetProjExtState(0,extname,key,'');
                local item = reaper.BR_GetMediaItemByGUID(0,key);
                if item then;
                    local mute = reaper.GetMediaItemInfo_Value(item,'B_MUTE');
                    if mute ~= value then
                        if not UNDO then;
                            UNDO = title2;
                            reaper.Undo_BeginBlock();
                            reaper.PreventUIRefresh(1);
                        end;
                        reaper.SetMediaItemInfo_Value(item,'B_MUTE',value);
                    end;
                end;
            else
                break;
            end;
        end;
        --reaper.ShowConsoleMsg('msg')
        if Arc.GetSetToggleButtonOnOff(0,0)==1 then;
            Arc.GetSetToggleButtonOnOff(0,1);
        end;
        -----
    else;
        -----
        local CountSelItem = reaper.CountSelectedMediaItems(0);
        if CountSelItem == 0 then Arc.no_undo()return end;
        for i = 1,CountItem do;
            local item = reaper.GetMediaItem(0,i-1);
            local sel = reaper.GetMediaItemInfo_Value(item,'B_UISEL');
            local mute = reaper.GetMediaItemInfo_Value(item,'B_MUTE');
            if sel == 1 then
                if mute ~= 0 then;
                    if not UNDO then;
                        UNDO = title1;
                        reaper.Undo_BeginBlock();
                        reaper.PreventUIRefresh(1);
                    end;
                    reaper.SetMediaItemInfo_Value(item,'B_MUTE',0);
                    local GUID_it = reaper.BR_GetMediaItemGUID(item);
                    reaper.SetProjExtState(0,extname,GUID_it,mute);
                end;
            else;
                if mute == 0 then;
                    if not UNDO then;
                        UNDO = title1;
                        reaper.Undo_BeginBlock();
                        reaper.PreventUIRefresh(1);
                    end;
                    reaper.SetMediaItemInfo_Value(item,'B_MUTE',1);
                    local GUID_it = reaper.BR_GetMediaItemGUID(item);
                    reaper.SetProjExtState(0,extname,GUID_it,mute);
                end;
            end;
        end;
        if Arc.GetSetToggleButtonOnOff(0,0)~=1 then;
            Arc.GetSetToggleButtonOnOff(1,1);
        end;
        -----
    end;
    
    if UNDO then;
        reaper.PreventUIRefresh(-1);
        reaper.Undo_EndBlock(UNDO,-1);
    end;
    
    reaper.UpdateArrange();
    
    ---------------------------------------------------------------
    ---------------------------------------------------------------
    
    local ActiveDoubleScr,stopDoubleScr;
    local function loop();
        ----- stop Double Script -------
        if not ActiveDoubleScr then;
            stopDoubleScr = (tonumber(reaper.GetExtState(extname,"stopDoubleScr"))or 0)+1;
            reaper.SetExtState(extname,"stopDoubleScr",stopDoubleScr,false);
            ActiveDoubleScr = true;
        end;
        
        local stopDoubleScr2 = tonumber(reaper.GetExtState(extname,"stopDoubleScr"));
        if stopDoubleScr2 > stopDoubleScr then return end;
        --------------------------------
        if Arc.ChangesInProject()then;
            local anyItMute = anyItemMute();
            if not anyItMute then;
                cleanProjExtState(extname);
                if Arc.GetSetToggleButtonOnOff(0,0)==1 then;
                    Arc.GetSetToggleButtonOnOff(0,1);
                end;
                return;
            end;
        end;
        reaper.defer(loop);
    end;
    reaper.defer(loop);
Edit:
The point is to save "userdata" directly, without calling GUID
__________________
=================================
ReaPack| Archie-ReaScript: Discussion | Donate |
Archie is offline   Reply With Quote
Old 05-19-2020, 10:49 AM   #8
mpl
Human being with feelings
 
mpl's Avatar
 
Join Date: Oct 2013
Location: Moscow, Russia
Posts: 2,831
Default

Didn`t really dig into your code deep, but anyway I would:
1) use GetMediaItemTakeByGUID() instead BR_GetMediaItemByGUID, since the last one is chunk-based
2) reduce calls of SetProjExtState to one (store one string, comma or space separated GUIDs and states, then parse it once to a table, then lookup table),
3) cache GUIDs
4) reduce defer rate by reaper.GetProjectStateChangeCount()
__________________
SoundCloud | MPL Scripts discussion | ReaPack | Donate
mpl is offline   Reply With Quote
Old 05-19-2020, 11:45 AM   #9
Sexan
Human being with feelings
 
Sexan's Avatar
 
Join Date: Jun 2009
Location: Croatia
Posts: 2,808
Default

Is it require script or totally separate script?

You can call main script with flag so it does go thru defer and pick up the table you need

Main Script
Code:
local tbl = {1,2,3,4,5}

flag = false
function get_tbl()
  return tbl
end

function main()

if not flag then
  reaper.defer(main)
end
end
main()
Slave script:
Code:
package.path = debug.getinfo(1, "S").source:match [[^@?(.*[\/])[^\/]-$]] .. "?.lua;"

require("main")

flag = true

A_tbl = get_tbl()
Sexan is offline   Reply With Quote
Old 05-20-2020, 01:28 AM   #10
Archie
Human being with feelings
 
Archie's Avatar
 
Join Date: Oct 2017
Location: Russia
Posts: 131
Default

Quote:
Originally Posted by Sexan View Post
Is it require script or totally separate script?

You can call main script with flag so it does go thru defer and pick up the table you need

Main Script
Code:
local tbl = {1,2,3,4,5}

flag = false
function get_tbl()
  return tbl
end

function main()

if not flag then
  reaper.defer(main)
end
end
main()
Slave script:
Code:
package.path = debug.getinfo(1, "S").source:match [[^@?(.*[\/])[^\/]-$]] .. "?.lua;"

require("main")

flag = true

A_tbl = get_tbl()

No, you run "main script" as a module, but I wanted the table to be created in the slave script and saved in the module and then called in the slave script from the module.

Quote:
Originally Posted by mpl View Post
Didn`t really dig into your code deep, but anyway I would:
1) use GetMediaItemTakeByGUID() instead BR_GetMediaItemByGUID, since the last one is chunk-based
2) reduce calls of SetProjExtState to one (store one string, comma or space separated GUIDs and states, then parse it once to a table, then lookup table),
3) cache GUIDs
4) reduce defer rate by reaper.GetProjectStateChangeCount()
1) I tried it. And yes - indeed "GetMediaItemTakeByGUID" is faster than "BR_GetMediaItemByGUID", but not significantly. But even though “GetMediaItemTakeByGUID” has become a little faster, now “BR_GetMediaItemTakeGUID” is very slow. in the end, nothing has changed.
2) Function "SetProjExtState" in this case is fast and does not slow down the process (checked) because it does not write anything to the file, unlike "reaper.SetExtState(.., .., ..,true)". In addition the script can be said not defer.
3) Do not understand what mean.
4) It's done.


Quote:
Originally Posted by amagalma View Post
This is not quite the and does not allow you to save directly "userdata".
But functions
"reaper.JS_Window_AddressFromHandle"
"reaper.JS_Window_HandleFromAddress"
they were much faster than the "Guid"
At 10000 items, with "GUID" Reaper hangs from 10 to 30 seconds.
Using "JS_Window_HandleFromAddress" time was reduced to 1-2 seconds. This is a victory!
Thanks!
__________________
=================================
ReaPack| Archie-ReaScript: Discussion | Donate |
Archie is offline   Reply With Quote
Old 05-20-2020, 02:30 AM   #11
Sexan
Human being with feelings
 
Sexan's Avatar
 
Join Date: Jun 2009
Location: Croatia
Posts: 2,808
Default

You can convert userdata to string (I've done few times)
Sexan is offline   Reply With Quote
Old 05-20-2020, 02:37 AM   #12
Archie
Human being with feelings
 
Archie's Avatar
 
Join Date: Oct 2017
Location: Russia
Posts: 131
Default

Quote:
Originally Posted by Sexan View Post
You can convert userdata to string (I've done few times)
How ? And then how to get "userdata" back from the string
__________________
=================================
ReaPack| Archie-ReaScript: Discussion | Donate |
Archie is offline   Reply With Quote
Old 05-20-2020, 03:37 AM   #13
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default

Reaper's items, takes, etc are C objects. C objects when accessed in lua are called userdata. So let's say that object=userdata. A GUID is a number that is an attribute of the reaper object and shows its identity. Every time you open and close your reaper project the GUIDs are the same. The identities of the reaper's objects do not change except if you explicitly ask them to. Objects are stored at a memory address. This memory address is a number. In order to work with an object you need a handle that will point you to that memory address/location where said object exists.

Example:
Code:
track = reaper.GetTrack(0,0)
-- we can access that track (reaper object) using the variable 'track'
-- variable 'track' is our handle to that object
-- in the IDE you see 'reaper object [object's address in hex]'

what_is = type(track) -- "userdata"
-- you see that variable track refers to userdata -> C object (reaper object)

string_representation = tostring(track) -- "userdata: its address in hex"
-- you get a string that tells you that it is a userdata and its address in hex

just_the_adress = tonumber("0x" .. string_representation:sub(11))
-- this is the address of the object

-- alternative way to get the address
address = reaper.JS_Window_AddressFromHandle( track )
-- remember that variable 'track' is the handle to our object

if just_the_adress == address then
  reaper.ShowConsoleMsg("The addresses are equal\n")
end

-- let's get back a new handle to the object whose memory address we know
new_handle = reaper.JS_Window_HandleFromAddress( address )
-- we now have a reference to our object stored in variable 'new_handle'

if new_handle == track then
  reaper.ShowConsoleMsg("They refer to the same object\n")
end
Take into account that each time you open a reaproject, its objects are stored in new memory locations. So, if you save the addresses in an external state, close the project and reopen it, you will not be able to access again the objects you had stored. If you want to access them again then store them with their GUIDs.

I hope this clarifies things.
amagalma is offline   Reply With Quote
Old 05-20-2020, 03:50 AM   #14
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default

Speed comparison:
Code:
local track = reaper.GetTrack(0,0)
local just_the_adress, address

local times = 1000000

local start1 = reaper.time_precise()
for i = 1, times do
  just_the_adress = tonumber("0x" .. tostring(track):sub(11))
end
speed1 = reaper.time_precise() - start1

local start2 = reaper.time_precise()
for i = 1, times do
  address = reaper.JS_Window_AddressFromHandle( track )
end
speed2 = reaper.time_precise() - start2

ratio = math.floor((speed2/speed1)*100 + 0.5)

reaper.ShowConsoleMsg("Using JS_Window_AddressFromHandle takes " .. ratio ..
"% of the time it would take by using the lua way\n")
Code:
Using JS_Window_AddressFromHandle takes 55% of the time it would take by using the lua way

Last edited by amagalma; 05-20-2020 at 04:24 AM.
amagalma is offline   Reply With Quote
Old 05-20-2020, 04:22 AM   #15
amagalma
Human being with feelings
 
amagalma's Avatar
 
Join Date: Apr 2011
Posts: 2,071
Default

And another speed comparison test:
Code:
local function TestSpeed(function_name, times_to_run)
  times_to_run = times_to_run or 1000000
  local start = reaper.time_precise()
  for i = 1, times_to_run do
    function_name()
  end
  return reaper.time_precise() - start, function_name()
end


local track = reaper.GetTrack(0,0)
track_address = reaper.JS_Window_AddressFromHandle( track )


local function UsingGmem()
  reaper.gmem_attach("Gx470Tdr_2.6" )
  reaper.gmem_write(1, track_address )
  return reaper.gmem_read( 1 )
end
time1, handle1 = TestSpeed(UsingGmem)

local function UsingExtProject()
  reaper.SetProjExtState( 0, "Test speed", "address", track_address )
  local _, val = reaper.GetProjExtState( 0, "Test speed", "address" )
  return tonumber(val)
end
time2, handle2 = TestSpeed(UsingExtProject)

ratio = math.floor((time2/time1)*100 + 0.5)

reaper.ShowConsoleMsg("Using SetProjExtState takes " .. ratio ..
"% of the time it would take by using the gmem way\n")
Code:
Using SetProjExtState takes 796% of the time it would take by using the gmem way
amagalma is offline   Reply With Quote
Old 05-20-2020, 05:21 AM   #16
Archie
Human being with feelings
 
Archie's Avatar
 
Join Date: Oct 2017
Location: Russia
Posts: 131
Default

@amagalma
Thank you for the detailed explanation.
__________________
=================================
ReaPack| Archie-ReaScript: Discussion | Donate |
Archie is offline   Reply With Quote
Old 05-20-2020, 09:37 AM   #17
Meo-Ada Mespotine
Human being with feelings
 
Meo-Ada Mespotine's Avatar
 
Join Date: Apr 2020
Location: Leipzig
Posts: 279
Default

Wait! You can send objects like MediaTracks, Mediaitems, Envelopes, Projects, etc through ExtStates from one source-script to another targetscript, and regain it in a way, that you can work with the object again in the targetscript?
__________________
Use you/she/her, when contacting me, please. Thanks :) Not mentoring via PMs, sorry.
Ultraschall-API - 1111 ReaScript functions for Reaper - Reaper Internals - Developerdocs4Reaper
Meo-Ada Mespotine is offline   Reply With Quote
Old 05-20-2020, 10:27 AM   #18
Archie
Human being with feelings
 
Archie's Avatar
 
Join Date: Oct 2017
Location: Russia
Posts: 131
Default

Quote:
Originally Posted by Meo-Ada Mespotine View Post
Wait! You can send objects like MediaTracks, Mediaitems, Envelopes, Projects, etc through ExtStates from one source-script to another targetscript, and regain it in a way, that you can work with the object again in the targetscript?
Through ExtStates no.
You can do it like this

source script:
Code:
   reaper.gmem_attach("Gx470Tdr_2.6")
   
   for i = 1,reaper.CountTracks(0)do;
       track = reaper.GetTrack(0,i-1)
       address = reaper.JS_Window_AddressFromHandle(track)
       reaper.gmem_write(i-1,address)
   end;
second script
Code:
   reaper.gmem_attach("Gx470Tdr_2.6")
   t={}
   
   for i = 1,math.huge do;
      address = reaper.gmem_read(i-1)
      if address ~= 0 then
          track = reaper.JS_Window_HandleFromAddress(address)
          t[#t+1] = track
      else
          break
      end
   end
Edit:
More precisely, you can use and "ExtStates" , but "gmem" is faster.
__________________
=================================
ReaPack| Archie-ReaScript: Discussion | Donate |

Last edited by Archie; 05-20-2020 at 10:35 AM.
Archie is offline   Reply With Quote
Old 05-20-2020, 10:34 AM   #19
Meo-Ada Mespotine
Human being with feelings
 
Meo-Ada Mespotine's Avatar
 
Join Date: Apr 2020
Location: Leipzig
Posts: 279
Default

Quote:
Originally Posted by Archie View Post
Through ExtStates no.
You can do it like this

source script:
Code:
   reaper.gmem_attach("Gx470Tdr_2.6")
   
   for i = 1,reaper.CountTracks(0)do;
       track = reaper.GetTrack(0,i-1)
       address = reaper.JS_Window_AddressFromHandle(track)
       reaper.gmem_write(i-1,address)
   end;
second script
Code:
   reaper.gmem_attach("Gx470Tdr_2.6")
   t={}
   
   for i = 1,math.huge do;
      address = reaper.gmem_read(i-1)
      if address ~= 0 then
          track = reaper.JS_Window_HandleFromAddress(address)
          t[#t+1] = track
      else
          break
      end
   end
OMG Thankyouthankyouthankyou. This opens up tons of possibilities.
Does this work for tables as well?
__________________
Use you/she/her, when contacting me, please. Thanks :) Not mentoring via PMs, sorry.
Ultraschall-API - 1111 ReaScript functions for Reaper - Reaper Internals - Developerdocs4Reaper
Meo-Ada Mespotine is offline   Reply With Quote
Old 05-20-2020, 10:58 AM   #20
Archie
Human being with feelings
 
Archie's Avatar
 
Join Date: Oct 2017
Location: Russia
Posts: 131
Default

Quote:
Originally Posted by Meo-Ada Mespotine View Post
OMG Thankyouthankyouthankyou.
Not to me, but "amagalma" Thank you

Quote:
Originally Posted by Meo-Ada Mespotine View Post
Does this work for tables as well?
Tables seem impossible or I didn’t understand how
But in any case, it can be sorted out in a loop and also passed.
__________________
=================================
ReaPack| Archie-ReaScript: Discussion | Donate |
Archie 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 05:50 AM.


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