record_voice_over
Wherein we show the use of Voices to produce polyphonic bliss.
The MusicAPI includes a polyphonic voice manager, Voices, to transform a monophonic instrument into a polyphonic one. It does this simply by creating multiple independent instances of the instrument and tracking the lifetime of notes that it delivers.
This code produces the audio graph that follows it.
// The usual startup boilerplate
let scene = await Ascene.BeginFiber(this);
let osc = scene.NewAnode("Hz.Osc", {cfg: "mono"});
let pan = scene.NewAnode("Hz.Mix", {cfg: "1to2"});
let dac = scene.GetDAC()
scene.Chain(osc, pan, dac);
// create our voice manager and request 20 voices of our synth
// defined by the sink node of its "voice". The number of voices
// active is governed by the combination of the note duration
// and the // ADSR release time. AudioStatus shows the number
// of active voices in the LoadAvg display.
let voicemgr = scene.NewVoices();
voicemgr.AddVoices(pan, 20);
scene.VisualizeGraph("Voices"); // 20 * 2 + 1 == 41 nodes.
await scene.Sync();
Now when we send notes to voicemgr it finds an available voice and
assigns it the task of performing the note. Note that the Note
method
(NoteOn
too), returns two values, noteId
and voiceId
. The latter
is useful in setting parameters for an individual voice in the voicemgr's
pool.
pan.SetParam("Gain", -5); // sets gain for all voices.
osc.SetParam("/adsr/A", .5); // sets long Release for all voices.
osc.SetParam("/adsr/R", 1); // sets long Release for all voices.
for(let j=0;j<20;j++)
{
for(let i=0;i<10;i++)
{
// provide a random parameter value (pan.pan) for each note
// parameters key is anode (orig), value is map of paramId to value.
let [noteId,voiceId] = voicemgr.Note(30+3*i,
.1 +.4*Math.random(),
scene.Seconds(.5), 0);
voicemgr.SetVoiceParam(voiceId, pan, "Pan", Math.random());
await scene.Wait(scene.Seconds(.3));
}
}
This example shows that more complex subgraphs can be voiced.
Here's the subgraph of a single voice.
This example also shows how to use /adsr/T
to account for delay
effects to finish before a voice is released. As with the prior
example, keep an eye on the live voice counter within
AudioStatus/LoadAvg.