( // Generate a terrain table ~terrain = Buffer.alloc(s, 1024, 1); ~terrain.sine1([1, 0.5, 0.3, 0.2, 0.1], true); // You can sculpt this more chaotically SynthDef("fm_pulsar_terrain", { arg a, e, f, d; var t, overlap, carfreq, modfreq, mInd, vel, vel_, tblPos, sig; f = \freq.kr(100); vel = \kel.kr(0.5); vel_ = vel.clip(0,1.0).linlin( 0, 1, 2, 0.7); e = EnvGen.kr( Env.adsr( \att.kr(0.025) * vel_, \dec.kr(0.02) * vel_, \susl.kr(0.7), \rel.kr(4), curve: \curve.kr(-4) ), gate: \gate.kr(1), doneAction:2 ) * vel; carfreq = f * \cf.kr(2) + LFNoise1.kr(3).range(0,50); modfreq = carfreq * \mf.kr(2) + LFNoise1.kr(3).range(0,100); t = Impulse.ar(f); mInd = e.pow(3.3); // use envelope shape to shift table pos overlap = 1.1 - e.pow(0.5); d = overlap / f; // convert modfreq to index between 0 and 1 for VOsc tblPos = modfreq.linlin(0, 8000, 0, 1).wrap(0, 1); // safer than clip for edge cases a = Mix([ SinOsc.ar(f), 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.kr(0), a.dup * \gain.kr(0.1)); }).add; ) ( // MIDI ~notes = Array.newClear(128); ~ccVals = Array.fill(128, { 0.0 }); // NEW: store CC values // Map CC knobs 0–15 on MIDI channel 2 (0..15).do { |ccNum| MIDIdef.cc(\cc_ ++ ccNum, { |val, num, chan, src| if (chan == 1) { // channel 2 ~ccVals[num] = val.linlin(0, 127, 0.0, 1.0); }; }, ccNum); }; MIDIdef.noteOn(\noteOnTest, { arg vel, nn, chan, src; [vel, nn].postln; if (chan == 0, { ~notes[nn] = Synth.new( \fm_pulsar_terrain, [ \amp, vel.linexp(1,127,0.01,0.3), \freq, nn.midicps, // NEW: use CC knob values \att, ~ccVals[0].linlin(0, 1, 0.01, 0.2), \dec, ~ccVals[1].linlin(0, 1, 0.01, 0.2), \susl, ~ccVals[2].linlin(0, 1, 0.1, 1.0), \rel, ~ccVals[3].linlin(0, 1, 0.2, 8.0), \cf, ~ccVals[4].linlin(0, 1, 0.5, 4.0), \mf, ~ccVals[5].linlin(0, 1, 0.5, 4.0), \curve, ~ccVals[6].linlin(0, 1, -8, 8), \gain, ~ccVals[7].linlin(0, 1, 0.01, 0.5), \kel, ~ccVals[8].linlin(0, 1, 0.1, 1.0) // Knobs 9–15 are unused for now ] ); }); }); MIDIdef.noteOff(\noteOffTest, { arg vel, nn, chan; [vel, nn].postln; if (chan == 0, { ~notes[nn].set(\gate, 0); ~notes[nn] = nil; }); }); ) ( // MIDI setup MIDIClient.init; MIDIIn.connectAll; ) ( ~notes[60] = Synth(\fm_pulsar_terrain, [ \freq, 60.midicps, \amp, 0.2, \att, ~ccVals[0].linlin(0,1, 0.001, 0.2), \dec, ~ccVals[1].linlin(0,1, 0.001, 0.2), \susl, ~ccVals[2].linlin(0,1, 0.1, 1.0), \rel, ~ccVals[3].linlin(0,1, 0.1, 8.0), \cf, ~ccVals[4].linlin(0,1, 0.5, 5.0), \mf, ~ccVals[5].linlin(0,1, 0.5, 5.0), \curve, ~ccVals[6].linlin(0,1, -8, 8), \gain, ~ccVals[7].linlin(0,1, 0.01, 0.5), \kel, ~ccVals[8].linlin(0,1, 0.1, 1.0) ]); )