HISE Logo Forum
    • Categories
    • Register
    • Login

    Named Pipe idea

    Scheduled Pinned Locked Moved Presets / Scripts / Ideas
    21 Posts 4 Posters 969 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.
    • J
      jdurnil
      last edited by

      I had the routing all wrong my offline container didn't do what I thought it did so that last snippet was trash. I copied the sidechain2 snippet because it does exactly what I need it to

      HiseSnippet 1738.3oc6Y80TabCDWGfbBNPZRZZe1ORll5w2YCFl9PiMFm5.FbroLIOkQbmLV36jtb2Y.mN8g9V+X0OR8aP6p6O1m.avwAnsyTyLLVZWIsZ2e6OsRtkmvj56K7PZKe3PWJRaEbmg7fda2iv3nF0PZOBGP8C3BK55npCcI99TKjl1huVpf1xKgB+7m+XUhMgaRG2EBcjfYR2i4vBF2aqWsKy1tNwhdHyIk1kdUCSAeagsX.XLKhKfbIl8ImP2mHUaALRKyNVr.gWm.BXQHskpJrF1om3bdj9Gw7YGaSkMzQcfIJp65BaKoEK6EscOlsUqjMsOBlkVicAKF4BdNtIyhMp+wthmDJH23Qj1ensfp4snh4om17Jjx7lfIokxjVJxjdJtioGyMXrjnPSCd.0qKAb6oMkHcQZ+AdaAn.OHuCoOstGzXz.VaiBEdYN3eu3G5NfaFvD7bB99h.5A70dQ1eI6xY+0r4trntcmnL4x3IrsodSTrLR6ccCbM9.mioduL2YD6AzQJBaeUeJd59zzgbynccJEE7FbVvAtT9z.BnXWE7setQMR.QFHh6Czyk5EvjlfVM5Y.pNJrrLtF0uefvEv0WIlAnEg0.aRfJDRl2DK.7AJwMYvg6yBFlNu5VCWMql3SwsXAl8lrMtvDrQvScWXiwYiqh2oaWpYvXCbIb82cOj5giV+rwodvZJW7Uhalq96zULh8G3Ti46ZSFVc.XvdR3p1BvVJIa72wiQ9tdTWhG8PQKP+07INt1z1fw+xbGaKL62g8I5USWbirspRMVyrGgyo19ySVUlY1coe8Qq1hAAL9IMIAdrK..A3C5.72lzsisNoOPhRhaWP1VBX5P4VgM9K3SrPcYasXg5WRnwHuIHz3RBKJauXrvhIBSkQuOM3bgW+vHb72QZOHJb5GFd9P2KfMND0j3csGfKjG9CUw1Vb91BGWVbl.DmC6qkvdnaOAmYJ6JRijMcEGw.XUis2eh3eHgYKSc5LvGHfrNf2ATN7nRIzce3jUHjTmXBgjgsHA8jofRJL.uS8xaNB2qXqigdgfZH50h3AGUFDh7jQN4DmbdZjWGrSGoCI7zzEkxCOALxBvxk3gXmnXoh47DrWTfNerT0E+FVnERQeFkgIaA7GKHWwGi2A.oVvQzgjtGIgpHseCuUw7EEaV8MeW6caLrbaCiZ6J368wtUpW5SCZOrYwx65yOsSgR60duAka2zo26691id+o6T+ccqvK2LeMpc9KNn3gC2cmyNdqCZk+vFMqAcdxaDmvaHda2hCkcUsYil0qQqCce9YmdZk7cMqrUuJ.SzX2IBkxE8.ry.6.FZpArHweVtnQwhETVnnP+MfL9xWnmgk0XY0h4RaSMorynpq4WiAlmSAJ37SpRvYBAjxWBHfjV.ruIiGGxwXcHiqI4hQsMf1cBntR1vD4c5SOOx3hVYo8uJt4vwyYkAAB.lNpjuznuzwwuBK7MgRQg+CJnqtk+VLgSrG5SymVoOSesjVvldQRwDW5zgK2iLefaQuPd912qiTxJjymJfTYWMUIWIaaEbC+ijRMI1I4Z.MkBCPHrR+tG18Xk.v+k8+o1TOB2j3C5Tudf5NZ0Q6ntcCti2HZo2H2k3lYxk7vHBS86dFyrXF2T3.GTc2fdWL0ZsZL5UV67D1ZdIHWobkkS+dLbqkIEWKNMWagKw0VZBbsxsYF4MDnHkwglLAdFrg5rtLzA7QYleNLC5Eh9nWZqB55anqPpKUvvXqsJTZ8MLLJVZyxatd7kcq6Q+3.nvogIlyyvaVdi7gSkQ40Wu3VkTrM7zObICVeioriWIbcx0VVvGRY3nY0YpOUm4qgCll9jtzMMoQkEzpGw+ZBIyzrjI7dnISxpR8LJVzXSci0mdlbVruM4Lpj.6ebtsvajLS7OYv9PU2pV7iGUQanraw5YWQ9lKbZ3UwRbuYwdQ0UcE13awCteD1mYQmPIieSJ1uwp7ky.FyqabevqK7Xmv.L1cBu9sZX3oiNCxaR0ReacTjlh6YD75RqUBHOwTtkpZelXXxhqSoVGSL6eYBu6+RPxhaue0CB6395lMyjOR8lKoT+ZHgeLN9cfnV+qnHynKY8ObMlKG+tE5++CWL8SXTu4cDEdzsUPeIWCO7wxJnqlkG914wGC5iTZFys2vZhu7vHSTJWM+XtfOUEh9Njv29a9dX+6423T1dpuwYhvwuwoCwzS7AynG7U5VdXXO.9lG96lsLDgg14zQmEWT9nGs2gYw9foo5TckAZLuCr37NvRy6.WedG3Fy6.KOuCbyadfxepw32xRlzfPMasSziQnsCmbrczu5.5uAGIxKxB
      
      1 Reply Last reply Reply Quote 0
      • J
        jdurnil
        last edited by

        The idea is to take in signal from another instance but not from a sidechain. You can communicate anything really but osc may be a simpler design if you’re just looking to pass around parameters and such. To take in an actual signal block by block interprocess connection is perfect. My plugin already uses osc to communicate with a daemon I run and it also communicates via osc to a networked mixed reality app. I am worried about delay as I haven’t put my named pipe code(I have it in non HISE JUCE app) to use yet but I think the sidechain2 snippet I modeled after is the perfect setup if it will work at all. Any ideas are more than welcome.

        A 1 Reply Last reply Reply Quote 0
        • A
          aaronventure @jdurnil
          last edited by

          @jdurnil my issue with OSC in hise is that while you can use it to communicate between different plugins, you cannot communicate between the instances of the same plugin as you have to sort of hard code the ports.

          Some sort of crosstalk is definitely a good thing. My current implementation using the HISE API is to write to a file in APPDATA, and for all instances to have a listener timer that checks for new files. If it finds any, it checks for id of the instance to make sure it was sent from another instance, a few other checks as well.

          Works pretty well for sending parameter data, you can even create crosstalk groups (using strings) so that only some instances exchange data like this. I just don't know the performance implications of constantly being on the lookout for external json files (except during transport playback).

          1 Reply Last reply Reply Quote 0
          • J
            jdurnil
            last edited by

            But if you did hard code the ports wouldn’t it work between instances, I read callback does not function for globalrouting in same instance so in theory if you hard coded the ports it should work

            A 1 Reply Last reply Reply Quote 0
            • A
              aaronventure @jdurnil
              last edited by

              @jdurnil https://forum.hise.audio/topic/9119/osc-for-crosstalk-one-manager-for-sending-another-for-receiving/

              1 Reply Last reply Reply Quote 0
              • J
                jdurnil
                last edited by

                Thank you. I read through the post and it sounds like there is one osc scenario in there that worked for you, I am only concerned with two instances communicating and I have a decision tree that sets one to master and one to slave, any other instances have code that ignores running any code in a callback. If I can have one send port and one receive port and run code according to the instance designation as master or slave it might work for me. I don’t know if not I’ll roll my own with named pipe.

                A 1 Reply Last reply Reply Quote 0
                • A
                  aaronventure @jdurnil
                  last edited by

                  @jdurnil yes the master/slave case should work using OSC.

                  my issue was that I wanted all instances to be able to send as well as receive.

                  J 1 Reply Last reply Reply Quote 0
                  • J
                    jdurnil @aaronventure
                    last edited by

                    @aaronventure you should really look into JUCEs interprocess connnection server, it’s available in juce core in HISE and is easy to use and works really well.

                    A 1 Reply Last reply Reply Quote 0
                    • A
                      aaronventure @jdurnil
                      last edited by

                      @jdurnil I am a JUCE noob so I have no idea how would I do that or implement it into HISE.

                      J 1 Reply Last reply Reply Quote 0
                      • J
                        jdurnil @aaronventure
                        last edited by

                        @aaronventure I am writing some code now that will demonstrate it, it really is quite simple I’d be happy to share it with you when I’m done.

                        J 1 Reply Last reply Reply Quote 2
                        • J
                          jdurnil @jdurnil
                          last edited by

                          @jdurnil if it’s something people want I can ask Christopher if he’d allow me to write it into HISE.

                          J d.healeyD 2 Replies Last reply Reply Quote 0
                          • J
                            jdurnil @jdurnil
                            last edited by

                            @jdurnil auto correct I meant Cristoph

                            A 1 Reply Last reply Reply Quote 0
                            • A
                              aaronventure @jdurnil
                              last edited by

                              @jdurnil Here are the features of my file-based implementation:

                              "sentBy": instanceId, // checks against this so it doesn't execute its own changes
                              "targetGroup": groupId, // string from a user-facing label, for grouping instances
                              "time": Date.getSystemTimeMs(), // checks against this with the cleanup listener which deletes all files older than x
                              "componentId": componentId, // used by the listener to apply the value to the correct control
                              "value": value, 
                              "changeId": generateInstanceId(), // stores this for each component on execution and checks against it, to avoid repeatedly executing the same changes
                              
                              J 1 Reply Last reply Reply Quote 0
                              • d.healeyD
                                d.healey @jdurnil
                                last edited by d.healey

                                @jdurnil said in Named Pipe idea:

                                if he’d allow me to write it into HISE.

                                Possibly it could be added to the snippet browser - or if it's something hardcoded then upstreamed

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

                                1 Reply Last reply Reply Quote 0
                                • J
                                  jdurnil
                                  last edited by

                                  I was thinking it could be added to the scripting api in globalroutingmanager

                                  d.healeyD 1 Reply Last reply Reply Quote 1
                                  • J
                                    jdurnil @aaronventure
                                    last edited by

                                    @aaronventure it looks like you have something working for and that was very clever to come up with that, but I do think it would be cleaner with named pipe

                                    Christoph HartC 1 Reply Last reply Reply Quote 0
                                    • d.healeyD
                                      d.healey @jdurnil
                                      last edited by

                                      @jdurnil That would be even better

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

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

                                        @jdurnil You could give it a shot, but I think it will be more efficient if I'm writing the API wrapper as there is quite a load of glue boilerplate code necessary (also the thread synchronization must fit into the threading model of the HISE scripting engine which is not an easy task for someone that isn't too familiar with the codebase).

                                        Let's talk the API first before I implement this. Would something like this work?

                                        // use the global routing manager for handling named pipes, good idea
                                        const var rm = Engine.getGlobalRoutingManager()
                                        
                                        // Creates a pipe or connects to the existing pipe. mySender is the ID of the instance
                                        const var pipe = rm.openNamedPipe("mySender", "myPipe");
                                        
                                        // Send a message, can be any seriazable JSON object (primitive values, strings, arrays or JSON objects)
                                        pipe.sendMessage([1, 2, 3]);
                                        
                                        // Receive a message (on another instance
                                        pipe.setCallback(function(id, data)
                                        {
                                           if(id == "mySender")
                                               Console.print(trace(data));
                                        }
                                        

                                        Once open, the NamedPipe would spawn a thread that polls for new data and calls the function when there is something coming in from another sender ID. From a performance perspective this is certainly less effort than periodically reading a file and check for changes.

                                        J A 2 Replies Last reply Reply Quote 1
                                        • J
                                          jdurnil @Christoph Hart
                                          last edited by

                                          @Christoph-Hart yes it would, that was pretty much what I was thinking

                                          1 Reply Last reply Reply Quote 0
                                          • A
                                            aaronventure @Christoph Hart
                                            last edited by aaronventure

                                            @Christoph-Hart

                                            This is my entire class. It works great. I have a value change broadcaster for all my controls, and it checks for the condition (key modifiers) before setting a canPush variable for the component.

                                            If that's true, it simply calls pushValueChange which stores the JSON file. If crosstalk is enabled in an instance, it has a listener timer that checks the appdata folder for new files. I described the properties in the previous post.

                                            The setting of groupId isn't implemented, but it would likely be an argument of the pushValueChange function.

                                            That's pretty much the only user facing method needed: sending the data and being able to specify targets (groups).

                                            If string operations are an issue, you can add a method setInstanceGroupName or something like that that will take in a string or an int, and if it's a string, it'll convert it to an int code. This would be called when the user is doing label edits in the GUI for naming the instance.

                                            namespace Crosstalk
                                            {
                                            	// Crosstalk system using timers and a local JSON file
                                            	// Instance ID is randomly generated using system time and randomization
                                            	// Instances check themselves in by creating a file with their id and stamping it with signin time
                                            	// They continuously update the file to let everyone know they're still here
                                            	// After 5 seconds of inactivity, the file gets deleted
                                            	
                                            	// ===========================================================================================
                                            	
                                            	// System time + random 0-999 int
                                            	inline function generateInstanceId() 
                                            	{
                                            	    return ((Date.getSystemTimeMs()*1000) + Math.randInt(0, 999));
                                            	}
                                            	
                                            	reg i = 0;
                                            	reg obj = 0;
                                            	
                                            	reg enabled = true;
                                            	
                                            	const var instanceId = generateInstanceId();
                                            	const var groupId = "";
                                            	
                                            	const var TIMEOUT_MS = 1600;
                                            	const var LISTENER_MS = 400;
                                            	const var CLEANUP_MS = 1000;
                                            	
                                            	const var lastExecutedInstruction = {};
                                            	
                                            	// the main directory for crosstalk stuff
                                            	const var DIR = FileSystem.getFolder(FileSystem.AppData).createDirectory("Sharing");
                                            	
                                            	// ===========================================================================================
                                            	
                                            	// Write id and time to a check in file used for checking if instance is online
                                            	inline function checkIn()
                                            	{
                                            		local f = DIR.getChildFile("id_" + instanceId);
                                            		
                                            		f.writeObject(
                                            		{
                                            			"id": instanceId,
                                            			"group": groupId,
                                            			"time": Date.getSystemTimeMs(),
                                            		});
                                            		
                                            		if (UI.DEBUG_CROSSTALK) Console.print(instanceId + "checking in... || " + Date.getSystemTimeISO8601(true));
                                            		
                                            		return f;
                                            	}
                                            	
                                            	// Get all check-in files from the directory
                                            	inline function getInstances()
                                            	{
                                            		return FileSystem.findFiles(DIR, "id_*", true);
                                            	}
                                            	
                                            	// Get all value changes from the directory
                                            	inline function getValueChanges()
                                            	{
                                            		return FileSystem.findFiles(DIR, "vc_*", true);
                                            	}
                                            	
                                            	// Check write time for each file and clean up
                                            	inline function cleanUp(fileList)
                                            	{
                                            		local time = Date.getSystemTimeMs();
                                            		local obj = 0;
                                            		
                                            		for (x in fileList)
                                            		{
                                            			obj = x.loadAsObject();
                                            			
                                            			if (obj.id != instanceId) // obviosuly this one is active
                                            			{
                                            				if (Math.abs(time - obj.time) > TIMEOUT_MS)
                                            				{
                                            					x.deleteFileOrDirectory();
                                            					
                                            					if (UI.DEBUG_CROSSTALK) Console.print("Deleted: " + obj.id);
                                            				}
                                            			}
                                            		}
                                            	}
                                            	
                                            	// Push changes for crosstalking
                                            	// Call this in control callbacks, broadcasters, etc.
                                            	inline function pushValueChange(componentId, value)
                                            	{	
                                            		local f = DIR.getChildFile("vc_" + componentId);
                                            		
                                            		f.writeObject(
                                            		{
                                            			"sentBy": instanceId,
                                            			"targetGroup": groupId,
                                            			"time": Date.getSystemTimeMs(),
                                            			"componentId": componentId,
                                            			"value": value,
                                            			"changeId": generateInstanceId(),
                                            		});
                                            	}
                                            	
                                            	// Find and evaluate value change files
                                            	inline function pullAllValueChanges()
                                            	{
                                            		local list = FileSystem.findFiles(DIR, "vc_*", true);
                                            		local obj = {};
                                            		local component = 0;
                                            		
                                            		for (x in list)
                                            		{
                                            			obj = x.loadAsObject();
                                            			component = Content.getComponent(obj.componentId);
                                            			
                                            			if (obj.sentBy != instanceId && obj.value != component.getValue() && groupId == obj.targetGroup && obj.changeId != lastExecutedInstruction[obj.componentId])
                                            			{
                                            				if (isDefined(Animation.valueChangeAnimators[obj.componentId]))
                                            				{
                                            					Animation.startValueChangeAnimator(component, obj.value);
                                            				}
                                            				else 
                                            				{
                                            					component.setValue(obj.value);
                                            					component.changed();
                                            				}
                                            				
                                            				lastExecutedInstruction[obj.componentId] = obj.changeId;
                                            			}
                                            		}
                                            	}
                                            	
                                            	// ===========================================================================================
                                            	
                                            	// create a file with the instance id and sign in time
                                            	// write it once on startup
                                            	checkIn();
                                            	
                                            	cleanUp(getInstances()); // clean up old files on launch
                                            		
                                            	// ===========================================================================================
                                            	
                                            	// Timer that loads the shared data file periodically and executes the crosstalk logic
                                            	const var listener = Engine.createTimerObject();
                                            	listener.setTimerCallback(function()
                                            	{	
                                            		if (TransportHandler.isOffline == false)
                                            		{
                                            			checkIn();
                                            			pullAllValueChanges();
                                            		}
                                            		
                                            		if (UI.DEBUG_CROSSTALK) Console.print(instanceId + ": Listening... || " + Date.getSystemTimeISO8601(true));
                                            	});
                                            	//listener.startTimer(LISTENER_MS);
                                            	
                                            	// Timer that cleans up inactive check-in files
                                            	const var cleaner = Engine.createTimerObject();
                                            	cleaner.setTimerCallback(function()
                                            	{
                                            		if (TransportHandler.isOffline == false)
                                            		{
                                            			cleanUp(getInstances());
                                            			cleanUp(getValueChanges());
                                            		}
                                            		
                                            		if (UI.DEBUG_CROSSTALK) Console.print(instanceId + ": Cleanup... || " + Date.getSystemTimeISO8601(true));
                                            	});
                                            	//cleaner.startTimer(CLEANUP_MS);
                                            	
                                            	// ===========================================================================================
                                            	
                                            	// Transport and Crosstalk interaction
                                            	inline function setTimersOnTransport(playing)
                                            	{
                                            		if (playing)
                                            		{
                                            			listener.stopTimer();
                                            			cleaner.stopTimer();
                                            		}
                                            		else if (enabled)
                                            		{
                                            			listener.startTimer(LISTENER_MS);
                                            			cleaner.startTimer(CLEANUP_MS);
                                            		}
                                            	};
                                            	TransportHandler.transportBroadcaster.addListener(TransportHandler.transportHandler, "Start/Stop Crosstalk Timers on Transport", setTimersOnTransport);
                                            	
                                            	// ===========================================================================================
                                            	
                                            	// Call this when enabling or disabling crosstalk.
                                            	inline function enableCrosstalk(shouldBeEnabled)
                                            	{
                                            		enabled = shouldBeEnabled;
                                            		
                                            		if (shouldBeEnabled)
                                            		{
                                            			cleanUp(getInstances());
                                            			cleanUp(getValueChanges());
                                            			checkIn();
                                            			listener.startTimer(LISTENER_MS);
                                            			cleaner.startTimer(CLEANUP_MS);
                                            		}
                                            		
                                            		else 
                                            		{
                                            			listener.stopTimer();
                                            			cleaner.stopTimer();
                                            		}
                                            	}
                                            	
                                            	// ===========================================================================================
                                            	
                                            	// Store Key Modifiers for Execution Evaluation
                                            	
                                            	// Store modifiers with the broadcaster. Attach to component in control helpers.
                                            	// In the Value Change Broadcaster for Controls, check for key modifier combination before pushing value to crosstalk
                                            	
                                            	const var mouseStatus = {};
                                            	const var keyModifiers = {};
                                            	const var canPush = {};
                                            	
                                            	// Broadcaster definition
                                            	const var mouseBroadcaster = Engine.createBroadcaster({
                                            	  "id": "mouseBroadcaster",
                                            	  "args": ["component", "event"],
                                            	  "tags": [],
                                            	  "comment": "Store key modifier status for evaluating crosstalk execution."
                                            	});
                                            	
                                            	mouseBroadcaster.addListener(keyModifiers, "Store Modifiers", function(component, event)
                                            	{
                                            		if ((event.clicked || event.drag) && !event.rightClick && event.altDown && (event.ctrlDown || event.cmdDown))
                                            		{
                                            			mouseStatus[component.get("id")] = true;
                                            			canPush[component.get("id")] = true;
                                            		}
                                            		else if (event.mouseUp)
                                            		{
                                            			mouseStatus[component.get("id")] = false;
                                            			Content.callAfterDelay(100, function[component]()
                                            			{
                                            				if (mouseStatus[component.get("id")] == false) canPush[component.get("id")] = false; // because value change gets executed after mouseUp
                                            			});
                                            		}
                                            	});
                                            	
                                            	inline function attachToKeyModifierBroadcaster(component)
                                            	{
                                            		mouseBroadcaster.attachToComponentMouseEvents(component, "Clicks, Hover & Dragging", "");
                                            	}
                                            	
                                            	// ===========================================================================================
                                            	
                                            	
                                            }
                                            
                                            1 Reply Last reply Reply Quote 1
                                            • First post
                                              Last post

                                            30

                                            Online

                                            1.7k

                                            Users

                                            11.8k

                                            Topics

                                            103.1k

                                            Posts