( // Generate a terrain table ~terrain = Buffer.alloc(s, 1024, 1); ~terrain.sine1([1, 0.5, 0.3, 0.2, 0.1], true); SynthDef("fm_pulsar_terrain", { arg freq=440, gate=1, amp=0.2, att=0.01, dec=0.05, susl=0.6, rel=1.0, curve=(-4), cf=2, mf=2, kel=0.5, gain=0.1, outBus=0; var e, vel_, carfreq, modfreq, mInd, overlap, d, t, tblPos, a; vel_ = kel.clip(0,1.0).linlin(0, 1, 2, 0.7); e = EnvGen.kr( Env.adsr(att * vel_, dec * vel_, susl, rel, curve: curve), gate: gate, doneAction:2 ) * kel; carfreq = freq * cf + LFNoise1.kr(3).range(0,50); modfreq = carfreq * mf + LFNoise1.kr(3).range(0,100); t = Dust.ar(freq); mInd = e.pow(3.3); overlap = 1.1 - e.pow(0.5); d = overlap / freq; tblPos = modfreq.linlin(0, 8000, 0, 1).wrap(0, 1); a = Mix([ SinOsc.ar(freq), GrainBuf.ar(1, t, d, ~terrain, tblPos, carfreq, 0, 2) ]); a = LeakDC.ar(a * e); a = FreeVerb.ar(a, 0.33, 1); Out.ar(outBus, a.dup * gain); }).add; ) ( ~notes = Array.newClear(128); MIDIdef.noteOn(\noteOnTerrain, { |vel, nn, chan, src| if (chan == 0) { var freq = nn.midicps; var amp = vel.linexp(1,127,0.01,0.3); var p = ~ccVals; // current cc values ~notes[nn] = Synth(\fm_pulsar_terrain, [ \freq, freq, \amp, amp, \att, p[0].linlin(0,1, 0.001, 0.3), \dec, p[1].linlin(0,1, 0.001, 0.3), \susl, p[2].linlin(0,1, 0.1, 1.0), \rel, p[3].linlin(0,1, 0.1, 8.0), \cf, p[4].linlin(0,1, 0.5, 5.0), \mf, p[5].linlin(0,1, 0.5, 5.0), \curve, p[6].linlin(0,1, -8, 8), \gain, p[7].linlin(0,1, 0.01, 0.5), \kel, p[8].linlin(0,1, 0.1, 1.0) ]); } }); MIDIdef.noteOff(\noteOffTerrain, { |vel, nn, chan| if (chan == 0 and: { ~notes[nn].notNil }) { ~notes[nn].set(\gate, 0); ~notes[nn] = nil; } }); ) ( var paramName, adjustedValue; // Init array to store CC values ~ccVals = Array.fill(16, 0.5); // defaults to 0.5 (midpoint) // Assigning names to the parameters ~paramNames = [ "Attack", "Decay", "Sustain Level", "Release", "Carrier Frequency", "Modulation Frequency", "Envelope Curve" "Gain", "Kelvin", "Unused", "Unused", "Unused", "Unused", "Unused", "Unused", "Unused" ]; MIDIdef.cc(\ccTerrainKnobs, { |val, num, chan, src| if (chan == 0 and: { num < 16 }) { // Map CC value (0–127) to the range [0.0, 1.0] ~ccVals[num] = val.linlin(0, 127, 0.0, 1.0); // Print the parameter name and the adjusted value paramName = ~paramNames[num]; adjustedValue = ~ccVals[num].round(2); // Rounded for readability ("CC " ++ num ++ " (" ++ paramName ++ ") = " ++ val ++ " → " ++ adjustedValue).postln; } }); )