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.

Can you make one that combines these drums with the 303? That would be really fun.
Fucking hell this is super nice
This is so sick!
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.pdf
class 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/Q
for(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.1
input.compressorGain = 4; // min=1, max=4, step=0.1
input.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:-1
this.ratio = .9; // PARAM
this.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;
else
this.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];
else
return 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.html
const 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 lengths
for(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;
}
});
 
 
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7575 chars