Skip to content

Instantly share code, notes, and snippets.

@cheery
Last active August 29, 2015 14:04
Show Gist options
  • Save cheery/61550611a1e435224508 to your computer and use it in GitHub Desktop.
Save cheery/61550611a1e435224508 to your computer and use it in GitHub Desktop.
Karplus-Strong
/*
Sort of string instrument simulation. I'm not sure where it's going to.
http://boxbase.org/sequencer/
*/
var hpi = Math.PI/2;
var pi = Math.PI;
var tau = Math.PI*2;
var sin = Math.sin;
var cos = Math.cos;
function dsp(t) {
var sum = 0;
for(var i = 0; i < midi.length; i++) {
var vo = midi[i];
var freq = vo.freq;
var vel = vo.vel;
var y = 0.99;
var l = Math.floor(sampleRate/vo.freq);
var cut = 8000;
if (!vo.ready) {
vo.ready = true;
vo.sync = Sync();
vo.ring = new RingBuffer(l);
vo.filt = new LowPass();
for (var j = 0; j < l; j++) {
var a = j / sampleRate;
var k = noise();
var q = vo.ring.last;
k = vo.filt(q*y, cut) + k;
vo.ring.push(k);
}
}
// var x = /*sin(t*tau*freq)noise()*/ * clamp(1 + vo.t - t, 0.5, 1);
// var x = sqr(t*tau*freq) * clamp(1 + vo.t - t, 0.5, 1);
var x = sin(t*tau*freq) * 0.0;
//var y = vo.ring.reduce(average, x);
var z = vo.ring.last;
x = vo.filt(z*y, cut) + x;
//x = vo.filt(x + y, freq/2);
//x = x + y*(0.5 + 0.4*saw(t*tau*freq));
vo.ring.push(x);
sum += x;
//sum += vo.sync(tri(t*tau*freq*(2.0 + 1.95*tri(t*tau/100)) ), sqr(t*tau*freq));
//sum += tri(freq*t*tau) * sqr((freq/4)*t*tau);
/*
if (midi[i].key < 70)
{
sum += sqr((freq/8+0.001*usin(freq/8*10.0*t*tau))*t*tau) * 0.5 * vel * Math.exp(z);
} else if (midi[i].key > 80)
{
sum += clamp(tri(freq*t*tau), -0.5, 0.5)*0.1;
} else
{
sum += sqr(freq*t*tau + tri(freq*t*tau/100)) * 0.1;
}
*/
}
return sum / 6;
}
function average(n, x) {
return (n+x) / 2;
}
function upol(x) {
return x * 0.5 + 0.5;
}
function bpol(x) {
return x * 2 - 1;
}
function usin(t) {
return sin(t) * 0.5 + 0.5;
}
function tri(x) {
return Math.abs(1 - ((x-hpi) % tau) / pi) * 2.0 - 1.0;
}
function saw(x) {
return 1 - ((x-hpi) % tau) / pi;
}
function sqr(x) {
return sin(x) > 0 ? 1 : -1;
}
function arp(ts, x, y, z) {
return Math.sin(x * Math.exp(-ts * y)) * Math.exp(-ts * z);
}
function clamp(x, mi, ma) {
return Math.max(mi, Math.min(ma, x));
}
function noise() {
return Math.random() * 2 - 1;
}
/*
linear interpolation between samples
*/
function mix(a, b, t) {
t = clamp(t, 0.0, 1.0);
var u = 1 - t;
return a*u + b*t;
}
function LP() {
var dout=0, din=0;
return function (x, cutoff) {
var rc = 1.0/(cutoff*2*tau);
var dt = 1.0/sampleRate;
var alpha = dt/(rc+dt);
dout = dout + alpha*(x - dout);
din = x;
return dout;
};
}
function HP() {
var dout=0, din=0;
return function (x, cutoff) {
var rc = 1.0/(cutoff*2*tau);
var dt = 1.0/sampleRate;
var alpha = rc/(rc+dt);
dout = alpha*(dout + x - din);
din = x;
return dout;
};
}
/*
supposed to reset the phase with an another signal
*/
function Sync() {
var previous = 0;
var start = 0;
return function(signal, t) {
if ((previous < 0) && (signal >= 0)) {
start = t;
}
previous = signal;
return t - start;
};
}
/*
butterworth filters, copied from http://basicsynth.com
*/
var sqr2 = 1.414213562;
function LowPass() {
var dlyOut2 = 0, dlyOut1 = 0;
var dlyIn2 = 0, dlyIn1 = 0;
return function (x, cutoff_freq) {
var c = 1.0 / Math.tan((Math.PI / sampleRate) * cutoff_freq);
var c2 = c*c;
var csqr2 = sqr2 * c;
var d = c2 + csqr2 + 1;
var In0 = 1 / d;
var In1 = In0 + In0;
var In2 = In0;
var Out1 = (2 * (1 - c2)) / d;
var Out2 = (c2 - csqr2 + 1) / d;
var out = (In0 * x)
+ (In1 * dlyIn1)
+ (In2 * dlyIn2)
- (Out1 * dlyOut1)
- (Out2 * dlyOut2);
dlyOut2 = dlyOut1; dlyOut1 = out;
dlyIn2 = dlyIn1; dlyIn1 = x;
return out;
};
}
function HighPass() {
var dlyOut2 = 0, dlyOut1 = 0;
var dlyIn2 = 0, dlyIn1 = 0;
return function (x, cutoff_freq) {
var c = Math.tan((Math.PI / sampleRate) * cutoff_freq);
var c2 = c*c;
var csqr2 = sqr2 * c;
var d = c2 + csqr2 + 1;
var In0 = 1 / d;
var In1 = -(In0 + In0);
var In2 = In0;
var Out1 = (2 * (c2 - 1)) / d;
var Out2 = (1 - csqr2 + c2) / d;
var out = (In0 * x)
+ (In1 * dlyIn1)
+ (In2 * dlyIn2)
- (Out1 * dlyOut1)
- (Out2 * dlyOut2);
dlyOut2 = dlyOut1; dlyOut1 = out;
dlyIn2 = dlyIn1; dlyIn1 = x;
return out;
};
}
function BandPass() {
var dlyOut2 = 0, dlyOut1 = 0;
var dlyIn2 = 0, dlyIn1 = 0;
return function (x, cutoff_freq) {
var c = 1 / Math.tan((Math.PI / sampleRate) * cutoff_freq);
var d = 1 + c;
var In0 = 1 / d;
var In1 = 0;
var In2 = -In0;
var Out1 = (-c*2*Math.cos(2*Math.PI*cutoff_freq/sampleRate)) / d;
var Out2 = (c - 1) / d;
var out = (In0 * x)
+ (In1 * dlyIn1)
+ (In2 * dlyIn2)
- (Out1 * dlyOut1)
- (Out2 * dlyOut2);
dlyOut2 = dlyOut1; dlyOut1 = out;
dlyIn2 = dlyIn1; dlyIn1 = x;
return out;
};
}
function RingBuffer(length){
this.array = new Float32Array(length);
this.length = length;
this.pos = 0;
this.last = 0;
this.first = 0;
}
RingBuffer.prototype.push = function(el){
this.last = this.array[this.pos];
this.first = el;
this.array[this.pos++] = el;
if (this.pos === this.length) {
this.pos = 0;
}
};
RingBuffer.prototype.forEach = function(fn){
var i = this.pos;
for (; i < this.length; i++) {
fn(this.array[i]);
}
if (this.pos > 0) {
for (i = 0; i < this.pos; i++) {
fn(this.array[i]);
}
}
};
RingBuffer.prototype.reduce = function(fn, val){
val = val || 0;
this.forEach(function(el){
val = fn(val, el);
});
return val;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment