Newbie plugin design questions

Hi Jucers! I started learning C++ and Juce a couple months ago. So far it’s been a great experience — the tutorials and documentation are very well-written, and I’ve been able to get things working pretty well. I’m developing a MIDI plugin right now, and I’d like to run some of my design decisions past you more experienced people, to see if they make sense.

1 — I’m using an APVTS for my parameters. I’m wrapping this in a custom State object, which gets passed by reference to most of my Components, so they can access the parameters. The object also includes pointers to the individual parameters, so I can read their normalized values quickly with get() or getIndex(), or set them with =.

2 — Sometimes processBlock needs to trigger some audio parameter changes or other time-consuming work. I’ve been avoiding making these changes directly from that function, since I’m not sure whether they may block. So based on some advice I’ve seen here, I’ve used readerwriterqueue to set up a few non-blocking FIFO queues. I can push a “message” to them from processBlock. There’s a queue for each type of message I may need to send.

Then I have a separate juce::Timer, which I start in prepareToPlay. It repeatedly looks for new messages in these queues, and responds if necessary. I didn’t want to run this timer in one of the UI components, because it needs to still function when the plugin editor is closed.

I do have a second timer in the main UI component, for when I need to update the UI based on the player state. I generally trigger these updates by setting std::atomic values in processBlock, and reading them in the timer callback.

3 — Sometimes I need to trigger an audio parameter change when another parameter changes. So I use addListener on the parameter, check the parameter index/value, and push a message onto one of my queues if necessary. The addListener docs state that you can use AsyncUpdater for this, but the AsyncUpdater docs say to beware of using it from an audio thread, so I’ve avoided this.

4 — So far I haven’t run into any issues with processBlock taking too long. But is there a typical way to measure its performance, to ensure that I’m not doing too much work? Or just run it on a slow machine and see if it crashes? I’m already trying to make sure I don’t make any blocking calls.

Thanks to anyone reading this.

1 Like

From your description, this seems like a well crafted design. Pushing some non-allocating message structures to a lock free queue from the audio callback from time to time should normally not create any overhead that I’d bother about. But it’s always a good idea to run a release build with a profiler attached, which gives you a good overview about how much time is spent where in your code. Play around with various sample rates and block sizes to see if that has an impact on your profiling result.

If that doesn’t reveal any suspicious performance bottlenecks, try setting up a more real-world DAW session with a realistic instance count running at a lower block size and e.g. 196 kHz sample rate. Some hosts like e.g. Pro Tools and Logic will also trigger an immediate overload warning if a single block took too long, so that might be good test hosts for tests like that. Sidenote: Especially Pro Tools on mac emits a profiler signpost in case of an overload which can be useful to Track down the source of blocks that took too long. If all runs smooth here, you can be quite confident that your plugin will not cause any dropouts :slight_smile:

I am weary of your step 2: using a timer to empty the queue. While this might work in real time, it will most likely fail in an offline bounce, where the timing is suddenly completely different.

Things that were done asynchronously in real time like pre-fetching need to be blocking in an offline bounce, that way you can process the data as fast as possible.

I would see if you can avoid this asynchronous operation…

Get yourself familiar with Pluginval.
Your design seems pretty solid, but don’t take any chances.
Let this tool try to crush and break your plugin. It has 10 levels of strictness and if you pass level 10 - you win!

@PluginPenguin Thanks! I’ve started playing around with CLion’s profiler, and will definitely try your other suggestions.

@daniel That’s a really good point — I hadn’t thought about bouncing. So would it be okay to just change audio parameter values from processBlock itself when necessary? In that case, I’m not sure there’s much use for the queues, unless they’re just for updating the UI. (Or is there some other way I could empty the queues that would work during bouncing? Maybe I’m just being over-cautious — I’m probably not doing so much processing that they’re necessary.)

@duvrin Just added it to my build pipeline. So far so good!

If you’re on Mac, also add to your pipeline Thread Sanitizer, Address Sanitizer and Instruments.

if i understand you correctly (which might not be true, because i’m sorta guessing my way through your post) you want to modulate a parameter with another parameter and you’re wondering why that kinda sucks to be done in processBlock, right? the reason for that is that it’s not typical to modulate a parameter in processBlock. parameter changes typically come from the message thread (from the gui specifically), or from the daw (as automation usually) or from preset changes (getState / setState). the only time when processBlock modulates a parameter is in “gain rider”-plugins. plugins that automate their own gain parameter while running over some audio to give the user an alternative workflow to standard compressors. if you’re attempting to do that then you do have to change parameters from procssBlock i suppose. but i wanna make clear that there might also be other misconceptions that might make someone feel like it’s needed to update parameters in processBlock. for example if you wanna use modulators on a parameter. you could have a macro-parameter like in serum and you wrote a system that asigns its output as a modulator to other parameters in processBlock. i once did that and sure that works, but then i noticed that’s not how it should be done because it just makes the parameter moving on its own all the time visually, rather than just modulating it in the background as some sort of seperate signal. you notice that this is an issue if you start adding modulators that constantly change the value, like LFOs. suddenly you can barely touch the knob anymore without feeling weird about it. so if this is about such stuff, then rather find a solution where you don’t have to let parameters modulate each other in processBlock and instead write a mod system that adds their values together into a new value that is then being used in processBlock to do its thing while lettting the parameters keep their original values.

and if your issue has nothing to do with that sry :smiley:

@Mrugalla In one case, I have a parameter that should logically be turned off if another parameter is on. I wanted to trigger this from the audio thread in case automation turned the second parameter on. But I think I can do what you’re suggesting: just leave its value on, but ignore it.

In another case my UI lets you “record” certain parameter changes by playing MIDI notes. This doesn’t need to happen when bouncing, so my queues will probably be fine here.

Thanks for your response.