HISE Logo Forum
    • Categories
    • Register
    • Login

    FR: Global variables scripting API

    Scheduled Pinned Locked Moved Feature Requests
    16 Posts 3 Posters 2.8k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • E
      elanhickler
      last edited by

      It would be nice to be able to create global variables. The more simple the declaration the better. One idea I had for global variables is something like this, using a specific keyword/namespace/etc.:

      var GLOBAL.myGlobal = some_value_here;
      

      Another implementation might be this, having a built-in array in HISE (although I prefer the above syntax):

      var myGlobal = 3;
      GLOBAL[myGlobal] = some_value_here
      

      *To Kontakt devs: HISE knob variables are already global, so you wouldn't need to, for example, use PGS to get a knob value from one script into another as you do in Kontakt.

      1 Reply Last reply Reply Quote 0
      • Christoph HartC
        Christoph Hart
        last edited by

        This would be not difficult to implement. I would prefer a global "dictionary" with Identifier access and the API calls

        Engine.setGlobal("varName", 1.7);
        var x = Engine.getGlobal("varName");
        
        1 Reply Last reply Reply Quote 0
        • E
          elanhickler
          last edited by

          Would that allow for arrays?:

          var a = 10;
          var b = 20;
          var c = 30;
          
          Engine.setGlobal("varName", [a,b,c]);
          var x = Engine.getGlobal("varName");
          
          var Ten = x[1];
          var Twenty = x[2];
          var Thirty = x[3];
          
          

          or this?

          var Ten = Engine.getGlobal("varName")[1];
          var Twenty = Engine.getGlobal("varName")[2];
          var Thirty = Engine.getGlobal("varName")[3];
          
          
          1 Reply Last reply Reply Quote 0
          • Christoph HartC
            Christoph Hart
            last edited by

            Yes, the first syntax example would work. Altough I would recommend to name the array for clarity:

            var array = [];
            array[0] = 10;
            array[1] = 20;
            array[2] = 30;
            
            Engine.setGlobal("varName", array);
            var x = Engine.getGlobal("varName");
            
            Console.print(x[0]); // 10
            Console.print(x[321]); // undefined
            
            

            BTW the JUCE Javascript Engine has some quirks when defining arrays and this style of definition is the most working one.

            Also there will be a difference between handling arrays and primitive values as globals. Long story short: arrays will be referenced and primitive values (integers, doubles and even strings) will be copied:

            var x = Engine.getGlobal("varName"); // "varName" is an array
            x[4] = 7; // operates on the actual array, so the global array will be changed.
            
            var x = Engine.getGlobal("varName"); // "varName" is an integer
            x = 7; // will not change the global variable
            Engine.setGlobal("varName", 7); // this is still needed
            
            

            This would be the least intrusive implementation (for different behaviour it will get messy)

            And I have not decided whether the access to the global variables will be by integer index or named Identifier. This is a performance question and I will do some benchmarking to check if the latter is too heavy.

            1 Reply Last reply Reply Quote 0
            • E
              elanhickler
              last edited by

              As far as I know that shouldn't be a problem. I don't know when you'd need to copy a global array. If you need to then, this would work?

              var reference = Engine.getGlobal("myglobal");
              for (var i = 0; i < array.size(); i++)
              {    
                  var copy[i] = reference[i]
              };
              
              You could make a specific command API just in case, but not important as it would be easy to make your own function to do it.
              [code]
              var copy = Engine.copyGlobal("myglobal")
              [/code][/i][/i]
              
              1 Reply Last reply Reply Quote 0
              • G
                gregjazz
                last edited by

                Doesn't Javascript already have "global" and "local" variable scopes? I guess by global, you mean in the sense that the variables are available outside the single script itself, correct?

                I like the "Engine" object approach, since that looks like a pretty natural way to work. I assume the data type is conveyed when setting the global variable, too? For example:

                var exampleArray = [1, 1, 2, 3, 5, 7, 13];
                Engine.setGlobal("fibonacci", exampleArray);
                
                

                Would you be able to have global functions as well?

                1 Reply Last reply Reply Quote 0
                • Christoph HartC
                  Christoph Hart
                  last edited by

                  @1g2fhobs:

                  Would you be able to have global functions as well?

                  Well, anything that can be saved in a var on the script side will be saved as global variable engine-wide, so theoretically functions will work too. But I can imagine it will get messy quite quickly (What if you call a function that references a variable from another script and the script was deleted?).

                  1 Reply Last reply Reply Quote 0
                  • E
                    elanhickler
                    last edited by

                    If a function references a variable from a script that was deleted, then surely you just throw an error "variable is undefined"

                    What would the code look like to create a global function, is it like this?

                    function myGlobalFunction (a,b) { return a * b; }
                    Engine.setGlobal("myGlobalFunction", myGlobalFunction);
                    
                    myLocalFunction = Engine.getGlobal("myGlobalFunction");
                    
                    var multiplied = myLocalFunction(3,2);
                    
                    

                    Seems like includes would be better for this.

                    1 Reply Last reply Reply Quote 0
                    • Christoph HartC
                      Christoph Hart
                      last edited by Christoph Hart

                      So, I implemented this and how I expected, the function storage comes with some quirks. Consider this example:

                      Script 1:

                      var faultyVariable = 25;
                      
                      var x = function()
                      {
                      	Console.print(faultyVariable);
                      };
                      
                      Engine.setGlobal(0, x);
                      
                      

                      Script 2:

                      var x = Engine.getGlobal(0);
                      
                      x();
                      

                      The console output is undefined. This is because the function uses a variable defined in Script 1, but not in Script 2. If I however, add

                      var faultyVariable = 27;
                      

                      in the second script (before the function call of course), it will print this value (so the global function looks in the local scope). This can lead to pretty unpredictable behaviour where functions do work in one script but not in the next, so I would suggest removing function storage from the global variable container.

                      Also, the variables are clones before they are saved into the global container. This is needed because if you create an array, save it as global and then delete the script processor, the array contains dangling pointers to deleted elements and this will result in a crash.
                      There are a few drawbacks because of this:

                      • interface objects can't be stored (and controlled from another script, but there is the API call Synth.setParameter() for this functionality.
                      • saving an array is slower because it has to be duplicated - but I strongly recommend not saving an array in one of the time critical callbacks anyway…
                      1 Reply Last reply Reply Quote 0
                      • E
                        elanhickler
                        last edited by

                        referring to globals by number is going to be difficult for large projects.

                        the coder will have to create include variables to refer to global numbers

                        include:

                        mynamespace.ColorEnum = {
                            myGlobal : 0,
                            myOtherGlobal : 1,
                            anotherGlobal: 2
                        }
                        
                        

                        first script processor:

                        include("theAbove.js");
                        Engine.setGlobal(mynamespace.myGlobal, a * b + c / e - f );
                        
                        

                        another script processor:

                        include("theAbove.js");
                        var output = Engine.getGlobal(mynamespace.myGlobal, a * b + c / e - f );
                        
                        

                        Seems like a lot to do a simple thing. I think a use-case for global variables would be when you want to take a knob value and do a calculation ONCE, and send that calculation to other knobs or values/parameters in other scripts.

                        1 Reply Last reply Reply Quote 0
                        • Christoph HartC
                          Christoph Hart
                          last edited by

                          @3kbojkje:

                          If a function references a variable from a script that was deleted, then surely you just throw an error "variable is undefined"

                          This is the default behaviour - a crash only occures when storing an interface widget as global variable and then deleting the script.
                          However this really leads to unpredictable mess behaviour and would be a cause of many irritations and bugs. Consider this example:

                          var velocity = 0;
                          
                          var calculateStuff = function()
                          {
                              Synth.setParameter(12, velocity);
                          }
                          
                          function onNoteOn()
                          {
                              velocity = Message.getVelocity();
                          }
                          
                          

                          You can't use this function in another script without knowing that it needs a variable called 'velocity' and the variable needs to be updated every noteOn. This is a heavy violation of the encapsulation principle that is the main reason for functions.

                          @3kbojkje:

                          Seems like includes would be better for this.

                          Yes indeed. I think limiting global variables to values and arrays only is the least complicated thing to do.

                          About the index vs named identifier stuff for global access, I will make some performance tests, but the snippet you wrote does not look too complicated to me (especially in combination with the include functionality). But I think you have the syntax wrong. This example works:

                          // This stuff can be outsourced to an external enum file
                          var globals = {};
                          globals.BUTTON_VALUE = 4;
                          
                          Engine.setGlobal(globals.BUTTON_VALUE, 55);
                          Engine.getGlobal(globals.BUTTON_VALUE);
                          
                          
                          1 Reply Last reply Reply Quote 0
                          • E
                            elanhickler
                            last edited by

                            the include file:
                            Edit: So this would all be built into the API. In this case, the developer only has to do the globals.BUTTON_VALUE part. –>

                            function global(arguments) 
                            {
                                if (arguments.length == 1) Engine.getGlobal(arguments[0]);
                                else Engine.setGlobal(arguments[0],arguments[1]);
                            };
                            var globals = {};
                            globals.BUTTON_VALUE = 4;
                            
                            
                            // get
                            var local_button_value = global(BUTTON_VALUE);
                            
                            // set
                            global(BUTTON_VALUE, some_formula);
                            
                            

                            Is there any (feasible) way you can make Engine.SetGlobal() and Engine.GetGlobal() something that happens in the background so the developer can just focus on referring to global variables by their variable name? The above code is my attempt to do that.

                            1 Reply Last reply Reply Quote 0
                            • Christoph HartC
                              Christoph Hart
                              last edited by

                              OK I added another way of handling global variables:

                              Globals.x = 5;
                              
                              Console.print(Globals.x); // 5 in every script
                              
                              

                              It can't be simpler than that. 'Globals' is a object that is hold by the engine and their properties ( = every variable that is stored in it) are reference counted. You can use this system like normal variables (so everything from functions to objects and script interface widgets can be stored here. However because it uses reference counting, deleting a script processor with widget saved into the global storage does not delete the widget (not until the global property is overwritten or cleared).

                              But as always: with great power comes great responsibility (it is a little bit like the getter/setter paradigm vs. direct access). I think I keep both styles for now (the overhead is negible) and everybody can use the system he prefers.

                              1 Reply Last reply Reply Quote 0
                              • E
                                elanhickler
                                last edited by

                                sweet, perfect!

                                1 Reply Last reply Reply Quote 0
                                • Christoph HartC
                                  Christoph Hart
                                  last edited by

                                  And I added Autocomplete support for this style. Pressing Escape shows "Globals" under the API classes and if you press Escape again, you'll see all global properties with their current value (just like the local properties).

                                  Also the variables are shown in the variable watch window (with the prefix 'Globals.')

                                  Kind of starting to like this too :)

                                  1 Reply Last reply Reply Quote 0
                                  • E
                                    elanhickler
                                    last edited by

                                    :o :D

                                    1 Reply Last reply Reply Quote 0
                                    • First post
                                      Last post

                                    50

                                    Online

                                    1.7k

                                    Users

                                    11.7k

                                    Topics

                                    101.9k

                                    Posts