Developing Audio Applications With JUCE (part 1)

Audio processor graph

Audio processor graph

Several times we’ve worked on projects in the pro audio/music instrument industries that have used a very useful C++ cross-platform application framework called ‘JUCE‘. It was originally developed as part of the ‘Tracktiondigital audio workstation, and later extracted out as a standalone framework (in much the same way that Ruby on Rails was extracted from 37 Signals’ work on Basecamp). For open source projects, JUCE is licensed under the GPL, and for commercial projects it’s licensed under a very reasonable per-company license (not per-project, or per-year). Applications written using the framework can be deployed on Windows, OS X, Linux, iOS, and Android.

For audio developers, it’s an incredibly useful framework that contains solid and well-conceived classes to deal with many of the lower-level tasks that any application processing audio will need to deal with — opening and configuring multichannel audio devices (which may involve dealing with numerous different driver stacks), finding and loading audio effects plug-ins (again — several different formats exist in the wild, including VST and Apple’s AudioUnits), accepting and generating MIDI data, implementing audio effects algorithms, reading and writing audio files, and so on.

I’ve worked on a few projects that used JUCE, but was never involved in any of the work that touched that audio layer. I’ve been considering writing the next version of a long-standing personal music software system over to JUCE, and after spending some time poring over the documentation and sample code, I decided that it would be best for my sanity to start with a smaller project as a learning experience, and document what I learned as I go.

JUCE comes with good class-level documentation and a set of very useful sample apps (as well as full source for JUCE itself), but there’s not much in the way of “here are the important things that you need to know to become productive.” After a few weeks of poring through the sample code and Doxygen output, I’m hoping that this series of posts will act as the document that I wish had existed.

The Incline to The Scumbler

For decades, I’ve been a fan of the music of guitarist Robert Fripp. Throughout his career, he’s done a series of solo performances (originally referred to as ‘Frippertronics’, later rechristened ‘Soundscapes’) that consist of him performing over live loops of his own playing. Originally, he implemented this using a pair of reel-to-reel tape recorders with a single long loop of tape strung between both of them, then transitioning to a rack of digital delays and effects as those tools became available. In 2010, I took my son into Manhattan to hear what Fripp says will be his final Soundscapes performance ever.

http://www.spinner.com/2010/12/03/robert-fripp-soundscapes-live/
http://www.wnyc.org/shows/newsounds/2010/dec/22/

(He’s far from the only person to use similar techniques — guitarist David Torn explores similar ideas in much of his work, and there are many other musicians around the world also working in this area — see http://www.loopers-delight.com/loop.html)

For this project, I decided that it was a reasonably-scoped project to build a pure software system that:

  • Works with professional grade multichannel audio I/O hardware
  • Runs on both Mac OS and Windows (…and eventually iOS, I hope)
  • Can loop multiple independent tracks of audio
  • Can process audio using VST or AU plugins.

I’m calling this system ‘The Scumbler’ — partly because it’s a name that I can be safe that no marketing guy is ever going to try to use, and partly because it describes what the program will do very well. “Scumble” is a term from art for covering a painted area with a layer of semi-opaque paint to soften the colors or outlines, and that’s a lot like what we’ll end up doing.

scumble_squares

(source http://www.jessbates.com/pages_tutorials/glazing_scumble.htm)

Source for this project will be made available on github at some point — you can check at https://github.com/bgporter

Instructions for getting JUCE itself are available at http://www.rawmaterialsoftware.com/downloads.php

Step 1: Do No Harm

The first step is to gain enough understanding of the audio subsystems that I can

  • Instantiate an audio device
  • Change its configuration
  • Persist that configuration to and from disk
  • Instantiate input and output connections on the audio device
  • Connect the input and output for a simple audio passthrough

Luckily, much of this is found already in the JUCE Audio Plugin Host example program that comes with the JUCE distribution. The downside is that there’s also a lot of other stuff in there that makes it difficult to understand exactly how the various pieces need to fit together unless you spend a lot of time with the source. This project is fallout from my spelunking through the JUCE sample apps and using what I learn along the way. I expect that as we go along, my understanding of things will change and I’ll have opportunities to go back and rework things to accomodate my updated knowledge.

The major pieces that we need to be aware of are a combination of concrete classes supplied with JUCE that can be used directly and base classes that your code will need to use for derived classes specific to your application.

AudioDeviceManager

http://www.rawmaterialsoftware.com/juce/api/classAudioDeviceManager.html

Your application should have a single instance of the AudioDeviceManager class.This object is used to encapsulate all of the pertinent audio and midi hardware that’s available on the system that’s running your app. JUCE provides built in auxiliary classes and UI code to select and configure these devices, as well as persisting that configuration between runs of the program.

For audio to start flowing, you need to pass this object a pointer to another object that implements the AudioIODeviceCallback interface.

Once your code adds that callback to the device manager, it will be called repeatedly with buffers of audio and midi data that need to be processed.

AudioProcessor

http://www.rawmaterialsoftware.com/juce/api/classAudioProcessor.html

Classes derived from the AudioProcessor base class are used (as you might guess) to process audio and MIDI data. This class includes a lot of member functions, but the key one is:

…which is called each time a block of samples and/or MIDI data need to be handled. Each processor replaces the contents of the buffer and MIDI Messages objects passed to it by reference.

AudioProcessorGraph

http://www.rawmaterialsoftware.com/juce/api/classAudioProcessorGraph.html

A single audio processor on its own probably isn’t too interesting in any app, and certainly not in this one. JUCE’s AudioProcessorGraph is-a AudioProcessor that holds a bunch of other AudioProcessor objects and can connect their outputs and inputs in arbitrary topologies. This is where the real fun lies (and if you have some time on your hand, I really recommend sitting down with a glass of brandy and the source code for this class sometime — it’s some really nice and complex code that rewards the time spent studying it).

Audio flows into the graph on one side, gets processed by all of the processor nodes owned by the graph, and the processed audio flows out the graph on the other side.

AudioProcessorPlayer

http://www.rawmaterialsoftware.com/juce/api/classAudioProcessorPlayer.html

JUCE provides this class to glue together the AudioDeviceManager and the AudioProcessorGraph. It is-a AudioIODeviceCallback object, and can be passed to the AudioDeviceManager for your app to start handling audio samples, and it has-a AudioProcessor. Create an instance of AudioProcessorGraph, pass it to the AudioProcessorPlayer::setProcessor() function, and then start adding AudioProcessors to the graph.

AudioProcessorGraph::AudioGraphIOProcessor

http://www.rawmaterialsoftware.com/juce/api/classAudioProcessorGraph_1_1AudioGraphIOProcessor.htm

More classes provided for us by JUCE — you can instantiate this class as one of a special set of input or output nodes that take care of putting audio input into your processor graph or sending the audio output from your graph back to the outside world.

A little bit of code…

Some of this code was lifted more-or-less verbatim from the sample app “Audio Plugin Host”, and in some cases, I used that as a model that I’ve veered away from. Modulo a few minor tweaks, the code follows the coding conventions documented here.

In my MainWindow.h file (originally created by the Introjucer app that comes with JUCE to get a JUCE project started, including creating XCode/MSVC project files), I have an instance of AudioDeviceManager:

…and in the .cpp file, we initialize it, including trying to load any previous settings from the application’s properties file:

Everything else interesting happens inside an instance of my app’s Scumbler object. This object gets a reference to the AudioDeviceManager at construction time, and owns its own instances of AudioProcessorPlayer and AudioProcessorGraph:

When we start up that Scumbler object, we create input and output nodes and add them to the graph, then connect them together. In the constructor we do this:

and some other interesting member functions:

(where Scumbler::Connect() is a member function that does what you’d think, but is a little more dense than might be appropriate to show here. It connects the output of the input node to the input of the output node.)

At this point, our Scumbler app is what I would call version 0.1.0 — it doesn’t do anything particularly interesting, perhaps, but we:

  • open the desired audio I/O device
  • create a graph of processors that we’ll soon enough be able to insert any audio processing plug-in that’s available on the user’s machine
  • passes audio through the graph to the output.

In part 2, we’ll deal with finding, loading, connecting, and modifying VST and AU plugins.

Brett g Porter

Brett g Porter

Lead Engineer, Audio+Music at Art+Logic
Lead Engineer, Audio+Music development at Art+Logic. Always looking for excuses to write code. Tweets at both @artandlogic and @bgporter.
Brett g Porter

@bgporter

Music+Software+Music Software+Ice Cream. Relapsing composer/trombonist. Day job @artandlogic. Creator of @tmbotg.
RT @WesFlinn: Playing again has made me a better composer, better teacher, and better person. If you can at all, make music today. - 7 hours ago
Brett g Porter
Brett g Porter

Latest posts by Brett g Porter (see all)

Tags:

Creative Commons License

This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License.

2 Comments

  1. Manuel

    Hi, I just wanted to say that the further links of the entry
    “Developing Audio Application with Juce” dont work.

    Best regards
    Manuel

    • Brett g Porter

      Thanks for the note — I’ll correct this.

Trackbacks/Pingbacks

  1. Developing Audio Applications With JUCE (part 2) | Art & Logic Blog - [...] Last time we looked at getting a very basic version of an application that can process audio running using…
  2. Developing Audio Applications With JUCE (part 3) | Art & Logic Blog - [...] Scumbler application that can interface with audio hardware and route audio signals through itself (part 1) and also load…
  3. Designing Audio Effect Plug-ins in C++ (book review) | Art & Logic Blog - […] been writing a series of posts here over the last few months discussing the JUCE C++ application framework and…
  4. Designing Audio Effect Plug-ins in C++ (book review) | - […] been writing a series of posts here over the last few months discussing the JUCE C++ application framework and…
  5. Developing Audio Applications With JUCE (part 2) | - […] Last time we looked at getting a very basic version of an application that can process audio running using…