|
|
(
|
|
|
// 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)
|
|
|
|
|
|
]);
|
|
|
)
|