There’s no better way to procrastinate than trying to do the perfect anything. You spend days on tiny things.
Um, anyway, completely unrelated to that, I’ve spent the last two days making a bassdrum synthdef.
First, I made an awesome bass drum patch on my analog synthesizer. (Well, “awesome” is a strong word, but anyway, I made a patch.) Then, I recorded the patch to my computer using the beta version of Audacity and an older version of OS X. Then I lovingly cut them up perfectly and saved them as AIFF files. Then I threw away my original recording to save space on my overly crowded hard drive. then I discovered all my AIFF files were empty because the beta version of Audacity is great if you have the lastest version of OS X, but much less fantastic if you don’t.
Hey, but that analog warmth takes up a lot of hard drive space. You need to rotate through several samples or it sounds the same every time. And I don’t have a MIDI->CV converter to use if I carry around my synths thusly patched to all my gigs. Also, it’s kind of hard to transport while patched. And I’d need to buy a second synthesizer for other stuff. So clearly the next best thing to do is replicate the patch as a SuperCollider synthdef.
I have to admit upfront that this is not as nice as the analog version because the RLPF UGen is nice, but it’s not the same kind of sound as the Voyetra8 RLPF, which is Moogy. So if you want to improve the patch, you could use a Moog emulator ugen and make sure the bass is enhanced. I’m not going to bother because of the CPU hit, but I like ot keep stupid flash apps running in the background and have a poor sense of priorities.
Um, anyway, here’s the final version:
(
SynthDef(bassD, {|out = 0, hit_dur, amp, pan|
var ringmod, noise, lpf, hpf, lpf_env, hpf_env, noise_env, env, panner,
pitch_env, slew, trig, sh;
lpf_env = EnvGen.kr(Env.perc(0.05, 56.56, 12, -4));
hpf_env = EnvGen.kr(Env.perc(0.05, 48.54, 12, -6));
noise_env = EnvGen.kr(Env.perc(0.0001, 0.032, 1, -8));
pitch_env = EnvGen.kr(Env.perc(0.07, hit_dur, 12, -2));
env = EnvGen.kr(Env.perc(0.00005, hit_dur, amp, -2), doneAction: 2);
trig = Impulse.ar(0.45/hit_dur, 1.0.rand);
sh = Dwhite(-6, 6,inf);
slew = Lag.ar(Demand.ar(trig, 0, sh), hit_dur/1.7);
ringmod = LFTri.ar(
(31 + slew + LFTri.ar((27 + pitch_env).midicps, 4.0.rand, 60)).midicps,
4.0.rand); // 5 octave log range
noise = PinkNoise.ar(noise_env);
lpf = RLPF.ar(ringmod, (56.56 + lpf_env).midicps, 0.5);
hpf = RHPF.ar(lpf + noise, (48.54 + hpf_env).midicps, 0.5);
panner = Pan2.ar(hpf, pan, env);
Out.ar(out, panner);
}).store;
)
I use “hit_dur” instead of dur because I want my drum sound to end before the Pbind gets around to playing again, but you can change that to dur.
I figured out those values by using the Conductor class, which is in a quark. I’m fond of this as a sound design method because you can mess around with a GUI to change values and you can save them to disk. Here’s what the test code looks like:
(
SynthDef(bassD, {|out = 0, freq1, freq2, lpf_f, hpf_f, lpf_d, hpf_d, noise_d, dur, amp|
var ringmod, noise, lpf, hpf, lpf_env, hpf_env, noise_env, env, panner,
pitch_env, slew, imp;
lpf_env = EnvGen.kr(Env.perc(0.05, lpf_d, 12, -4));
hpf_env = EnvGen.kr(Env.perc(0.05, hpf_d, 12, -6));
noise_env = EnvGen.kr(Env.perc(0.0001, noise_d, 1, -8));
pitch_env = EnvGen.kr(Env.perc(0.07, dur, 12, -2));
env = EnvGen.kr(Env.perc(0.00005, dur, amp, -2), doneAction: 2);
imp = Dust.ar(0.45/dur, 12) - 6;
slew = Lag.ar(imp, dur/1.7);
ringmod = LFTri.ar((freq2.cpsmidi + slew +
LFTri.ar((freq1.cpsmidi + pitch_env).midicps, 4.0.rand, 60)
).midicps, 4.0.rand); // 5 octave log range
noise = PinkNoise.ar(noise_env);
lpf = RLPF.ar(ringmod, (lpf_f.cpsmidi + lpf_env).midicps, 0.5);
hpf = RHPF.ar(lpf + noise, (hpf_f.cpsmidi + hpf_env).midicps, 0.5);
panner = Pan2.ar(hpf, 0, env);
Out.ar(out, panner);
}).store;
Conductor.make({arg cond, freq1, freq2, lpf_f, hpf_f, lpf_d, hpf_d, noise_d, dur, db;
freq1.spec_(freq, 200+660.rand);
freq2.spec_(freq, 200+660.rand);
lpf_f.spec_(freq, 100+200.rand);
hpf_f.spec_(freq, 200+660.rand);
lpf_d.sp(1, 0.0001, 1.5, 0, 'linear');
hpf_d.sp(1, 0.0001, 1.5, 0, 'linear');
dur.sp(1, 0.0001, 2, 0, 'linear');
noise_d.sp(0.1, 0.00001, 1, 0, 'linear');
db.spec_(db, 02.ampdb);
cond.pattern_(
Pbind(
instrument, bassD,
db, db,
freq1, freq1,
freq2, freq2,
lpf_f, lpf_f,
hpf_f, hpf_f,
lpf_d, lpf_d,
hpf_d, hpf_d,
noise_d, noise_d,
dur, dur
)
)
}).show;
)
Those of you who like high bass drums will have fun with freq1 and freq2, if you’re bored and feel like messing around with such things.
That synthdef actually has more envelopes than my IRL synth does. I think it’s because I wrote a post about envelopes today like they’re the greatest thing since sliced bread. I also have a post about Conductors that I find somewhat more readable than the official documentation, but that’s probably just because I wrote it.
Edit
I corrected an error in the main synthdef. However, the one next to the conductor is the old version.