Hi. I was just wondering what the best method would be to send data from the ProcessBlock function in the plugin processor, to the plugin editor? I have made a level meter that collects a value from the processor, but would like to change it so that the processing is done within the level meter class rather than in processblock. In essence, I am just looking to just send a variable to the editor.
I think that if you set your processor as a ChangeBroadcaster, and set your editor as a ChangeListener of your processor, you can update the last one calling sendChangeMessage() from the first one.
Your processor should not know about the editor, and shouldn’t call any functions in it. You also need to assume that there could be zero, one, or many editors attached to the processor, and that this could change at any time, so the links should always be in the other direction. The best approach is either to make an editor periodically check its processor to see if anything has changed, or to have a listener/broadcaster system that allows the editors to register themselves with the processor for callbacks about something - but be very very careful about thread-safety if you do that.
I would add that be careful with sending or calling buffer after having done the processing job.
I tried same thing and the time that took editor to exploit buffer was higher than time between two calls of processBlock function, and so editor could not update itself fast enough and get a lag which blocks repainting stuff.
Ok thanks nicolas. Basically what I’ve done is create an empty AudioSampleBuffer (foo) in the processor. At the end of every processBlock method, I add the processed buffer to the end of the foo buffer (as there may be more blocks of audio than the meter refreshes). When the timer in the editor decides it’s time to redraw, I find the magnitude and RMS (for 2 different meters) values of the foo buffer, update the meters, and reset the foo buffer (and variables). Is this the right way to go? it seems a little choppy and I’m not sure if clearing the foo buffer from the editor is the best practice.
I’m trying to implement the listener/broadcaster method, and it works well while the editor is open, but as soon as I close it, I get
Thread 1: EXC_BAD_ACCESS (code=1, address=0x18)
Is there a way to check if an editor is open from the processor? Or if a broadcaster has listeners? Otherwise, is there a better way now to update the editor when something changes in the processor? In fact, I want the editor to be updated on every beat, and the only way I found to receive the beat ticks was using the getCurrentPosition method from within the processBlock function.
EXC_BAD_ACCESS tells you, that you tried to access some object in memory that is not there (anymore). Simple examples for this could be code like this:
std::array<int, 8> a;
a = 123; <-- Will likely crash, as only a to a are elements of the array
float* b = nullptr;
float c = 1.23f;
b = &c;
DBG (*b); <-- Will crash, as b points to the memory of variable c that doesn't exist anymore at this point
The good news: If you run your program under the debugger, it will stop at the line where it tries to access the invalid memory location. In a lot of cases this will give you a first hint on where to look for the source of your error.
What I assume to be happening in your case is something like in the second example: You probably add your editor as change listener on construction via addChangeListener and then close the editor (which will delete it) but without calling removeChangeListener before, right? Miliseconds later, your processor tries to access it through the pointer, it stored to the editor as listener instance, but there is no editor left at that memory address --> the program crashes. Right?
That all being said, the proposal you find above to use the ChangeListener approach is not really a good advice. Reasons are: It should be the editors responsibility to ask the processor for relevant information, not the processors responsibility to send it to the editor, the design of the processor should at best not even know if an editor currently exists or not. This makes sure, that the processing code doesn’t have to wait for anything regarding the editor and your audio processing runs smooth under all circumstances. Being a change broadcaster, the processor has the choice to send out the change message either synchronously which is a bad idea as the docs say it is only intended to be done synchronously by the message thread or async which is a bad idea, as this involves allocating a message on the message queue and allocation is not realtime safe as you might (should) know.
Better let your editor run a fast enough timer to poll a processor state and react to it – under nearly all circumstances this should be absolutely sufficient regarding speed…
You’re right, I actually forgot to remove the listener when the editor is destructed.
I also tried the timer option, but I got a warning from my DAW telling me that the MIDI sampling rate was not matching the audio sampling rate. Is it a thread issue? How can I make this process thread-safe? I tried using the HighResolutionTimer class, is that the one to use?
Not sure what exactly that was. There is no such thing as a MIDI Sampling rate, as MIDI is no continous stream of data like audio samples are. It’s probably best to open a new topic on the forum where you describe this problem with some more context like the DAW you, some code snippets and an exact copy of the error message you received to solve this.
Regarding the HighResolutionTimer, this might not be the right choice. Each HighResolutionTimer runs its own thread, which will guarantee the high resolution as it is not interrupted by anything else but what you do on it and the overall system load. However, a GUI update has to happen from the message thread, so if you want to do this inside your timer callback, a normal Timer that runs on the message thread by default is the better choice here. I’d only consider the HighResolutionTimer approach useful as an intermediate processing step, if data queried from the processor needs heavy computation before being visualized. In this case the HighResolutionTimer could do this and dispatch an async call to the message thread once it’s finished. However, most probably this won’t be the case.
@ the JUCE team: As this is a topic that comes up over and over in some ways, what about creating a tutorial on this exact topic, that shows some of the common best-practice approaches?
I’ll drop this here again, because it came up some days ago and left me very confused. There may be cases where an event managed by the processor is, for whatever reason, already happening on the message thread. In these cases, why would it be a bad idea to make the processor notify the editor(s)? There’s no concurrency involved -it’s a message thread to message thread communication. It seems overkill to also check those cases in a timer callback…
Reporting processing-related thing that are generated on the audio processing thread (which obviously only calls processor member functions) – here everything written above applies 100% in my opinion. But this is mainly not about parameters.
Chosing the right data structure and communication model: A polling timer definitively is not the only good approach to communicate everything between processor and editor. There are various other ways and good design choices to handle e.g. your parameters, e.g. by using the apvts. And of course, parameter changes in the value tree are not only triggered from the processing thread in all circumstances. So a good real world solution always needs to consider the bigger picture, but also need to consider that e.g. VST3 calls parameter changes from within the audio thread
In my case it’s not parameter changes, but other kind of events which affect the UI but may happen without it: preset / host chunk loads, latency changes, channel layout changes. All these are managed by the processor and happen on the message thread, and none of them can actually be done cleanly during playback. Only for these I use a broadcaster-listener scheme.