bd
perc
hat
noise
4 loops, 3 filters (5 workers, 1 AudioWorklet) tick: 0.000, time: 0.000 (138 bpm)
techno beat 1
Using a shared filter for compression.
Slightly different with each playback.
Log in to post a comment.
wrighter
· 2023/03/02
insane
dougd
· 2022/12/07
Can you make one that combines these drums with the 303? That would be really fun.
ape2man
· 2022/11/16
Fucking hell this is super nice
joris
· 2022/11/14
This is so sick!
jurgen
· 2022/11/13
Very nice!! 🙌
Compressor
threshold
-15.0
gain
4.00
en
1.00
Kick
ws1
0.58
wsAsym
0.52
// WIP// Basic elements of a beat/bassline as commonly found in (modern) techno.function processSVF(s, input, a1, a2, a3, k) {var v1, v2, v3;v3 = input - s.ic2eq;v1 = a1 * s.ic1eq + a2 * v3;v2 = s.ic2eq + a2 * s.ic1eq + a3 * v3;s.ic1eq = 2 * v1 - s.ic1eq;s.ic2eq = 2 * v2 - s.ic2eq;s.lp = v2;s.bp = v1;s.hp = input - k * v1 - v2;s.ap = s.lp + s.hp - k * s.bp;}// State Variable Filter based on an article by cytomic Sound Music Software// https://cytomic.com/files/dsp/SvfLinearTrapOptimised2.pdfclass SVF {constructor(opt){this.stages = [];this.mode = opt ? opt.mode || 'lp' : 'lp';this.fc = 0;this.q = 1;this.num = opt ? opt.num || 1 : 1;// g parameter determines cutoff// k parameter = 1/Qfor(var i = 0; i < this.num; ++i) {this.stages.push({lp:0, bp:0, hp:0, ap:0, ic1eq:0, ic2eq:0});}this.q = opt && isFinite(opt.q) ? opt.q : 1;this.fc = opt && isFinite(opt.fc) ? opt.fc : .25;}process(input){if(this.fc != this._fc) {this._fc = this.fc;this._q = this.q;var fc = this.fc * .5;if (fc >= 0 && fc < .5) {this.g = Math.tan(Math.PI * fc);this.k = 1 / this.q;this.a1 = 1 / (1 + this.g * (this.g + this.k));this.a2 = this.g * this.a1;this.a3 = this.g * this.a2;}}if(this.q != this._q) {this._q = this.q;this.k = 1 / this.q;this.a1 = 1 / (1 + this.g * (this.g + this.k));this.a2 = this.g * this.a1;this.a3 = this.g * this.a2;}for(var i = 0; i < this.num; ++i) {processSVF(this.stages[i], input, this.a1, this.a2, this.a3, this.k);processSVF(this.stages[i], input, this.a1, this.a2, this.a3, this.k);input = this.stages[i][this.mode];}return input;}}input.compressorThreshold = -15; // min=-30, max=0, step=0.1input.compressorGain = 4; // min=1, max=4, step=0.1input.compressorEn = 1; // min=0, max=1, step=1 (off, on)const dB2gain = v => 10 ** (v / 20);const gain2dB = v => Math.log10(v) * 20;const compressor = filter.def(class Compressor {constructor(opt) {this.gain = 1;this.threshold = -40; // PARAM min:-30 max:-1this.ratio = .9; // PARAMthis.peak = 0.01;this.active = 1; // PARAM}process(inv, opt) {this.threshold = input.compressorThreshold;this.gain = input.compressorGain;this.active = input.compressorEn;var inputLevel = Math.max(Math.abs(inv[0]), Math.abs(inv[1]));if(inputLevel > this.peak)this.peak = inputLevel;elsethis.peak *= .9999;inputLevel = gain2dB(this.peak);var compression = Math.max(0, inputLevel - this.threshold);var dgain = compression ? dB2gain(-compression * this.ratio) : 1;dgain *= this.gain;if(this.active > .5)return [inv[0] * dgain, inv[1] * dgain];elsereturn inv;}}).createShared();class Delayline {constructor(n) {this.n = ~~n;this.p = 0;this.lastOut = 0;this.data = new Float32Array(n);}clock(input) {this.lastOut = this.data[this.p];this.data[this.p] = input;if(++this.p >= this.n) {this.p = 0;}return this.lastOut;}tap(offset) {var x = this.p - offset - 1;x %= this.n;if(x < 0) {x += this.n;}return this.data[x];}}function allpass(delayline, x, k) {var delayin = x - delayline.lastOut * k;var y = delayline.lastOut + k * delayin;delayline.clock(delayin);return y;}// Simple allpass reverberator, based on this article by Spin Semiconductor:// http://www.spinsemi.com/knowledge_base/effects.htmlconst reverb = filter.def(class {constructor(opt) {this.lastReturn = 0;this.krt = .5;this.delaylines = [];this.flt = new SVF({fc:.02, q:.5, num:1});this.pred = new Delayline(60 * .425 / ditty.bpm / ditty.dt);// Create several delay lines with random lengthsfor(var i = 0; i < 12; ++i) {this.delaylines.push(new Delayline(10 + Math.floor(Math.random() * 2441)));}this.wet = opt.wet || .04;}process(input, opt) {var inv = input[0];if(opt.dist) {inv = clamp(inv*opt.dist,-1,1)}inv = this.flt.process(this.pred.clock(inv));var v = this.lastReturn;// Let the signal pass through the loop of delay lines. Inject input signal at multiple locations.v = allpass(this.delaylines[0], v + inv, .5);v = allpass(this.delaylines[1], v, .5);this.delaylines[2].clock(v);v = this.delaylines[2].lastOut * this.krt;v = allpass(this.delaylines[3], v + inv, .5);v = allpass(this.delaylines[4], v, .5);this.delaylines[5].clock(v);v = this.delaylines[5].lastOut * this.krt;v = allpass(this.delaylines[6], v + inv, .5);v = allpass(this.delaylines[7], v, .5);this.delaylines[8].clock(v);v = this.delaylines[8].lastOut * this.krt;v = allpass(this.delaylines[9], v + inv, .5);v = allpass(this.delaylines[10], v, .5);this.delaylines[11].clock(v);v = this.delaylines[11].lastOut * this.krt;this.lastReturn = v;// Tap the delay lines at randomized locations and accumulate the output signal.var ret = [0, 0];ret[0] += this.delaylines[2].tap(111);ret[1] += this.delaylines[2].tap(2250);ret[0] += this.delaylines[5].tap(311);ret[1] += this.delaylines[5].tap(1150);ret[0] += this.delaylines[8].tap(511);ret[1] += this.delaylines[8].tap(50);ret[0] += this.delaylines[11].tap(4411);ret[1] += this.delaylines[11].tap(540);// Mix wet + dry signal.ret[0] = ret[0] * this.wet + input[0];ret[1] = ret[1] * this.wet + input[1];return ret;}});
7575 chars