Audio plugin host

Ok, thank you.

I would like to solve it properly, and to me the most important thing to do that is to understand exactly how it should behave. I’m hoping I got it right now? :
On each external device/port change, we want the device restarted, just like it happens when changing active channels in the AudioIODeviceSelectorComponent.

Let me take another look at it then and get back to you.

Yes, that’s right.

Thank you for your patience. How would you feel about something like this:

updateActivePorts() gets called on each port change. Hence no need to call it from open(). It knows to ignore port changes in other clients (every jack port change triggers our callback), and also to restart the device when necessary.

[code]diff --git a/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp b/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp
index 6346237…c8d5211 100644
— a/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp
+++ b/modules/juce_audio_devices/native/juce_linux_JackAudio.cpp
@@ -221,7 +221,7 @@ public:
close();

     juce::jack_set_process_callback (client, processCallback, this);
  •    juce::jack_set_port_connect_callback (client, portConnectCallback, nullptr);
    
  •    juce::jack_set_port_connect_callback (client, portConnectCallback, this);
       juce::jack_on_shutdown (client, shutdownCallback, this);
       juce::jack_activate (client);
       isOpen_ = true;
    

@@ -270,7 +270,6 @@ public:
}
}

  •    updateActivePorts();
       return lastError;
    
    }

@@ -282,6 +281,7 @@ public:
{
juce::jack_deactivate (client);
juce::jack_set_process_callback (client, processCallback, nullptr);

  •        juce::jack_set_port_connect_callback (client, portConnectCallback, nullptr);
           juce::jack_on_shutdown (client, shutdownCallback, nullptr);
       }
    

@@ -312,6 +312,34 @@ public:
start (nullptr);
}

  • void updateActivePorts()
  • {
  •    BigInteger currentOutputChannels, currentInputChannels;
    
  •    for (int i = 0; i < outputPorts.size(); ++i)
    
  •        if (juce::jack_port_connected ((jack_port_t*) outputPorts.getUnchecked(i)))
    
  •            currentOutputChannels.setBit (i);
    
  •    for (int i = 0; i < inputPorts.size(); ++i)
    
  •        if (juce::jack_port_connected ((jack_port_t*) inputPorts.getUnchecked(i)))
    
  •            currentInputChannels.setBit (i);
    
  •    // There is not always a real change, this method gets triggered also on jack port changes not concerning us
    
  •    if (currentOutputChannels.compare (activeOutputChannels) != 0 ||
    
  •        currentInputChannels.compare  (activeInputChannels)  != 0)
    
  •    {
    
  •        AudioIODeviceCallback* const currentCallback = callback;
    
  •        stop();
    
  •        activeOutputChannels = currentOutputChannels;
    
  •        activeInputChannels  = currentInputChannels;
    
  •        if (currentCallback != nullptr)
    
  •            start (currentCallback);
    
  •    }
    
  • }
  • bool isOpen() { return isOpen_; }
    bool isPlaying() { return callback != nullptr; }
    int getCurrentBufferSizeSamples() { return getBufferSizeSamples (0); }
    @@ -388,20 +416,6 @@ private:
    return 0;
    }
  • void updateActivePorts()
  • {
  •    activeOutputChannels.clear();
    
  •    activeInputChannels.clear();
    
  •    for (int i = 0; i < outputPorts.size(); ++i)
    
  •        if (juce::jack_port_connected ((jack_port_t*) outputPorts.getUnchecked(i)))
    
  •            activeOutputChannels.setBit (i);
    
  •    for (int i = 0; i < inputPorts.size(); ++i)
    
  •        if (juce::jack_port_connected ((jack_port_t*) inputPorts.getUnchecked(i)))
    
  •            activeInputChannels.setBit (i);
    
  • }
  • static void threadInitCallback (void* /* callbackArgument */)
    {
    jack_Log (“JackAudioIODevice::initialise”);
    @@ -571,8 +585,11 @@ private:
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JackAudioIODeviceType);
    };

-void JackAudioIODevice::portConnectCallback (jack_port_id_t, jack_port_id_t, int, void*)
+void JackAudioIODevice::portConnectCallback (jack_port_id_t, jack_port_id_t, int, void* arg)
{

  • if (JackAudioIODevice* device = static_cast <JackAudioIODevice*> (arg))
  •    device->updateActivePorts ();
    
  • for (int i = activeDeviceTypes.size(); --i >= 0;)
    if (JackAudioIODeviceType* d = activeDeviceTypes[i])
    d->portConnectionChange();
    [/code]

Ok, thanks, that seems to make sense! I’ve checked something in if you want to sanity-check…

Thank you Jules, it builds and performs correctly AFAICT. Both external and internal port changes trigger a device restart, and the correct number of actually-connected in/out ports are sent to the audio callback at all times. Great to have that fixed.

Just a C++ question, out of curiosity: you have declared the static function JackAudioIODevice::sendDeviceChangedCallback() within the class, and defined it at the end of the same file. Is there some cleverness to this - would it make any difference defining it directly within the class definition?

Cheers

Glad we got there in the end!

I put that method at the end because it wouldn’t compile if it was inline - it calls the device type class, which isn’t defined until later in the file.

Duh, silly me - thanks!