SATIE Plugins#

Writing audio plugins for SATIE.

Description#

SATIE plugins are SuperCollider functions. They are later wrapped together in order to generate several synths. There are three categories of plugins:

Audio plugins#

Are inserted into the signal chain and provide either a sound source or effect. Usually they generate audio signals. The following families of plugins fall into this category:

  • sources (generators)

  • effects

  • post-processors

  • spatializers

Mappers#

Their purpose is to apply mappings (i.e. via functions) to audio plugins’ parameters

Analysers#

Plugins that are inserted into audio plugins signal path, before spatialization stage, and their sole purpose is to analyse some signal feature (i.e. envelope, pitch, etc.) and forward it via OSC. See OSC-API for the OSC message description. These plugins should not return any audio but should make use of SendTrig and SendReply UGens to send triggers or streams of desired information.

(
s = Server.scsynth.local;
~satieConfiguration = SatieConfiguration.new(s, [], outBusIndex: [0], ambiOrders: [1,2,3], minOutputBusChannels: 4);
~satie = Satie.new(~satieConfiguration);
~satie.waitForBoot({
    s.meter;
    s.makeGui;
    s.plotTree;
});
)
// Setup an OSC client to listen to messages on port 18060
// if you have the oscdump utility installed on your system, open a terminal and run:
//  $ oscdump 18060
~satie.makeAmbi(\input1, \MonoIn, [], [], [\envTrigger], 2, [], ~satie.config.ambiBus[1]);
~satie.makeAmbi(\drone, \misDrone, [], [], [\envFollow], 2, [], ~satie.config.ambiBus[1]);
~satie.makeSourceInstance(\in1, \input1, \default);
~satie.groupInstances[\default][\in1].set(\t_trig, 1, \aziDeg, 30, \eleDeg, 30, \gainDB, -10, \envTrigger_triggerLevel, 0.12);
~satie.makeSourceInstance(\droney, \drone_kamikaze, \default);
~satie.groupInstances[\default][\droney].set(\t_trig, 1, \aziDeg, -30, \eleDeg, 30,\gainDB, -10, \envTrigger_triggerLevel, 0.12);

Plugins in depth#

SATIE comes with a small set of plugins, and additional plugins can easily be developped and added to the engine. Satie looks for plugins in various locations. See SatiePlugin for a discussion of plugin locations and plugin directory structure.

Every plugin should follow the following format:

// Mandatory "fields" of a plugin:
// name
~name = \pluginName;
// description
~description = "Short description of plugin's purpose or functionality";
// channel layout. Either \mono or \ambi
~channelLayout = \mono;
// function definition
~function = {| sfreq = 200 |
    // note that the plugin should not contain Out uGen.
    FSinOsc.ar(sfreq)
};
// Optional field:
// setup function to be called before instantiation of the plugin that takes a satie instance as a sole argument. Can be an empty function.
~setup = { |satieInstance| }

Each plugin resides in its own .scd file. Each field is a global variable which gets consumed by the plugin loader, one at a time. It’s recommended that the ~name field matches the plugin’s file name.

The process of turning a plugin into a synth is as follows:

  • Register the plugin with the appropriate dictionary in SatieConfiguration

  • Let SatieFactory wrap it into a SynthDef

  • Instantiate the pluginName

  • And finally interact with it

Registering the plugin#

SATIE needs to know about the plugins it manages. The plugins files are kept in directories which organize them by family. They are loaded at configuration time and kept in the following SatieConfiguration members:

SatieConfiguration: -sources Typpically audio generators. They are usually placed in a group at head

SatieConfiguration: -effects Effects, that can be fed signal from generators (placed at tail)

SatieConfiguration: -spatializers Spatialisers (typically VBAP and other types of amplitude panning)

SatieConfiguration: -mappers Parameter mapping.

SatieConfiguration: -postprocessors Post processing, or mastering. These are ensured to be at the end of the processing chain, between the above plugins and the physical interface.

SatieConfiguration: -monitoring These are side-chains that can be inserted amongst audio generators or effects in order to monitor the signal and, optionally, send OSC trigger messages back to the server.

SatieConfiguration: -hoa Higher-order Ambisonics decoders.

The registration is done by means of loading the plugin code by SatiePlugins and its methods. The source code of SatieConfiguration: -loadPluginDir shows how this is done.

The plugins are compiled into SynthDefs via Satie: -makeSynthDef. This method will look up the function definition in the indicated dictionary address and prepare a SynthDef associated with provided symbol ID. It will then place the compiled SynthDef in renderer’s dictionary where it will be ready for instantiation.

And finally, we instantiate the desired Synth with Satie: -makeInstance so that we can start interacting with it.

Note, that by default, the above steps including the compilation of SynthDefs are done automatically by SATIE at boot time, unless overriden in configuration (see variable SatieConfiguration: -generateSynthdefs).

Special use plugins#

Kamikaze#

SATIE compiles additional “copies” of generator and effect plugins. It will create “shadow” suffixed with -kamikaze. These are exact copies of the provided code, except that the synth will free itself when silence is detected. These kinds of synths are intended to be used in situations where we don’t want to keep track of them. They could be used, for instance, to accompany particle systems.

Ambi#

The Ambi shadows are created only when SATIE is configured to use Higher-order Ambisonics. All plugins will then be compiled with copies that are usable with each ambisonics order specified.

Working with plugins on-the-fly#

When developing new plugins, it is sometimes desirable to be able to doodle on a scratchpad without the need of saving and loading files off the disk. SatiePlugins: -addAudioPlugin allows us to load a plugin defined in an environment, mimicking the above plugin creation guidelines:

(
s = Server.supernova.local;
~satieConfiguration = SatieConfiguration.new(s, [\stereoListener], numAudioAux: 2);
~satie = Satie.new(~satieConfiguration);
~satie.boot;
)
(
// SATIE Plugin definition
~plugin = Environment.make({
    ~name = \simpleSine;
    ~description = "A simple sine tone generator";
    ~channelLayout = \mono;
    ~function = { |freq = 200|
        SinOsc.ar(freq)
    };
});
)
// add it to the list of plugins
~satie.config.sources.addAudioPlugin(~plugin);
// make a SynthDef based on this plugin
// the plugin and SynthDef can be named differently or the same
~satie.makeSynthDef(\simpleSine, \simpleSine, [], [~satie.aux[0]], [], ~satie.config.listeningFormat, ~satie.config.spatBus);
// see that \simpleSine was added to SATIE's SynthDef library
~satie.synthDescLib.browse;
// now, play an instance of \simpleSine called \mySine
~satie.makeSourceInstance(\mySine, \simpleSine, group: \default, synthArgs: [\freq, 440, \gainDB, -12]);