Last active
January 6, 2023 17:30
-
-
Save ckmahoney/a4ae25293c4f56b1d9d7d8aa5fcec732 to your computer and use it in GitHub Desktop.
Generator for a constrained range of FM SynthDef
This file contains 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
/* | |
Generate a list of new FM synths | |
This is a good preset for target frequencies in 50 - 600 Hz range. | |
Above that produces aliasing effects on the highs, and below on the lows. | |
*/ | |
( | |
// Initialize a shared list to record the new synthesizers generated | |
~synths = []; | |
// a gen which can be called globally | |
~fmSynthGenerator = {|name| | |
var timbreCar = [LFTri, LFSaw, SinOsc].choose; | |
var modulationRatio = (1..16).choose / (1..8).choose; | |
var carrier = [SinOsc, Pulse, LFSaw].choose; | |
var envGen = {|dur = 1| | |
var breath = (1..3).choose.half; | |
var ampMod = 2.pow((3..6).choose); | |
// create an envelope measured in seconds | |
var atk = 5 + 30.rand; | |
var sus = (10..80).choose + (SinOsc.kr(breath).range(0, ampMod)/2); | |
var rel = 100 - atk - sus + 15.rand; | |
var a0, a1, a2, a3; | |
a0 = 20 + 80.rand; | |
a1 = 85 + 15.rand; | |
a2 = (a1/2) + (a1/4).rand; | |
a3 = 0; | |
EnvGen.ar(Env.new([a0, a1, a2, a3]/100, [atk, sus, rel]*dur/100, \exp)); | |
}; | |
name = name ? "fmSynth_" ++ ~synths.size.asString; // set default value to enclosed | |
SynthDef(name, {|freq=300, amp=0.1, dur=4, cps=2.1, t=0| | |
var sig = 0; | |
// timbreMixMod is the amount of color change over time | |
var timbreMixMod = timbreCar.ar(cps/128, t.mod(32)/32).range(0, 1); | |
var ampEnv = envGen.value(dur); | |
var formants = []; | |
// use a stack of timbre shapers for thicc results | |
16.do{|i| formants = formants.add(i+1) }; | |
formants.scramble.keep(4); | |
formants.do{|hz, i| | |
var modTimeScale = 2.pow(i); | |
var gain = (formants.size - i)/(i + 1); | |
var timbre = timbreCar.ar(hz * cps/modTimeScale).range(0, 2.pow(i+5)); | |
var modIndex = timbreMixMod * timbre; | |
var mod = modIndex * SinOsc.kr(modulationRatio * freq, mul: gain * formants.size.reciprocal); | |
sig = sig + carrier.ar(freq + mod, mul: formants.size.reciprocal); | |
}; | |
sig = sig * ampEnv; | |
Out.ar(0, Pan2.ar(sig)); | |
DetectSilence.ar(sig, doneAction: 2); | |
}); | |
}; | |
/* Convenience, make a new one and hear it now */ | |
~synths = ~synths.add(~fmSynthGenerator.value); | |
~synths.last.add; | |
Pbindef(\player, \instrument, ~synths.last.name); | |
) | |
/* Play a melody */ | |
( | |
if (~synths.isNil || ~synths.size == 0, { "Make a synth, you silly goose!".throw }, { | |
// Generate a melody | |
var scale = [0,2,4,5,7,9,11]; | |
var octave = 5; | |
var midikey = 3; | |
var notes = (midikey + (12 * octave)) + scale.scramble; | |
var durs = notes.collect({[1/2, 1, 2].choose}); | |
// Use a global to track the progression of time. | |
~t = 0; | |
~cps = 1 + 2.0.rand; | |
TempoClock.tempo = ~cps; | |
Pbindef(\player, | |
\instrument, ~synths.last.name, | |
\midinote, Pshuf(notes, inf), | |
\dur, Pseq(durs, inf), | |
\t, Pfunc({|event| var dur = event.at(\dur); ~t = ~t + dur; ~t; }), | |
\cps, ~cps, | |
).play; | |
}); | |
) | |
/* Live update the player */ | |
// Manually set the instrument you want to hear | |
Pbindef(\player, \instrument, \fmSynth_1); | |
Pbindef(\player, \instrument, ~synths.last.name); | |
Pbindef(\player, \instrument, ~synths[4].name); | |
/* reset the synth bank */ | |
// ~synths = nil; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment