InterprocessConnection: How does it works?

I Started to work around InterprocessConnection trying with a project from scratch just to test how to use interprocessConnection:

MainComponent.h:

#pragma once

#include <JuceHeader.h>

//==============================================================================
/*
    This component lives inside our window, and this is where you should put all
    your controls and content.
*/
class MainComponent  : public juce::Component
{
public:
    //==============================================================================
    MainComponent();
    ~MainComponent() override;

    //==============================================================================
    void paint (juce::Graphics&) override;
    void resized() override;
    
    bool connect();

private:

    TextButton send {"send"}, connectBtn{"connect"}, disconnect{"disconnect"};
    
    class InterprocessMixerConnection  : public InterprocessConnection
    {
    public:
        
        InterprocessMixerConnection (MainComponent& mainComponent);

        void connectionMade() override;
        void connectionLost() override;
        void messageReceived (const MemoryBlock& message) override; 

    private:
        
        MainComponent& owner;
        
        JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (InterprocessMixerConnection)
    };
    
    InterprocessMixerConnection mixerConnection {*this};

    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};

MainComponent.cpp:

#include "MainComponent.h"

//==============================================================================
MainComponent::MainComponent()
{
    setSize (600, 400);
    addAndMakeVisible(connectBtn);
    addAndMakeVisible(send);
    addAndMakeVisible(disconnect);
    
    connectBtn.onClick = [this]() {
        if (mixerConnection.isConnected())
        {
            DBG("yet connected");
        } else {
            if (connect())
            {
                DBG("succesfully connected");
            } else {
                DBG("not connected");
            }
        }
    };
    
    send.onClick = [this]() {
        const String text ("sender message");
        MemoryBlock messageData (text.toUTF8(), (size_t) text.getNumBytesAsUTF8());
        mixerConnection.sendMessage(messageData);
    };
    
    disconnect.onClick = [this]() {
        mixerConnection.disconnect();
    };
}

MainComponent::~MainComponent()
{
    mixerConnection.disconnect();
}

bool MainComponent::connect()
{
    bool connected = false;
    
    mixerConnection.createPipe("Ayra Node Graph", 5000, true);
    
    if (!mixerConnection.isConnected())
        if (mixerConnection.connectToPipe("Ayra Node Graph", 5000))
            connected = true;
    
    return connected || mixerConnection.isConnected();
}

//==============================================================================
void MainComponent::paint (juce::Graphics& g)
{
    g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
}

void MainComponent::resized()
{
    auto area = getLocalBounds();
    connectBtn.setBounds(area.removeFromLeft(area.getWidth()/3));
    send.setBounds(area.removeFromLeft(area.getWidth()/2));
    disconnect.setBounds(area);
}

//MARK: InterprocessMixerConnection ==============================================================================

MainComponent::InterprocessMixerConnection::InterprocessMixerConnection (MainComponent& mainComponent) : InterprocessConnection (true), owner (mainComponent)
{
}

void MainComponent::InterprocessMixerConnection::connectionMade()
{
    DBG("connectionMade()");
}

void MainComponent::InterprocessMixerConnection::connectionLost()
{
    DBG("connectionLost()");
}

void MainComponent::InterprocessMixerConnection::messageReceived (const MemoryBlock& message)
{
    DBG("messageReceived(\n" + message.toString() + "\n)");
}

I suppose to open two instances of this app → click connect on both → click send to one of the two and get in the other printed the message ā€œsender messageā€.
What really happens is that open just only one instance i click on connect and it connects, after a while I get in console connectionLost() and if I click on Send button my app starts work for a while (I can see working mouse icon on my mac) and after again is displayed connectionLost().

My final purpose is to get more apps connected to the same pipe and get them send messageData at their sliders value-changes so all sliders of those app are sync together (immagine widgets slider demo on juce DemoRunner where slider are synched together but with those slider in different apps )

If I read the docs correctly, one instance is the ā€˜server’ and needs to call createPipe (), and the others are ā€˜client’ and they need to call connectToPipe (). But it looks like you are calling create and connect in all instances?

1 Like

I don’t use a server up to now because for my purpose I don’t think I need it (…but obviously this is my first approach to the problem, so I don’t know), my approach here is: the first app launched creates the pipe, so in my ā€œconnect()ā€ func I call:

    mixerConnection.createPipe("Ayra Node Graph", 5000, true); // with this true teorically if the pipe is already created the func doesn't create another one

After all apps connect to that pipe.

Launching just one app and calling connect I can see the pipe is created and after connected, but after a while it is destroyed (also the sendMessage func of the connection seems works not correctly…)

this in console:


succesfully connected
connectionMade()

connectionLost()

succesfully connected
connectionMade()

connectionLost()

I am not sure we are understanding each other. The ā€˜pipe’ architecture is a server/client connection. Not in the ā€˜network’ context of server, just that one instance is in charge of calling create (server), and the other instance (client, of which only one client can exist, as far as I know) calls connect. You do not call connect from the instance that calls create, and you do not call create from the instance that calls connect. In the code you pasted, it looks like all instances call create and connect. Unless you have changed this, that is the first issue.

1 Like

Ok thank you … now it’s clear…
Maybe you can suggest me a right way to do this, because at this point I don’t think interprocess should do what I need …

scenario is this:

I have more instances of a plugin with a slider that drives panpot and a slider that drives gain.
I need to have a standAlone gui app with as many sliders as those contained in the plugins (for example if there are 3 instances standAlone app has 6 sliders, 3 for pan and 3 for gain).
When I change value in a slider of a plugin also the slider in standalone app shud change his value and viceversa.

Do you have any idea ? if StandAlone app is the server it cannot send messages…

Fastest idea off the top of my head; since you said there is a stand alone app. The stand alone app creates a ā€˜initiate connection pipe’, an instance of the plugin connects to that. THe stand alone creates a new pipe for this connection, and send the name to the client. The client then closes the initial connection pipe and opens the new one to talk to the stand alone. Now another instance can open the initial connection pipe, get a new pipe name for it’s comms,etc… of course, the plug ins will need to have some sort of loop when trying to connection to the initial pipe, as it might be in use. But, what happens when the stand alone isn’t running? There needs to be a way for this to be handled. I’m sure there are other edge cases to consider, but that is my quick and dirty idea. Also, you should not access the pipes from the audio callback. I hope this helps some. :nerd_face: :art:

1 Like

OK clear, I’ll work around this! but in the meantime I found an old example that doesn’t is present yet in DemoRunner: InterprocessCommsDemo also with this connect to pipe doesn’t work because the connection is lost immediately (while connection by socket is ok)