Non-Realtime (NRT) with SATIE#

Use of the non-realtime processing with SATIE, with examples of an ambisonic encoding pipeline.

Overview#

An existing spatialization project can be adapted fairly easily to use the NRT (non-realtime) functionality offered by SuperCollider. The advantage of using NRT is to process audio as fast as possible, or as slow as necessary, depending only on workload.

Here we provide examples of using SATIE to encode a group of sources into Ambisonic B-format and render this scene to a soundfile.

Example NRT Rendering using Ambisonics#

In this first example we are encoding a short sound moving in space into an Ambisonic B-format file.

Setup Global Variables#

We are using SuperCollider’s default soundfile, defining our Ambisonic order, and choosing a number of sources to create.

(
~inputFile = SoundFile(Platform.resourceDir + "/" + "sounds/a11wlk01.wav").info;
~iterNumber = 10;
~order = 3;
)

We can now proceed to SATIE configuration.

SATIE configuration#

In order to use SATIE’s synthDefs (like spatializers or sources plugins) in a Score, we first need to configure and boot a SATIE instance. We instantiate a SatieConfiguration then create a SATIE renderer and pass it the configuration. We must define the minOutputBusChannels according to the ambisonic order. We are waiting for SATIE to boot and then call the replaceAmbiPostProcessor method to create the Ambisonic 3rd order Identity plugin.

(
~config = SatieConfiguration(
    server: s,
    listeningFormat: [],
    ambiOrders: [~order],
    minOutputBusChannels: (~order + 1).pow(2).asInteger
);

~satie = Satie(~config);
~satie.waitForBoot {
    ~satie.replaceAmbiPostProcessor(\Identity, order: ~order);
};
)

Storing SATIE SynthDefs#

In order to be accessible by the NRT server, synthDefs must be stored as binaries in the Platform.resourceDir +/+ “synthdefs” folder.

In addition to the Identity Ambisonic Post-processor, we will use the sndFile plugin as a source. Because we are using 3rd order Ambisonics in our configuration, SATIE has created 3rd order Ambisonic versions of each of its source plugins. sndFileAmbi3 is the name given to the SynthDef of the 3rd order Ambisonic version of the sndFile plugin.

SATIE’s naming convention for Ambisonic Post-processor SynthDefs is: “ambipost_” ++ “_s” ++ spatializerNumber ++ “_o” ++ order. In our case, the SynthDef created for the Identity Ambisonic Post-processor is ambipost__s0_o3.

Note

NOTE: Make sure the replaceAmbiPostProcessor operation is completed before storing the SynthDef. It may take 1-2 seconds. Once a SynthDef has been stored, you don’t need to repeat the operation unless you are starting over from scratch.

Once we have our SynthDefs names figured out, we can store them to disk.

// look up SynthDefs by name and store them to disk
~satie.synthDescLib.synthDescs[\sndFileAmbi3].def.store ;
~satie.synthDescLib.synthDescs[\ambipost__s0_o3].def.store;
// we don't need SATIE anymore, and we must quit in order to proceed
~satie.quit;

We are now ready to create a score.

Creating the Score#

It’s recommended to use a Score object to run NRT processes. A Score object prepares the binary OSC file for you in the correct format. For more information on NRT capabilities in SuperCollider and the score creation, see this guide Non-Realtime Synthesis (NRT).

(
~score = Score.new;
// add buffer allocation and file reading to the score at time 0.0s
// NOTE: the value 131072 used below is the recommended number of frames for a Buffer used by DiskIn
~iterNumber.do { |i|
    ~score.add([
        0.0,
        ['/b_alloc', i, 131072]
    ]);
    ~score.add([
        0.0,
        ['/b_read', i, ~inputFile.path, 0, 131072, 0, 1]
    ]);
};
// instantiate an ambisonic encoder for each source
~iterNumber.do { |i|
    ~score.add([
        i * ~inputFile.duration,
        ['/s_new', 'sndFileAmbi3', -1, 0, 0, 'bufnum', i, 'gainDB', -6, 'aziDeg', i * (180 / ~iterNumber)]
    ]);
};
// instantiate an ambisonic decoder to perform mixdown of each source
~score.add([
    0.0,
    ['/s_new', 'ambipost__s0_o3', -1, 1, 0]
]);
)

Rendering the Score#

To render the Score, we must call Score: -recordNRT. The rendering process can take a while depending on the workload. Once finished, you should have a new soundfile containing a 3rd order Ambisonic B-format recording in your Platform.recordingsDir.

Note

NOTE: Score.recordNRT has an argument named options. You must provide this argument the ServerOptions of the Server that was configured by SATIE in the previous steps.

(
// the location where out exported soundfile will be written
~path = Platform.recordingsDir + "/" + "satieNRTAmbi3.wav";
~score.recordNRT(
    outputFilePath: ~path,
    sampleRate: ~inputFile.sampleRate,
    headerFormat: "WAV",
    sampleFormat: ~inputFile.sampleFormat,
    options: s.options,
    duration: ~inputFile.duration * ~iterNumber;
);
)

Binaural monitoring#

To make sure everything went well, you can listen to your Ambisonic soundfile using SATIE’s binaural decoder.

(
~config = SatieConfiguration(
    server: s,
    listeningFormat: [],
    ambiOrders: [~order],
);
~config.server.options.numOutputBusChannels = 2;
~satie = Satie(~config);
~satie.waitForBoot {
    // cue the soundfile to be read by DiskIn
    ~buf = Buffer.cueSoundFile(~satie.config.server, ~path, numChannels: 16);
    ~satie.config.server.sync;
    // instantiate ambisonic decoders
    ~satie.replaceAmbiPostProcessor(\HOABinaural, order: ~order);
    ~ambiDecoder = ~satie.makeSourceInstance(\ambiDecoder, \sndFileHOAAmbi3, synthArgs: [\order: ~order, \bufnum: ~buf, \loop: 1]);
};
)
// stop playback
~ambiDecoder.free;
// quit SATIE
~satie.quit;