HISE Logo Forum
    • Categories
    • Register
    • Login

    Broadcaster attachment design pattern

    Scheduled Pinned Locked Moved General Questions
    25 Posts 3 Posters 255 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.
    • OrvillainO
      Orvillain
      last edited by

      I'm doing something like this:

          inline function addEngineAmpEnvListeners()
          {
              Globals.broadcasters["Engine1AHDSRControl"].addListener("string", "metadata", function(param, value)
              {
                  BroadcasterCallbacks.controlLinkedAmpEnvParameter("Engine1", param, value);
              });
      
              Globals.broadcasters["Engine2AHDSRControl"].addListener("string", "metadata", function(param, value)
              {
                  BroadcasterCallbacks.controlLinkedAmpEnvParameter("Engine2", param, value);
              });
      
              Globals.broadcasters["Engine3AHDSRControl"].addListener("string", "metadata", function(param, value)
              {
                  BroadcasterCallbacks.controlLinkedAmpEnvParameter("Engine3", param, value);
              });
          }
      

      And I'm doing this in quite in a few functions. But if I do something like this:

          inline function addEngineAmpEnvListeners()
              {
                  local engines = ["Engine1", "Engine2", "Engine3"];
          
                  for (engine in engines)
                  {
                      local capturedEngine = engine;
                      Globals.broadcasters[engine + "AHDSRControl"].addListener("string", "metadata", function(param, value)
                      {
                          BroadcasterCallbacks.controlLinkedAmpEnvParameter(capturedEngine, param, value);
                      });
                  }
              }
      

      Then I get the error:

      BroadcasterCallbacks.controlLinkedAmpEnvParameter(capturedEngine, param, value);
      

      I can't declare capturedEngine as a const or var. If I don't capture the engine variable inside the for loop, then I get a different error:

      Interface:! Engine3AHDSRControl: BroadcastersBuilder.js (291): Illegal iterator target 
      

      I would guess this is due to some async closure issue, but I don't really know. Fundamentally, how can I do the above without needing to rely on a local variable?

      The callback works like this:

      	inline function controlLinkedAmpEnvParameter(engineName, param, value)
      	{
      		local paramMap = {
      			"Monophonic": 0,
      			"Retrigger": 1,
      			"Attack": 2,
      			"AttackLevel": 3,
      			"Hold": 4,
      			"Decay": 5,
      			"Sustain": 6,
      			"Release": 7,
      			"AttackCurve": 8,
      			"DecayCurve": 9,
      			"EcoMode": 10,
      		};
      
      		local engine = Globals.engines[engineName];
      		
      		for (mode in Globals.modeMap)
      		{
      			local modeData = engine["modes"][mode];
      
      			local parts = param.getId().split("_");
      			local paramName = parts[parts.length - 1];
      			local target = modeData["ahdsrEnvObj"];
      			target.setAttribute(paramMap[paramName], value);
      		}
      	}
      
      d.healeyD 1 Reply Last reply Reply Quote 0
      • d.healeyD
        d.healey @Orvillain
        last edited by

        @Orvillain Don't use globals. I thought they might be useful for broadcasters too but nope

        Libre Wave - Freedom respecting instruments and effects
        My Patreon - HISE tutorials
        YouTube Channel - Public HISE tutorials

        OrvillainO 1 Reply Last reply Reply Quote 0
        • OrvillainO
          Orvillain @d.healey
          last edited by

          @d-healey You mean my references to Globals.xxxxx ??? Don't worry about that, that's just a custom namespace. It might as well be called JamDoughnut.

          I'm using these broadcasters to link all parameters across all sound generators within a container - the containers being Engine1/Engine2/Engine 3.

          Christoph HartC d.healeyD 3 Replies Last reply Reply Quote 0
          • Christoph HartC
            Christoph Hart @Orvillain
            last edited by

            @Orvillain said in Broadcaster attachment design pattern:

            Don't worry about that

            Do worry about that, this might mess up your code because the HiseScript parser might think you're calling into globals.

            This is the same as back when we added a Settings object and everybody and his dog complained about their Settings namespace being trashed...

            At some point I'll need to add more strictness to reserved keywords (eg. I've run into an issue this week where a scriptnode network messed up the entire module tree if you name a parameter Type because that's a reserved attribute for HardcodedFX modules).

            1 Reply Last reply Reply Quote 0
            • d.healeyD
              d.healey @Orvillain
              last edited by d.healey

              @Orvillain said in Broadcaster attachment design pattern:

              Don't worry about that, that's just a custom namespace.

              You might run into issues with that name since it's a built in HISE keyword for declaring global variables

              adf62f3a-a3fa-499c-b96e-fd9b79295af4-image.png

              ^ what Christoph said :)

              Libre Wave - Freedom respecting instruments and effects
              My Patreon - HISE tutorials
              YouTube Channel - Public HISE tutorials

              1 Reply Last reply Reply Quote 0
              • OrvillainO
                Orvillain
                last edited by

                Oh, I thought the HISE namespace was called 'Global' - not 'Globals' ???? If I'm wrong, I will change the name to JamDoughnut after all!!!

                OrvillainO 1 Reply Last reply Reply Quote 0
                • OrvillainO
                  Orvillain @Orvillain
                  last edited by

                  OK. Fixed that. I've gone for SharedData instead.

                  Existing question still outstanding - is there a pattern for running multiple addListener() calls without needing to rely on local variables ???

                  d.healeyD 1 Reply Last reply Reply Quote 0
                  • d.healeyD
                    d.healey @Orvillain
                    last edited by

                    @Orvillain said in Broadcaster attachment design pattern:

                    I'm using these broadcasters to link all parameters across all sound generators within a container - the containers being Engine1/Engine2/Engine 3.

                    Broadcasters can't be shared across modules

                    However there is the module parameter syncer which might do it - the documentation isn't clear to me - https://docs.hise.dev/scripting/scripting-api/broadcaster/index.html#addmoduleparametersyncer

                    There are also global cables which might be useful for you.

                    Libre Wave - Freedom respecting instruments and effects
                    My Patreon - HISE tutorials
                    YouTube Channel - Public HISE tutorials

                    OrvillainO 1 Reply Last reply Reply Quote 0
                    • d.healeyD
                      d.healey @Orvillain
                      last edited by

                      @Orvillain Is the goal to have one set of knobs that control a bunch of envelopes?

                      Libre Wave - Freedom respecting instruments and effects
                      My Patreon - HISE tutorials
                      YouTube Channel - Public HISE tutorials

                      1 Reply Last reply Reply Quote 0
                      • OrvillainO
                        Orvillain @d.healey
                        last edited by Orvillain

                        What I've got is working perfectly. I just think the code is a bit messy. It isn't just for envelopes, no. I'm doing the same pattern for a whole variety of areas in my synth.

                        But the one example is:

                            inline function addEngineAmpEnvListeners()
                            {
                                SharedData.broadcasters["Engine1AHDSRControl"].addListener("string", "metadata", function(param, value)
                                {
                                    BroadcasterCallbacks.controlLinkedAmpEnvParameter("Engine1", param, value);
                                });
                        
                                SharedData.broadcasters["Engine2AHDSRControl"].addListener("string", "metadata", function(param, value)
                                {
                                    BroadcasterCallbacks.controlLinkedAmpEnvParameter("Engine2", param, value);
                                });
                        
                                SharedData.broadcasters["Engine3AHDSRControl"].addListener("string", "metadata", function(param, value)
                                {
                                    BroadcasterCallbacks.controlLinkedAmpEnvParameter("Engine3", param, value);
                                });
                            }
                        

                        And the callback I'm attaching to this is:

                        	inline function controlLinkedAmpEnvParameter(engineName, param, value)
                        	{
                        		local paramMap = {
                        			"Monophonic": 0,
                        			"Retrigger": 1,
                        			"Attack": 2,
                        			"AttackLevel": 3,
                        			"Hold": 4,
                        			"Decay": 5,
                        			"Sustain": 6,
                        			"Release": 7,
                        			"AttackCurve": 8,
                        			"DecayCurve": 9,
                        			"EcoMode": 10,
                        		};
                        
                        		local engine = SharedData.engines[engineName];
                        		
                        		for (mode in SharedData.modeMap)
                        		{
                        			local modeData = engine["modes"][mode];
                        
                        			local parts = param.getId().split("_");
                        			local paramName = parts[parts.length - 1];
                        			local target = modeData["ahdsrEnvObj"];
                        			target.setAttribute(paramMap[paramName], value);
                        		}
                        	}
                        

                        It works splendidly. I just don't like that initial bit of code, because I'm repeating myself and specifying a different engine name each time.

                        d.healeyD 1 Reply Last reply Reply Quote 0
                        • d.healeyD
                          d.healey @Orvillain
                          last edited by d.healey

                          @Orvillain said in Broadcaster attachment design pattern:

                          What I've got is working perfectly.

                          Have you tested it in a compiled project?

                          The way I handle this kind of thing is to make a controller script. Then I just connect the knobs to the controller using processor/parameter ID.

                          Here's an example of an AHDSR controller that will work for any number of envelopes, you can add/remove envelopes and no change to the script is required, it's completely self contained.

                          It avoids that repetition you've ran into and can be reused in many projects.

                          Libre Wave - Freedom respecting instruments and effects
                          My Patreon - HISE tutorials
                          YouTube Channel - Public HISE tutorials

                          OrvillainO 2 Replies Last reply Reply Quote 0
                          • OrvillainO
                            Orvillain @d.healey
                            last edited by

                            @d-healey said in Broadcaster attachment design pattern:

                            Have you tested it in a compiled project?

                            Yes. Works fine!

                            1 Reply Last reply Reply Quote 0
                            • OrvillainO
                              Orvillain @d.healey
                              last edited by Orvillain

                              @d-healey said in Broadcaster attachment design pattern:

                              The way I handle this kind of thing is to make a controller script. Then I just connect the knobs to the controller using processor/parameter ID.

                              Here's an example of an AHDSR controller that will work for any number of envelopes, you can add/remove envelopes and no change to the script is required, it's completely self contained.

                              It avoids that repetition you've ran into and can be reused in many projects.

                              Without bludgeoning you with loads of code, it is hard to explain. But I'm essentially dynamically creating parameter groups based on their names. Then I create a broadcaster for each group. Then I attach the broadcaster to each parameter based on the broadcaster type that is desired, then I am adding the listener callbacks to the broadcaster.

                              It is all very cool and modular. I just don't like this particular 3-engine manual duplicate approach, because if I want to move to 5 engines, then I need to do a bunch of manual work.

                              I was hoping for a design pattern where I could call the right number of addListener commands, against the right number of broadcaster or parameter groups. I can't just run a loop and use local variables, because local variables aren't allowed in inline functions in this manner.

                              d.healeyD 1 Reply Last reply Reply Quote 0
                              • d.healeyD
                                d.healey @Orvillain
                                last edited by

                                @Orvillain Engine1AHDSRControl These are referring to AHDSR envelopes?

                                Libre Wave - Freedom respecting instruments and effects
                                My Patreon - HISE tutorials
                                YouTube Channel - Public HISE tutorials

                                OrvillainO 1 Reply Last reply Reply Quote 0
                                • OrvillainO
                                  Orvillain @d.healey
                                  last edited by Orvillain

                                  @d-healey said in Broadcaster attachment design pattern:

                                  @Orvillain Engine1AHDSRControl These are referring to AHDSR envelopes?

                                  No, it refers to a broadcaster ID created in a previous step.

                                  I'm not going to post everything I'm doing, but imagine it more like this:

                                      inline function addEngineLinkedFeatureListeners()
                                      {
                                          SharedData.broadcasters["ENGINE-NAME-HERE-FEATUREHERE"].addListener("string", "metadata", function(param, value)
                                          {
                                              BroadcasterCallbacks.controlLinkedFEATUREParameter(ENGINE-NAME-HERE, param, value);
                                          });
                                      }
                                  

                                  I want to do the above, where the engine name or identifier increments. But I can't do that with a for loop, because it complains about it not liking local variables.

                                  Engine1AHDSRControl is a broadcaster id. and depending on the number of engines, I automatically create these. So for 3 engines I get:

                                  Engine1AHDSRControl
                                  Engine2AHDSRControl
                                  Engine3AHDSRControl

                                  And if I change the number of engines, my code will dynamically create the right amount of parameters, the right amount of broadcasters, and the right amount of parameter/component groups, and then it will attach the correct broadcaster to the correct component group.

                                  Next step is to add the listener to the broadcaster, in a dynamic way.

                                  d.healeyD 1 Reply Last reply Reply Quote 0
                                  • d.healeyD
                                    d.healey @Orvillain
                                    last edited by

                                    @Orvillain This error?

                                    Interface:! Engine3AHDSRControl: BroadcastersBuilder.js (291): Illegal iterator target

                                    The error is being triggered only for Engine3 so that implies the for loop in general is fine, but there is a more specific issue.

                                    Libre Wave - Freedom respecting instruments and effects
                                    My Patreon - HISE tutorials
                                    YouTube Channel - Public HISE tutorials

                                    OrvillainO 1 Reply Last reply Reply Quote 0
                                    • OrvillainO
                                      Orvillain @d.healey
                                      last edited by

                                      @d-healey Yeah, there's some underlying closure thing going on I think.

                                      d.healeyD 1 Reply Last reply Reply Quote 0
                                      • d.healeyD
                                        d.healey @Orvillain
                                        last edited by d.healey

                                        @Orvillain

                                        for (engine in engines)
                                        {
                                            local capturedEngine = engine;
                                        
                                            Globals.broadcasters[engine + "AHDSRControl"].addListener("string", "metadata", function(param, value)
                                            {
                                                BroadcasterCallbacks.controlLinkedAmpEnvParameter(capturedEngine, param, value);
                                            });
                                        }
                                        

                                        Oh I just realised the issue - the function within your loop doesn't have access to local variables outside of the function. So you'll need to find another structure.

                                        One workaround is to use a lambda variable, I try to avoid this because I find their use usually indicates a structural issue. But it's a quick and dirty fix.

                                        function[capturedEngine](param, value)

                                        Another suggestion is instead of using an array of strings and a for in loop. Just use a for loop that counts from 0 to the number of engines you have. Then you can just declare a NUM_ENGINES constant at the top of your script and use it whenever you need to know the number of engine you have.

                                        Libre Wave - Freedom respecting instruments and effects
                                        My Patreon - HISE tutorials
                                        YouTube Channel - Public HISE tutorials

                                        OrvillainO 1 Reply Last reply Reply Quote 0
                                        • OrvillainO
                                          Orvillain @d.healey
                                          last edited by

                                          @d-healey said in Broadcaster attachment design pattern:

                                          Another suggestion is instead of using an array of strings and a for in loop. Just use a for loop that counts from 0 to the number of engines you have. Then you can just declare a NUM_ENGINES constant at the top of your script and use it whenever you need to know the number of engine you have.

                                          If I try that, for example (different feature area this time):

                                          		for (i = 1; i < SharedData.engines.length; i++)
                                                  {
                                                      SharedData.broadcasters["Engine" + i + "ModeControl"].addListener("string", "metadata", function(index)
                                          		{
                                          			BroadcasterCallbacks.setActiveModeForSlot("Engine" + i, index);
                                          		});
                                                  }
                                          

                                          Then I get:

                                          Interface:! BroadcastersBuilder.js (260): Can't reference local variables in nested function body {{SW50ZXJmYWNlfEJyb2FkY2FzdGVyc0J1aWxkZXIuanN8ODQ3MHwyNjB8NTc=}}
                                          
                                          
                                          d.healeyD 1 Reply Last reply Reply Quote 0
                                          • d.healeyD
                                            d.healey @Orvillain
                                            last edited by

                                            @Orvillain The error is basically saying what I said.

                                            You can't access a local variable declared outside of the function.

                                            Libre Wave - Freedom respecting instruments and effects
                                            My Patreon - HISE tutorials
                                            YouTube Channel - Public HISE tutorials

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

                                            15

                                            Online

                                            1.8k

                                            Users

                                            12.1k

                                            Topics

                                            105.5k

                                            Posts