Processor and Editor communication

Hi everybody!

I need your advice to find the most JUCEy way of solving a problem I have:

What the code needs to do:

  1. Detect all preset files available locally (by searching a directory) and display the available presets in a comboBox
  2. Retrieve presets available online (I get a JSON from my endpoint, which I parse) and then display some of its fields in the GUI.
  3. Download a preset when you click a download button, and when the download is finished, update the comboBox options (as the newly downloaded preset is available locally and will now be detected by the 1) method

What I already did:

  1. The directory search works, and is in the AudioProcessor (I did this so my AudioProcessor can load the 1st available model) and I populate the comboBox in my AudioProcessorEditor constructor:
    auto availablePresets = (AudioParameterChoice *)vts.getParameter(
        presetSelection);
    _modelComboBox.addItemList(availablePresets->choices, 1);
    _modelComboBox.setSelectedItemIndex(0);
    _modelComboBoxAttachment.reset(new ComboBoxAttachment(
        vts, presetSelection, _modelComboBox));
  }
  1. The networking works and is in the GUI (IMO it should be in the AudioProcessor as well, but I could not find a way to transmit the parsed json (a juce::var) to the GUI via the VTS)
  2. The model download is working, and is in the GUI as well

And now I’m stuck, as I don’t know how to:

  1. Notify the AudioProcessor from the AudioProcessorEditor when a download is finished, so it can rescan the local directory.
    I guess I could do it by creating a fake click button, not displayed in the GUI, which is clicked by my networking code when the download has finished, but it seems like a really hacky way of doing it.

  2. Update the combobox with the new values.
    Should I recreate a new attachement with the new options?

Thanks for reading, have a nice day :slight_smile:

If I am understanding you properly;

First, the AudioProcessor is for realtime processing of the audio, and cannot be interrupted (blocked) by anything! Any sort of blocking is what causes pops, clicks, and zipper noise. So calls to the OS, such as searching a directory, are not permitted. Anything that can block the AudioProcessor must be moved to the message thread, or given its own thread.

To communicate with the AudioProcessor, one method is to create an atomic bool flag (at project scope) that the AudioProcessor can check to see when things are ready. You would set the flag from your GUI, for example, then the AudioProcessor can check the flag and respond to the change.

In general, orient your thinking such that the AudioProcessor does nothing but handle audio and perform DSP, and all other things are handled elsewhere.

Thank you for your response @bwall

So I should move the directory search code in the AudioProcessorEditor and populate the comboBox from here. Then when a download is finished I can run the directory scan, and update the comboBox.

However how do I retrieve the selected preset value (which is a string) in the AudioProcessor? the parameterChanged function only have const String &parameterID and float newValue as parameters.

The AudioProcessor should only play the preset that you give it. It is not involved in loading that preset. The preset should already be loaded, possibly in your prepareToPlay code. Or, again, you can code a preset loading mechanism in its own thread, and pass the preset value to the AudioProcessor, as a pointer, for example. Then, the AudioProcessor simply uses that preset.

There seems to be a misunderstanding:
A class is agnostic to what thread it is running on. The AudioProcessor has functions, especially processBlock() that is called from the audio thread, but it also has plenty of functions that run on the message thread, e.g. createEditor() etc.

There is nothing wrong implementing the preset loading or scanning a folder in the AudioProcessor.

What I would do is to implement it in an extra class that is owned by the AudioProcessor, so it can function regardless of the editor being shown or closed.
I would even go a step further and implement it in a SharedResourcePointer managed object. That way multiple instances can use the directory listing etc. (it might stop working in the future or in the latest Logic already now, when the processor runs in it’s own process. But then it is just as if you had it directly in the processor, nothing worse).

But all in all there are many ways to Rome.

The danger, of course is to break our separation of duties between GUI (including housekeeping tasks like loading samples), and the audio processing. The more you load up in the AudioProcessor class, the more you risk creating issues. My intention was to suggest an overall strategy to move everything away from the AudioProcessor, so as to prevent future issues. A philosophy I hold to myself, but for sure it is possible to do as you suggest if one is cautious.

Your help is always invaluable here, thank you for all you do!

No worries. I am just not sure what functionality you can trigger from the host without using the plugins own editor, that’s why I was leaning towards not implementing it in the editor.