Global Persistent Variables?

Home Forums General Using Ctrlr Global Persistent Variables?

Tagged: 

Viewing 13 posts - 1 through 13 (of 13 total)
  • Author
    Posts
  • #118291
    computerchemist
    Participant
      • Topics: 2
      • Replies: 31
      • Total: 33

      Hopefully the title explains it.. I’m looking for a way to save certain global variables prior to exit and restore them again during execution.
      This would use the panel functions “Called before any modulators..” for load, but I’m not sure what to use for exiting.
      My bigger problem is what method I must use to store/retrieve the variables. I’d like to use the same state files that must be used when Ctrlr stores MIDI channel, etc.
      I could solve this by making a standalone ini file but then I would need to store it in the profiles section which I don’t know for each operating system.
      So my question is:
      – How can we preserve certain variable states when the program is not being used, so they are there again at startup? Is there a special method for this?

      #118293
      goodweather
      Participant
        • Topics: 45
        • Replies: 550
        • Total: 595
        • ★★★

        You can achieve this with stateData. StateData is kept in the Ctrlr cache.

        Create a method SaveStateData to store the data you want to keep. Example:

        --
        -- Called when data needs saving
        -- StateData is saved when the user press Ctrl+Alt+S (Save Ctrlr StateData in the menu)
        -- or when quitting Ctrlr while leaving the panel open.
        -- At that time all modulators indicated as Dynamic are saved but by using this method 
        -- it is possible to also save values of variables that can then be read when loading the panel
        --
        SaveStateData = function(--[[ ValueTree --]]stateData)
        
        	-- Saving the last file loaded/saved so it can be reloaded at panel load
        	-- This is needed to have all uiLabels restored
        	-- If a uiLabel is set to Dynamic instead of Static then it is its value - 0 - and not its uiLabelText property that is saved
        	-- Therefore, obliged to keep all uiLabels as Static and to reload the last file
        	stateData:setProperty ("LastFileFullPath", sLastFileFullPath, nil)
        	stateData:setProperty ("LastFileName", sLastFileName, nil)
        	stateData:setProperty ("RootFolder", currentRootFolder, nil)
        
        end

        Create a method to read your stateData and put the values in variables. Example:

        --
        -- Called when data is restored
        --
        ReadStateData = function(--[[ ValueTree --]] stateData)
        
        	-- Variables to be read
        	sLastFileFullPath = stateData:getProperty("LastFileFullPath")
        	sLastFileName = stateData:getProperty("LastFileName")
        	currentRootFolder = stateData:getProperty("RootFolder")
        
        end

        In panel properties, set
        Called when Ctrlr state is saved: SaveStateData
        Called when Ctrlr is loaded: ReadStateDate

        In your PanelLoaded method, you need to check if the StateDate has been restored or not. Yep, it is sometimes failing… Example:

        -- Check restored statedata and, if found, inform timer to reload file to secure all uiLabels are correctly set
        if sLastFileFullPath == nil or sLastFileFullPath == "" then
        	sLastFileFullPath = ""
        	sLastFileName = ""
        	bLastFile = false
        else
        	-- if stateData is working then sLastFileFullPath, sLastFileName should have been restored correctly
        	-- Check if file has not been moved or been deleted
        	f = File(sLastFileFullPath)
        	if f:existsAsFile() then
        		bLastFile = true
        	else
        		sLastFileFullPath = ""
        		sLastFileName = ""
        		bLastFile = false
        	end
        end

        Last info: please be aware that StateData is only saved when the user quit Ctrlr by keeping the panel open. Thus not when simply closing a panel. A bit tricky and for sure not perfect.
        Good luck!

        #118299
        computerchemist
        Participant
          • Topics: 2
          • Replies: 31
          • Total: 33

          Thanks! That is exactly what I’ve been looking for (and could not find!) 🙂

          #118302
          goodweather
          Participant
            • Topics: 45
            • Replies: 550
            • Total: 595
            • ★★★

            Glad I could help 🙂

            #118304
            computerchemist
            Participant
              • Topics: 2
              • Replies: 31
              • Total: 33

              I have a follow on query that’s still within this context (sort of) and I hope you can help with too.

              I have a 3 dimensional array (table?), ProgramPatch[][] where the first element is the program number, the second element is the patch number in the program. I effectively want to store this into the State Data too.
              In other languages I’d serialise the array into a string, or use a JSON library to do the same. I just can’t seem to figure out the best way with LUA. What’s the best way in CTRLR of storing the array and retrieving it?

              #118307
              goodweather
              Participant
                • Topics: 45
                • Replies: 550
                • Total: 595
                • ★★★

                I have never tried but I think you can store what you want in StateData.
                Just make a test 😉
                I suppose this is for user patches. For factory patches, just declare your table in some TablesInit() method then call that method at panel load to initialize it.
                If you load user patchs in some component, then they stay there even if you close and re-open the panel because in the cache of the panel.
                Therefore, I’m not sure you really need to keep that in state data.
                You need to experiment different ways and choose the one you like the best. This is Ctrlr 😉

                #118308
                computerchemist
                Participant
                  • Topics: 2
                  • Replies: 31
                  • Total: 33

                  Thanks for the pointers (again) 🙂 Yes, it will store the current sliders but I can only do bulk sysex dumps to this synth so I need to store all 64 patches locally, not just the displayed one. I’ve probably take the long road doing that with arrays but I’ll give it a go.

                  #118309
                  computerchemist
                  Participant
                    • Topics: 2
                    • Replies: 31
                    • Total: 33

                    That just crashes and burns trying to reference the array directly – it doesn’t crash while saving state, but it doesn’t seem to write the arrays to the state table either. So when reading back in, I’m just getting a nil array.

                    #118310
                    goodweather
                    Participant
                      • Topics: 45
                      • Replies: 550
                      • Total: 595
                      • ★★★

                      OK. which synth is it?
                      You didn’t tell me if it was factory or user patches.
                      Now that you gave me more info about what you want to do I can redirect you a bit.
                      Please look at my Pro 2 panel.

                      I would use Memoryblocks (look in Juce API doc and also in another post I answered recently.

                      MemoryBlock v CtrlrLuaMemoryBlock


                      One MB would be a bank of 64 patches. Each patch has a fixed length. Starts by F0 and finishes by F7.
                      When starting the panel, you load a MB of 64 patches (file on disk) and from there you can send each patch to the synth as you want.
                      You also treat each patch as a MB.

                      #118311
                      computerchemist
                      Participant
                        • Topics: 2
                        • Replies: 31
                        • Total: 33

                        Sorry – it was for the Korg Poly-800. I think I’ve taken the wrong approach then completely – because of all the bit slicing and shifting I decode the sysex in one block (it doesn’t handle CC or individual patches) into a 64×99 array which I then map onto the controllers. A prog up/down just refreshes the controllers from the appropriate row in the array. When saving, I read the controllers in a loop and update the appropriate row in the array. The first element is the program and the second is the controller. So the complete program patch state is stored in a 3 dimensional array from when it leaves the sysex (file or dump) to when it need to go back to be sysex (file or dump).
                        Hold up – I think I answered my own question. Why not store the sysex dump which is a MemoryBlock prior to writing, I could treat the save state as a thrid way of storing sysex. Duh. On it now… 😀

                        #118313
                        dnaldoog
                        Participant
                          • Topics: 4
                          • Replies: 480
                          • Total: 484
                          • ★★

                          Hi computerchemist,

                          You can store a lua table as a JSON like string:

                          This is in my JD-990 panel:

                          ccMapArray is an table of modulator names as values with CC numbers as keys. The user can build a set of pairings for controlling the panel -> synth from a keyboard.

                          So I have for example a modulator I named TVFCUTFR, which is the JD-990 TVF cutoff. I want to save a mapped cc number, for example 41, to that modulator on reload, along with any other pairing.

                          I can save this using stateLData:addChild instead of Goodweather’s stateData:setProperty by creating a new ValueTree object called ccMapping. Here I use the modulator name as a hash/key, but you could just as easily store an index too, I guess.

                          First I check that the table is not empty otherwise bad things happen.

                          
                          SAVE CC MAPPING ARRAY TO stateData:child
                          
                          if not rawequal(next(ccMapArray), nil) then
                          	
                          	local aoc=ValueTree("ccMapping")
                          	for k,v in pairs (ccMapArray) do
                          		aoc:setProperty (tostring(v),tostring(k), nil) 
                          	end
                          	stateData:addChild (aoc, -1, nil)
                          	
                          end -- table not empty
                          

                          To read that ValueTree table back into Ctrlr on reload:

                          
                          mi=stateData:getChildWithName("ccMapping")
                          for i=0,mi:getNumProperties()-1 do
                          	local k=mi:getPropertyName(i) -- k is the name of the control
                          	local v=mi:getProperty(k) -- v is the cc number
                          	ccMapArray[tonumber(v)]=k -- re-populate ccMapArray with data
                          end
                          

                          Note: In my case I am reading the value back in as the key to my table. You probably would keep it the same as how it was saved, i.e.

                          
                          	local k=mi:getPropertyName(k) -- k is the name of the control
                          	local v=mi:getProperty(i) -- v is the cc number
                          	ccMapArray[tonumber(k)]=v -- re-populate ccMapArray with data
                          

                          I don’t seem to be checking whether mi exists . Maybe don’t have to? And mi should probably be declared local.

                          What Goodweather suggested is stored as <panelCustomData></panelCustomData> and <ccmapping /> will be stored within that node as XML Attributes. (I looked this all up on XML, so I may have the terminology wrong)

                          
                          <panelCustomData graphlinecolour="4282400832" envbackgroundbrightness="0" <ccMapping WGBENDSW="0" WGPICHRN="10" TVFCUTFR="41"/></panelCustomData></panel>
                          

                          I would leave it to you to work out how to do this with a three dimensional array or table in lua speak, but it must be doable!

                          • This reply was modified 3 years, 11 months ago by dnaldoog.
                          #118315
                          dnaldoog
                          Participant
                            • Topics: 4
                            • Replies: 480
                            • Total: 484
                            • ★★

                            Why not store the sysex dump which is a MemoryBlock prior to writing, I could treat the save state as a thrid way of storing sysex. Duh. On it now… ?

                            Or just do it that way!!!

                            
                            x=function()
                            str="F0 43 12 03 00 00 57 f7" 
                            m=MemoryBlock()
                            m:loadFromHexString(str)
                            end
                            
                            
                            save = function(--[[ ValueTree --]]stateData)
                            stateData:setProperty ("mylongsysexdump", m:toHexString(1), nil)
                            end
                            

                            P.S. unless you need those hash key or value names recorded …

                            • This reply was modified 3 years, 11 months ago by dnaldoog.
                            #118325
                            computerchemist
                            Participant
                              • Topics: 2
                              • Replies: 31
                              • Total: 33

                              Big shout out to both of you super helpful guys 🙂
                              I got it all working in the end with a mixture of both approaches.

                            Viewing 13 posts - 1 through 13 (of 13 total)
                            • The forum ‘Using Ctrlr’ is closed to new topics and replies.
                            There is currently 0 users and 80 guests online
                            No users are currently active
                            Forum Statistics
                            Threads: 2,495, Posts: 17,374, Members: 77,605
                            Most users ever online was 12 on January 22, 2019 3:47 pm
                            Ctrlr