Welcome

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.
Showing posts with label pluginparameters. Show all posts
Showing posts with label pluginparameters. Show all posts

Death to boilerplate

I made some good progress on ExtraNotes yesterday, it can now save the note correctly and serialize the image data to a base64-encoded string. It's pretty much ready to be released, but when I was testing it I realized what a pain in the ass it's going to be to make it compatible with all hosts. Text input in VSTs is always somewhat tricky, since many hosts like to intercept the keystrokes for themselves. Anyways, I'm just going to test under the ones that I already have, and wait for the bug reports to trickle in. :)

In doing the XML serialization work, I had the idea to make a common base class for the Teragon plugins, since they all have a bunch of identical boilerplate code in them. This wasn't such a bother for the parameter handling, since all of those functions were simple one-liners, but the serialization routine was starting to get rather complex and it pained me to keep copy-pasting it between plugins. So, I introduced a new abstract base class which extends AudioProcessor and takes care of all the PluginParameters related stuff. Doing this eliminates a ton of boilerplate code from all the plugins, making them much simpler to maintain and understand.

Also, today I fixed a nasty bug in NotNotchFilter where a small (but audible) click could be heard when moving from bypass to filter states. The reason for this is that the first "tick" of the knob ended up being a few hundred Hertz, which is quite an extreme difference to apply suddenly to a filter. Exponential scaling didn't work very well in practice for NotNotchFilter, because the frequency range goes from 0-20kHz, so the first half of the knob's motion would only cover ~10Hz, and the second half would race through all remaining frequencies.

So today, I busted open the venerable yet forgotten "Grapher" program which ships with Mac OSX. I discovered (or perhaps forgot) that one of the lesser-known features of this program is that you can generate a point set, and then interpolate through them. So I crafted a curve which would work well with the knob's range, and generated a 5th-degree polynomial equation to fit the points. The result is that now the knob is very comfortable to use, and the click is gone! I'll probably need to generate another set of curves for HiLoFilter, which has a similar problem but a different knob action.
Yay for math!

The return of Arooo

Did a bit of work on ExtraNotes today; last night I had a revelation about how to solve the problem of the GUI not updating properly when audio playback is paused. The solution is actually quite simple -- ConcurrentParameterSet should have pause/resume methods which are called when the playback is paused/resumed, and when paused, all realtime parameter changes are executed right away. Since playback is paused, the risk of priority inversion (which, for the record, is the reason that ConcurrentParameterSet is so damn complex) does not exist and the parameter updates can be safely executed.

The solution worked like a charm, and I am thinking about doing a bit of minor re-architecting in PluginParameters so that other plugins can use this solution per-default. As it stands, the other plugins which use ConcurrentParameterSet don't really need to execute parameter changes when playback is paused, so it's not such a big deal.

Yeah, at this point I'm now sitting on 4 plugins which are essentially finished, but haven't been released. The reason I haven't done any proper releases of these plugins is mostly laziness -- doing releases means getting out a bunch of different laptops, making builds, doing sanity checks, making the zipfiles etc. It's not hard work, but it's labor-intensive and takes time. Since I've got such a great creative burst going these days, I'm reluctant to blow it on the mindless repetitiveness on releasing software. But I promise that will change soon, and shortly after New Year's, I will make a huge release day and push everything out "into the wild".

On the subject of creative streaks, I started looking at Arooo again, and am now hacking it into plugin form. Arooo is a standalone app I did at last year's MusicHackDay Stockholm, and it was primarily intended for trying to stop a bad habit that our puppy was developing -- howling when I left the house. The software would open the default audio device, look for sounds which roughly matched the frequency signature of the howls, and then play a nasty noise if detected. The hack was easily done in the required 24 hours for the hackathon, and it was fun to demo.

Surprisingly, when I put Arooo into action, it did very well. I set the program up to log events to the screen, and then recorded the audio when I was gone and cross-referenced them against each other. It showed real promise, detecting the input noises at very high reliability. I haven't done much with the software since then, but I have been meaning to generalize it and make it into plugin form. When finished, the plugin will:

  • Allow the user to load in an audio file for profiling
  • Process audio in realtime, searching for signals which match the profile
  • Set a "tolerance" for how close the input signal must match the profile
  • Trigger an event when the input signal matches, which can include:
    • Playing another sample (could be used to build a drum-replacement sampler)
    • Playing a MIDI note
    • Sending a MIDI CC message
    • Executing a shell script
After one day of hacking, I made a basic GUI and re-architected some of the initial hackathon code to be a bit more generic. I still need to generalize the profiling algorithm, which was the key part of making Arooo work.

PluginParameters improvements, and introducing ExtraNotes

This morning I intended to "briefly explore" a concurrency bug in PluginParameters which had been bothering me ever since I started porting TeragonGuiComponents over to Windows. Although the demo ran great, and the framework required very little work to port to Windows, I was annoyed that the demo app was crashing on shutdown. This might normally be forgivable for a desktop app, but for a plugin this means that removing it from a channel will crash the host, so it's kind of a big deal.

Anyways, my "brief exploration" resulted in a full day of hacking PluginParameters, but at least now I've bumped the version to 3.0 and fixed a ton of bugs in the process. Also I renamed several classes in the library, since some classes were starting to get quite verbose (namely, ThreadsafePluginParameterSet is now ConcurrentParameterSet, which is still quite long but overall better). Unfortunately some concurrency bugs are still lurking in the framework, mostly having to do with calling set() or delete on a ConcurrentParameterSet directly after creating it. I would really like PluginParameters to be able to guarantee to the user that it is ready to do anything after the constructor returns, but unfortunately this is in practice very difficult to accomplish.

The good news is that in practice, a plugin should never need to call set() or delete on a parameter set immediately after creating it. However, I dislike shipping code with known bugs, no matter how small.

Anyways, I also made some progress yesterday on ExtraNotes, which is the renamed version of AUNotes. This will actually be the second time I've renamed the plugin; the name before AUNotes was "Notepad", which I think is far too generic and has poor search visibility. This name change reflects something bigger and more exciting -- the dropping of the "AU" from the plugin.

ExtraNotes will actually have an AudioUnit version, but unlike its predecessors, it will be available for other plugin containers (namely VST) and operating systems. A Windows/Linux port of this plugin has been on my wishlist for many years now, and I'm glad that I could finally do this using Juce and TeragonGuiComponents. Here's some preview screenshots:

Editing a note
Images are finally supported!
Yes, that's right! ExtraNotes will support importing images and store them in the note for you! This has been a looooong requested user feature, and using the Juce image framework, it actually wasn't terribly difficult to accomplish. The plugin will support two "tabs", so you can have both text and a single image. You can also import a text file from disk into the text tab, in the exact same way that you can load an image to the image tab.

Currently the plugin does not serialize the image during state save/restore, and although the text notes should, it's completely untested. I'm not sure how well Juce's XML serializer will deal with raw binary data like this, and I might need to serialize the image myself into a series of integers (yuck, let's hope it doesn't come to that).

So yeah, today I had originally planned to do Windows porting, and to be fair I did do some. The PluginParameters test suite now builds and runs on Windows, which is a big improvement, and the TeragonGuiComponents demo also is running on Windows without crashing (yay). However I probably won't have time to build Windows versions of the other plugins I did over Christmas, which means that I will probably end up doing a huge batch of releases sometime shortly after New Years.

More GUI stuff

I spent a bit of time in Pixelmator today cleaning up the template document. I think that the template was a good idea, as it allows one to do GUI layout very rapidly. Each component is now grouped, making it easier to move around and position relative to other components. I also added an alignment layer for each component, so one can quickly do pixel-perfect positioning from Introjucer.

The result of this work was redoing the demo app for TeragonGuiComponents and doing a new GUI for HiLoFilter. I decided to redo the demo app GUI because when working on the template document, I noticed that label opacity and component positioning were incorrect. The original GUI for HiLoFilter was made without the template document, and it was actually faster to redo it using the template then it would be to retrofit it with the alignment guides.

It took me some time to get HiLoFilter compiling again, it seems that every time I open up a plug-in, I run into problems with some library or another not compiling under whatever the latest version of Mac OS X is. However, it is fruitless to simply curse Apple, instead one must fix the errors and raise the minimum supported OS version. This means that when all of the plug-ins have been updated with their shiny new GUIs, the minimum required Mac OS version will be OS X 10.7 (Lion).

I personally wish that I could still support Snow Leopard, however Apple is making it increasingly difficult for users to lag behind on older versions of Mac OS, which I suppose isn't such a bad thing. However, I personally haven't made the upgrade to Mavericks myself, as I'm still waiting for many plug-ins and sequencers (namely Ableton Live) to be supported. It's a bit of a Catch-22 situation.

On the plus side, I will be able to ship the 64-bit versions of all plug-ins, thus ensuring that they will be compatible with the latest version of Logic Pro X.

Back in the saddle

After a very long (and necessary) break, I've started doing a bit of Teragon hacking again. I've picked up mostly where I left off, which is at TeragonGuiComponents. After making a release of PluginParameters with the new multi-threaded features, I have started updating the demo app in TeragonGuiComponents to use the new threadsafe API. I'm still finalizing the component API, but I have also introduced a common base class for components in the package which removes a lot of boilerplate and duplicate code.

After two evenings of work, I have got the demo app building and partially running with the multithreaded parameter sets! However, the key word here is "partially"; unfortunately components which share the same parameter "fight" to update the value, which is a sign that the parameter value is not being passed correctly to the components. Also I got a ghost crash which seems to have been spawned by the event dispatched thread, though the stacktrace doesn't indicate any traces of my own code, which is a bit odd.

Anyways, my goal for the evening was just to get everything updated and compiling again, so I haven't even started digging into debugging these problems. I don't think it will be so hard to fix these errors, but then again, everything involving multithreaded code tends to be a rabbit-hole of debugging. Let's hope that isn't the case this time. During the summer, I already put in the hard hours getting PluginParameters to be threadsafe, and once I reached the end of that journey I was 99% certain it was threadsafe, given the usage parameters which I defined in the documentation. If there are other race conditions or problems, it's likely not to be a simple one-liner fix in PluginParameters, so let's hope it doesn't come to that.

Also, on a frustrating sidenote, I fixed another compiler error in tinythread, only to discover that all of my previous pull requests on gitorious have remained unmerged. I guess I'm not terribly surprised, given that when I forked tinythread, I recall seeing at least one very ancient pull request sitting in the queue. However, I naively hoped that the author would take a look at my fixes/improvements and merge them in, but this hasn't been the case. If the author doesn't seem interested in maintaining this project, I'll likely move my fork from gitorious to github, since gitorious is a bit awkward to work with.

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.