Welcome to Teragon Audio, provider of audio plugins, utilities, and guides for those looking to develop their own audio software. This is the development blog, if you are looking for the official website please visit http://www.teragonaudio.com.

Making PluginParameters threadsafe

One feature of TeragonGuiComponents is that it uses PluginParameters as the engine for syncing parameter changes. This is very convenient, as it means that all the controls automatically understand the correct parameter ranges, and can also be associated with a parameter needed by the audio engine.

However, PluginParameters is not really thread-safe, which so far hasn't been a big issue since it has been mostly used in GUI-less plugins. I'm not sure if anybody outside of Teragon is using the library, a few people have forked/starred it on GitHub but that doesn't mean much.

Each component "registers" for a parameter, meaning that it is capable of both setting parameter values and also updating the component graphic based on parameter state. Since the observer interface is pretty simple, it means that components will end up notifying themselves whenever they try to set a parameter! Furthermore, there is the ever-present risk of data corruption since the parameter is shared directly between the GUI and audio threads.

So, PluginParameters is currently being adapted for use in a multi-threaded environment. This is fairly non-trivial, since non-blocking data structures must be used instead of mutexes. Mutex blocks threaten to cause priority inversion, forcing the audio thread to wait on the slower GUI thread.

The new architecture will thus look something like this:
  • A new EventDispatcher class will be responsible for setting parameter values.
  • A low-priority background thread will be used for asynchronous events. It will have its own dispatcher.
  • The audio thread will also "own" a dispatcher, and it will process events synchronously during the process() call.
  • Parameters can be set from either the realtime audio thread or the asynchronous background thread. In both cases, the dispatcher will notify all observers of the same type first, and then send the event to a non-blocking event queue on the other thread.
In this manner, events are shuffled between the low-priority GUI thread(s) and the high-priority audio thread. Also, when notifying observers an extra parameter for self can be passed so that GUI components do not notify themselves of parameter changes.