Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
supercollider level control for polyphonic synths
// execute these lines first
p = PolyLevelTest.new;
p.init(s);
/// execute this line repeatedly to spawn voices
p.randomNote(s);
// use a Limiter instead of manual gain changes
// note that Limiter and Normalizer impose latency equal to attack time
p.estimatedGainPerVoiceDb = 0;
p.outputSynth.set(\limiterMode, 1);
// Normalizer
p.outputSynth.set(\limiterMode, 2);
PolyLevelTest {
// homework: experiment with this value
var <>estimatedGainPerVoiceDb = 3;
var <activeVoiceCount = 0;
var outputBus;
var <outputSynth;
init { arg server;
SynthDef.new(\fm_noise_1shot, {
arg out=0, amp=0.5, pan=0.0,
hz = 440,
mod_freq_ratio = 1.5,
mod_depth = 0.5,
noise_level = 0,
noise_flavor = 0.0,
bpf_fc = 1760,
bpf_rq = 0.5,
saw_level = 0.5,
sine_level = 0.5,
attack=0.1, sustain=0.2, release=1.0;
var mod, mod_hz, snd, noise, aenv;
mod = SinOsc.ar(mod_freq_ratio * hz, mul:mod_depth);
mod_hz = hz * (2 ** (mod * mod_depth));
snd = SinOsc.ar(mod_hz) * sine_level + VarSaw.ar(mod_hz) * saw_level;
noise = SelectX.ar(noise_flavor, [WhiteNoise.ar, PinkNoise.ar, BrownNoise.ar]);
snd = snd + noise * noise_level;
snd = BPF.ar(snd, bpf_fc, bpf_rq);
aenv = EnvGen.ar(Env.new([0, 1, 1, 0], [attack, sustain, release]), doneAction:2);
snd = snd * aenv * amp;
Out.ar(out, Pan2.ar(snd, pan));
}).send(server);
outputBus =Bus.audio(server, 2);
// a stereo patch synth with smoothed level control and a final limiting stage
outputSynth = SynthDef.new(\smooth_stereo_patch, {
arg in, out,
amp=1.0, amp_lag = 0.1,
limiter_mode = 0; // 0 == no limiting, 1== limiter, 2 == normalizer
// homework: break out and play with smoothing parameters, algos
var snd = In.ar(in, 2) * Lag.ar(K2A.ar(amp), amp_lag);
// homework: b reak out and play with limiter / normalizer parameters
snd = Select.ar(limiter_mode, [Limiter.ar(snd), Normalizer.ar(snd)]);
Out.ar(out, snd);
}).play(server, [\in, outputBus.index, \out, 0]);
}
randomNote { arg server;
var params;
var hz = 55 * (2 ** 4.0.rand);
var bpf_hz = hz * (2 ** 3.0.rand);
var bpf_rq = 0.01.rrand(0.9);
var mod_freq_ratio = 2 ** (2.0.rand);
var mod_depth = 4.0.rand;
var pan = 1.0.rand2;
var noise_level = 1.0.rand;
var sine_level = 1.0.rand;
var saw_level = 1.0.rand;
var noise_flavor = 2.0.rand;
var attack = 1.0.rand;
var sustain = 1.0.rand;
var release = 4.0.rand;
// normalize saw, sine, and noise level together
var amp_sum = noise_level + sine_level + saw_level;
noise_level = noise_level / amp_sum;
sine_level = sine_level / amp_sum;
saw_level = saw_level / amp_sum;
params = [
\out, outputBus.index,
\hz, hz,
\bpf_hz, bpf_hz,
\bpf_rq, bpf_rq,
\mod_freq_ratio, mod_freq_ratio,
\mod_depth, mod_depth,
\pan, pan,
\noise_level, noise_level,
\sine_level, sine_level,
\saw_level, saw_level,
\noise_flavor, noise_flavor,
\attack, attack,
\sustain, sustain,
\release, release,
];
postln(params);
this.newNote(params, server);
}
newNote { arg params, server;
var synth = Synth.new(\fm_noise_1shot, params, addAction:\addToHead);
synth.onFree({
this.handleVoiceFreed;
});
this.handleVoiceAdded;
}
handleVoiceAdded {
activeVoiceCount = activeVoiceCount + 1;
this.computeBusLevel;
}
handleVoiceFreed {
activeVoiceCount = activeVoiceCount - 1;
this.computeBusLevel;
}
computeBusLevel {
var voiceGainDb =estimatedGainPerVoiceDb * activeVoiceCount;
outputSynth.set(\amp, voiceGainDb.dbamp);
}
}
@catfact

This comment has been minimized.

Copy link
Owner Author

@catfact catfact commented Apr 23, 2020

this is a class and script for investigating strategies for automatic gain control of polyphonic synths.

two basic approaches are included:

  1. keep track of active voice count and attempt to manually control bus level.
  2. use Limiter or Normalizer UGens.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment