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); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This comment has been minimized.
this is a class and script for investigating strategies for automatic gain control of polyphonic synths.
two basic approaches are included:
LimiterorNormalizerUGens.