Modulators
drive parameter-changes in Anodes.Currently, only built-in modulators offer this capability.
Traditionally audio synthesis engines define two (or more) kinds of signals, audio and control. Audio signals have a high, fixed sample rate and a value range of [-1, 1]. Control signals are often sampled at a fraction of the audio rate and defined over an arbitrary [min, max] range. Control signals ultimately modulate the output audio signal either directly (eg ADSR) or indirectly (eg a filter's resonance parameter).
Already your music programs can directly invoke SetParam and ModParam. What's more, you can spawn JavaScript or Lua fibers whose express purpose is to periodically update any number of Anode parameters. You can even define your own control-rate, signal patterns and targeting schemes.
Such general capabilities can be cumbersome for common modulation tricks. We provide automatic/programatic modulation to describe modulation setups and to perform them automatically within the audio engine. Several efficiencies, both in workflow and processing, are gained by expressing modulation effects indirectly.
Many popular audio engines support parameter and signal modulation via a
drag-drop interface to define the modulation source and its target. In
order to support multiple targets, a separate set of settings may be required
to characterize the remapping of the source modulation signal. So we
have two concepts here: Modulator
(class) and Modulator Routing
which
includes:
Like Anodes, Modulators are created via Ascene.
Unlike Anode, Modulators require special wiring into the graph through
Ascene's ModulateParam
method.
let ascene = await Ascene.BeginFiber(this);
let inst = ascene.NewAnode("Vital");
let lfo = ascene.NewModulator("Hz.ModLFO", "triangle");
ascene.Chain(inst, ascene.GetDAC());
ascene.ModulateParam(lfo, inst, "Oscillator 1 Pan");
await ascene.Sync();
lfo.SetParam("Frequency", .1); // .1 Hz => 10 second period.
When connecting the output of a modulator to a downstream parameter of
an Anode you have the option of defining a remapper. This is done
by invoking the modulator's NewRemapper
method. Here's the JavaScript
method signature:
NewRemapper(scale, bipolar, power=0, offset=0, mode=0)
Note that the NewRemapper
method accepts up to six positional parameters.
You can also invoke this method with a single parameter where
the parameter is an object whose keys are any of these named parameters
as shown here.
let scale = .5;
let bipolar = false;
let power = .25;
ascene.ModulateParam(lfo, inst, "Oscillator 1 Pan",
lfo.NewRemapper({scale, bipolar, power}));
Remapper Param | Description |
---|---|
scale |
scale [0,1] to a range appropriate for target |
bipolar |
(true or false) causes the scaled value to be centered around 0 (allowing negative numbers) |
power |
controls remapping curve, 0 is linear, > 0 convex, < 0 concave, range:[-5, 5] |
offset |
offsets values produced by the scale and bipolar values. Valuable primarily in mode:1. |
mode |
select between 0:add and 1:set . Default is 0 but mode:1 is useful for envelopes. |