«Little FM Synth (Second Version)» by yl0506
on 29 Apr'13 11:06 inI tried to increase the modularity of the code synthdef. It remains to improve the modularity and conciseness of code pattern. I find it too "heavy", too "long" ...
I also added comments in English this time!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
/* My simple synth with some controls, and then a small musical pattern to test it. I'm a newbie, so your help will be welcome if you learn me how improve these codes. Goal : This little exercise is to understand how the modulation of parameters of a synthesizer is possible with SuperCollider day 2 : I tried to increase the modularity of the code synthdef. It remains to improve the modularity and conciseness of code pattern. I find it too "heavy", too "long" ... */ // Execute this code first ( SynthDef( /* It is probably possible to further improve the modularity of the code. Your help is welcome. */ name:"fm3", ugenGraphFunc:{ // We declare arguments (corresponding to variables, if I understood correctly) arg f1 = 220, freqmu = 2, f2, index = 8, decay = 0.25, vol = 0.1, filterstart = 2000, filterend = 10, phasemod = 10, /* Try to declare an argument "signal" that will be used to better visualize the structure of the synth. Otherwise it's easy to be lost by different nesting, if we want to add multiple modules. Then I continue to declare variables representing each module, I guess that's the way to go !*/ signal, filter, oscillo, env1, env2, lfo1, lfo2, // ----> variable "volu" allows you to easily increase the output volume volu = 1; // Once variable declarations made, we can do calculations f2 = freqmu * f1; lfo1 = SinOsc.kr(55,0,1,0); lfo2 = SinOsc.kr(27.5,0,1,0); env1 = Line.kr( start: 0.5, end: 0, dur: decay, doneAction: 2); env2 = XLine.kr( start: filterstart, end: filterend, dur: decay - 0.09); oscillo = PMOsc.ar( // a simple FM oscillator carfreq:[ f1, f1 + lfo1 * 2], // with small modulation on chanel 1 modfreq:[f2 - lfo2 * 3, f2 + lfo2 * 3], // with modulation on the 2 chanels pmindex: index * (env1 * 2), modphase: phasemod + lfo1, // Is it a good idea ? mul: vol, add: 0); filter = Resonz.ar( in: oscillo * env1, freq: env2, bwr: 2 * env1, mul: (2 + volu) * env1 ); signal = Out.ar( bus: 0, channelsArray: filter ); }).add; ); /* Let's add reverb, for this we create a synth that does that. I seem to have taken a code from somewhere... help system perhaps... I don't remember... Then execute this code */ ( SynthDef(\FreeVerb2x2, {|outbus, mix = 0.25, room = 0.70, damp = 0.5, amp = 1.0| var signal; signal = In.ar(outbus, 2); ReplaceOut.ar(outbus, FreeVerb2.ar( // FreeVerb2 - true stereo UGen signal[0], // Left channel signal[1], // Right Channel mix, room, damp, amp)); // same params as FreeVerb 1 chn version }).add; ); // Finally execute next code ( /* We declare some variables to modularize the code. You can probably do better! I suppose there are other ways to easily enter notes in these patterns. But I do not yet know enough supercollider to successfully do so. The code is very long, I'd like it to be more concise. Unfortunately, I think I need help to successfully shorten it. All suggestions are welcome. */ var a, b, c, d, e, f, n1 = 45, n2, n3, o1, o2, o3, p1, p2, p3, r1 = 1, r2, r3, r4; /* a, b, c, d, e ,f : some patterns n1, n2, n3, o1, o2, o3, p1, p2, p3 : some notes in midi format n1 is the starting note, here A3 in midi format. Let's calculate other notes : */ n2 = n1 + 12; n3 = n2 + 12; o1 = n1 + 2; o2 = o1 + 12; o3 = o2 + 12; p1 = n1 + 4; p2 = p1 + 12; p3 = p2 + 12; /* r1, r2, r3, r4 : pattern repeat I think I made a mistake but where ? */ r2 = r1 * 2; r3 = r1 * 4; r4 = r1 * 8; // pattern a a = Pbind( \instrument, 'fm3', \dur, Pseq([0.2, 0.1, 0.1, 0.2, 0.2, 0.2, 0.2, 0.2], r1), //\freqmu, Pshuf([1, 2, 3, 4, 5, 6, 7, 8], 128), \freqmu, Pshuf([1, 2, 3, 4], r2), \f1, Pseq([n2,n3,n3,n2], r2).midicps, // \index, Prand([3,4,5,6],256), \vol, Prand([0.1,0.05,0.04,0.08],repeats:r4), ); // pattern b b = Pbind( \instrument, 'fm3', \dur, Pseq([0.4, 0.2, 0.2, 0.4, 0.4, 0.4, 0.4, 0.2], r1), //\freqmu, Pshuf([1, 2, 3, 4, 5, 6, 7, 8], 128), \freqmu, Pshuf([4, 5, 2, 7], r1), \f1, Pseq([n1,n2,n2,n1], r1).midicps, \index, Pseq([3,4,5,6], r1), \vol, Prand([0.2,0.15,0.1,0.2],repeats:r3), \filterend, Pseq(list:[10,100,1,500],repeats: r1), \filterstart, Pseq(list:[1000,2000,500,3000],repeats: r1), \phasemod, Pseq(list:[100,5,10,15,20],repeats: r1), \decay, Pseq(list:[0.25,0.40,0.2,0.55,0.15],repeats: r1), ); // pattern c c = Pbind( \instrument, 'fm3', \dur, Pseq([0.2, 0.1, 0.1, 0.2, 0.2, 0.2, 0.2, 0.2], r1), //\freqmu, Pshuf([1, 2, 3, 4, 5, 6, 7, 8], 128), \freqmu, Pshuf([1, 2, 3, 4], r2), \f1, Pseq([o2,o3,o3,o2], r2).midicps, // \index, Prand([3,4,5,6],256), \vol, Prand([0.1,0.05,0.04,0.08],repeats:r4), ); // pattern d d = Pbind( \instrument, 'fm3', \dur, Pseq([0.4, 0.2, 0.2, 0.4, 0.4, 0.4, 0.4, 0.2], r1), //\freqmu, Pshuf([1, 2, 3, 4, 5, 6, 7, 8], 128), \freqmu, Pshuf([4, 5, 2, 7], r1), \f1, Pseq([o1,o2,o2,o1], r1).midicps, \index, Pseq([3,4,5,6], r1), \vol, Prand([0.2,0.15,0.1,0.2],repeats:r3), \filterend, Pseq(list:[10,100,1,500],repeats: r1), \filterstart, Pseq(list:[1000,2000,500,3000],repeats: r1), \phasemod, Pseq(list:[100,5,10,15,20],repeats: r1), \decay, Pseq(list:[0.25,0.40,0.2,0.55,0.15],repeats: r1), ); // pattern e e = Pbind( \instrument, 'fm3', \dur, Pseq([0.2, 0.1, 0.1, 0.2, 0.2, 0.2, 0.2, 0.2], r1), //\freqmu, Pshuf([1, 2, 3, 4, 5, 6, 7, 8], 128), \freqmu, Pshuf([1, 2, 3, 4], r2), \f1, Pseq([p2,p3,p3,p2], r2).midicps, // \index, Prand([3,4,5,6],256), \vol, Prand([0.1,0.05,0.04,0.08],repeats:r4), ); // pattern f f = Pbind( \instrument, 'fm3', \dur, Pseq([0.4, 0.2, 0.2, 0.4, 0.4, 0.4, 0.4, 0.2], r1), //\freqmu, Pshuf([1, 2, 3, 4, 5, 6, 7, 8], 128), \freqmu, Pshuf([4, 5, 2, 7], r1), \f1, Pseq([p1,p2,p2,p1], r1).midicps, \index, Pseq([3,4,5,6], r1), \vol, Prand([0.2,0.15,0.1,0.2],repeats:r3), \filterend, Pseq(list:[10,100,1,500],repeats: r1), \filterstart, Pseq(list:[1000,2000,500,3000],repeats: r1), \phasemod, Pseq(list:[100,5,10,15,20],repeats: r1), \decay, Pseq(list:[0.25,0.40,0.2,0.55,0.15],repeats: r1), ); z = Synth(\FreeVerb2x2, [\outbus, 0], addAction:\addToTail); // It plays two patterns in parallel, ones after the others Pseq([Ppar([a, b]),Ppar([c, d]),Ppar([e, f]),Ppar([c, d]),Ppar([a, b])], 3).play; )
reception
"We declare arguments (corresponding to variables, if I understood correctly)"
you only want to have arguments for your synth's controls, everything else should be declared as variables, i.e. everything from signal down should be a var (including f2, and excluding vol which should remain an argument and be named 'amp'). i'm not sure about 'volu', you don't use it in the patterns. it should probably be merged with 'vol'.
there are some standard names for controls which you should use so that you can take advantage of some goodness built into patterns/events which you will discover as you learn more.
'freq' for your fundamental frequency 'amp' for 'vol' 'sustain' for 'decay' 'gate' is used for EnvGens and 'out' is usually specified as a control so that you can choose your output bus.
your freqmu control is only being multiplied once inside your synth, you might consider moving this out to the client side and performing this multiplication in a pattern for example.
\f1, Pseq([n2,n3,n3,n2], r2).midicps * Pshuf([1, 2, 3, 4], r2)
if you use the 'freq' naming convention you can drop the midicps call and just write
\note, Pseq([n2,n3,n3,n2], r2)* Pshuf([1, 2, 3, 4], r2)
lastly there is no need to assign the Out ugen to the variable 'signal', or any other variable. in fact there is no need for the signal variable at all.
hope that helps.
Thank you Polypus74 for your help. I will try soon to make a new version with your help.