Spatial Additive in SC & Reaper II

This advanced example for spatial additive synthesis is an extension of Spatial Additive in SC & Reaper I. With increasing number of partials, the DAW's performance becomes a problem whens encoding all of them as an individual point source. 100 is a good estimate to get good results - but especially for lower pitches, it takes even more to get a decent timbre.

The following example creates a spatially distributed sound through additive synthesis. A defined number (50-100) of partials is encoded in SC, with the resulting HOA signal being routed to Reaper for decoding:

/images/spatial/additive_spectral.png

Synthesis & encoding in SC > decoding in the DAW.


The SC server needs 16 channels to forward the encoded signal. THis example introduces groups to make node organization easier:

(
// increase the number of SC output channels
s.options.numInputBusChannels  = 0;
s.options.numOutputBusChannels = 16;

s.boot;

// show SC meter
s.meter;
)


// two groups to ensure right order of nodes (evaluate one at a time):
~partial_GROUP = Group(s);
~encoder_GROUP = Group(~partial_GROUP,addAction:'addAfter');

A Partial SynthDef

A SynthDef for a single partial with amplitude and frequency as arguments. In addition, the output bus can be set. The sample rate is considered to avoid aliasing for high partial frequencies.

(
SynthDef(\spatial_additive,

      {
              |outbus = 16, freq=100, amp=1|

              // anti aliasing safety
              var gain = amp*(freq<(SampleRate.ir*0.5));

              var sine = gain*SinOsc.ar(freq);

              Out.ar(outbus, sine);

      }
).send;
)

The Partial Synths

Create an array with 50 partial Synths, using integer multiple frequencies of 100 Hz. Their amplitude decreases towards higher partials. An audio bus with 50 channels receives all partial signals separately. All synths are added to a dedicated group to ease control over the node order.

~npart         = 50;

~partial_BUS   = Bus.audio(s,~npart);

(
~partials = Array.fill(40,
{ arg i;
  Synth(\spatial_additive, [\outbus,~partial_BUS.index+i, \freq, 100*(i+1),\amp, 1/(1+i*~npart*0.1)],~partial_GROUP)
});
)

s.scope(16,~partial_BUS.index);

The Encoder SynthDef

The extended encoder SynthDef offers additional control through arguments:

(
  SynthDef(\encoder, {
  |inbus=0, azim_offset=0, mod_bus, mod_amp=1, elev=0|
  var lfo_signal = In.kr(mod_bus, 1);
  // Total azimuth = where it starts + (LFO * how much it moves)
  var total_azim = azim_offset + (lfo_signal * mod_amp);

  Out.ar(0, HOAEncoder.ar(3, In.ar(inbus), total_azim, elev));
}).send;
)

The Encoder Synths

An array of 16 3rd order decoders is created in a dedicated encoder group. This group is added after the partial group to ensure the correct order of the synths. Each encoder synth receives a single partial from the partial bus. All 16 encoded signals are sent straight to the output.

(
~encoders = Array.fill(~npart,
      {arg i;
              Synth(\encoder,[\inbus,~partial_BUS.index+i,\azim, i*0.1],~encoder_GROUP)
});
)

// watch the output
s.scope(16,0)