FR: Global variables scripting API



  • @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?).



  • 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.



  • 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…


  • 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.



  • @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);
    
    


  • 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.



  • 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.



  • sweet, perfect!



  • 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




Log in to reply
 

8
Online

491
Users

1.7k
Topics

12.9k
Posts