Created
April 23, 2020 06:04
-
-
Save catfact/0a95fe4890cc27130cd973f18fce15cf to your computer and use it in GitHub Desktop.
supercollider level control for polyphonic synths
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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); | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
this is a class and script for investigating strategies for automatic gain control of polyphonic synths.
two basic approaches are included:
LimiterorNormalizerUGens.