Part 2. Containerizing in a JUCE-based plugin
Now, that we managed to build the thing. Let’s see how we can actually use it. Of course, you can follow official instructions and sorta “bake” SuperCollider synthdefs into the plugin. There is (was) even a script among the SuperCollider quarks for that. But this approach kinda fell flat for me. Guess why? Boooring GUI? Yes sure, and also no multichannel output, and MIDI support being rudimental as well.
The normal approach here would be just port the project to a modern framework like JUCE, because the there is not that much DSP-specific code in SCAU itself, it kinda just start the SC server, exposes a UDP port and routes the server’s output to plugin output. Unfortunately, this is still too much for my “a pointer to a pointer? WTF?” C++ skills.
So, my descioon was to start the DSP C++ journey of mine with something extremely noob-friendly and actively maintained as JUCE framework. Among the million other cool DSP-related features, it provides a suite to . So, you basically can hve a plugin audio-procesing chain inside a parent “container plugin”. Well, why not try it with our recently build SuperColliderAU then? Let’s see how it goes:
Starting from minimal JUCE template project
- Create a new plugin using Projucer. It will lay out a C++ audio plugin project structure with PluginProcessor and PluginEditor classes.
- Using JUCE audio format manager load an instance of SuperColliderAU
- In processor’s header file we add the defintion of our plugin instance, plugin format manager and OSC sender.
OSCSender oscSender; AudioPluginFormatManager pluginFormatManager; std::unique_ptr<AudioPluginInstance> scInstance;
- When in PluginProcessors’ constructor we code something like this:
pluginFormatManager.addDefaultFormats(); String errorMessage; PluginDescription descr; descr.pluginFormatName = "AudioUnit"; descr.fileOrIdentifier = "/Library/Audio/Plug-Ins/Components/SuperColliderAU.component"; scInstance = pluginFormatManager.createPluginInstance(descr, 48000, 512, errorMessage); // you can then test if the pointer was set, or output and errorMessage otherwise
- In processor’s header file we add the defintion of our plugin instance, plugin format manager and OSC sender.
- SuperColliderAU exposes it’s UDP server port number as a plugin parameter, so we can easily read it and setup an OSC connection using built-in JUCE OSC sender.
Caveat
: when you load SCAU as a plugin instance, it does not automatically start the server, you need to callprepareForPlay
for that. So the following routine better be called in your PluginProcessor’s “prepareToPlay” method.scInstance->prepareToPlay(sampleRate, samplesPerBlock); scInstance->refreshParameterList(); Array<AudioProcessorParameter*> params = scInstance->getParameters(); // SCAU has only one parameter and it is the UDP port number scPort = std::stoi(params.getFirst()->getText(params.getFirst()->getValue(), 4).toStdString()); oscSender.connect ("127.0.0.1", scPort);
- When the host plugin receives MIDI input, translate it to OSC messages
- Pass audio buffer to be filled by SuperCollderAU during host plugin process loop
MidiBuffer::Iterator it = MidiBuffer::Iterator(midiMessages); MidiMessage msg; int s; while(it.getNextEvent(msg, s)) { if(msg.isNoteOn() && msg.getNoteNumber() == 42) { oscSender.send(("/s_new", (String)"testsynth", (int)-1, (int)0, (int)0, (String)"paramName1", (float)*paramValue1, (String)"paramName2", (float)*paramValue2); } }
NOTE: it is implied that you have a SuperCollider Synthdef (testsynth.scsyndef
) written to a file and placed it into SCAU resoucses dir, the synthdef is loaded at SCAU internal server startup.
sudo cp ~/Library/Application\ Support/SuperCollider/synthdefs/testsynth.scsyndef /Library//Library/Audio/Plug-Ins/Components/SuperColliderAU.component/Contents/Resources/synthdefs
Also, if your synthdef is using SuperCollider extensions library, those complied SC-classes should be copied there as well:
wi11iew0nka@chocolatefactory > ls /Library/Audio/Plug-Ins/Components/SuperColliderAU.component/Contents/Resources
BinaryOpUGens.scx FFT_UGens.scx MembraneUGens ReverbUGens.scx
ChaosUGens.scx FilterUGens.scx MulAddUGens.scx TestUGens.scx
DWGUGens GendynUGens.scx NoiseUGens.scx TriggerUGens.scx
DelayUGens.scx GrainUGens.scx OscUGens.scx UIUGens.scx
DemandUGens.scx IOUGens.scx PV_ThirdParty.scx UnaryOpUGens.scx
DiskIO_UGens.scx LFUGens.scx PanUGens.scx UnpackFFTUGens.scx
DynNoiseUGens.scx ML_UGens.scx PhysicalModelingUGens.scx