Skip to content

Instantly share code, notes, and snippets.

@cheery
Last active November 20, 2015 12:47
Show Gist options
  • Save cheery/24b691ad848066e3dac5 to your computer and use it in GitHub Desktop.
Save cheery/24b691ad848066e3dac5 to your computer and use it in GitHub Desktop.
Sync, mix, unipolar, butterworth filters, "8-bit" ring modulator, envelope
var tau = Math.PI * 2;
var sync1 = Sync();
var sync2 = Sync();
var sync3 = Sync();
var lp = LowPass();
var bp = BandPass();
var hp = HighPass();
/* an ugly bell sequence */
var sequencer = Sequencer([
[0.0, bell, 1, 70],
[1.0, bell, 2, 72],
[1.5, bell, 2, 74],
[2.0, bell, 2, 69],
[2.5, bell, 2, 68],
[2.5, bell, 1, 65],
[3.0, bell, 1, 68]
]);
function bell(start, duration, note) {
var ev = Envelope([0.0, 1.0, 0.5, 0.0], [0.01, 1.5, 1.0]);
return function(t) {
if (start + ev.duration * duration <= t) return;
var freq = midi(note);
var x = bit8(tri(freq, t), sqr(freq - 100, t));
return x * ev((t - start) * duration);
};
}
function dsp(t) {
return lp(hp(sequencer(t, t % 8), 30), 20000) * 0.1;
}
/*
function dsp(t) {
var i = unipolar(sin(0.1, t)) * 0.5 + 0.1;
var u = sync1(tri(200, t), t);
var w = sync2(tri(201, t), t);
return mix(
tri(350, u) * tri(345, w),
sqr(440, t) * sqr(445, t),
i) * 0.1;
}
function dsp(t) {
var x = bit8(tri(440, t), sqr(120, t));
return 0.1 * lp(hp(x, 30), 20000);
}
function dsp(t) {
var x = bit8(tri(440, t), sqr(300, t));
return 0.3 * lp(hp(x, 30), 20000) * ev(t % 3);
}*/
function sin(freq, t) {
return Math.sin(t * freq * tau);
}
function saw(freq, t) {
return 1-2 * (t % (1 / freq)) * freq;
}
function tri(freq, t) {
return Math.abs(1 - (2 * t * freq) % 2) * 2 - 1;
}
function sqr(freq, t, duty) {
duty = duty || 0;
return tri(freq, t) > duty ? 1 : -1;
}
function noise() {
return Math.random() * 2 - 1;
}
/*
converts bipolar (-1, +1) signal to unipolar (+0, +1) signal.
*/
function unipolar(v) {
return v * 0.5 + 0.5;
}
function bipolar(v) {
return v * 2.0 - 1.0;
}
/*
converts midi note to frequency
*/
function midi(inp) {
return Math.pow(2, (inp - 69)/12) * 440;
}
/*
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;
}
/*
clip the value between minima and maxima.
*/
function clamp(x, mi, ma) {
return Math.max(mi, Math.min(ma, x));
}
/*
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;
};
}
/*
triangle waveporm used to be modulated with square signal in c64
this thing reduce the precision of the samples and invert the bits if
the modulator signal is positive (figured it's same as if most significant bit set)
*/
function bit8(signal, modulator) {
modulator = modulator || -1;
if (modulator >= 0) {
return bipolar((~Math.floor(unipolar(signal)*255)) / 255);
} else {
return bipolar(Math.floor(unipolar(signal)*255) / 255);
}
}
/*
an envelope generator, converts the amplitudes and durations into line functions.
*/
function Envelope(amps, durs) {
var xs = [];
var ks = [];
var cs = [];
var x = 0;
var a = 0;
for (var i = 0; i < durs.length; i++)
{
var a1, a2, d, k;
d = durs[i];
a1 = amps[i];
a2 = amps[i+1] || 0;
if (d > 0.0)
{
k = (a2 - a1) / d;
cs.push(a1 - k*x);
ks.push(k);
xs.push(x+d);
}
x += d;
a = a2;
}
var envelope = function(t) {
var i;
for (i = 0; i < xs.length; i++)
{
if (t < xs[i]) return ks[i]*t + cs[i];
}
return a;
};
envelope.duration = x;
return envelope;
}
/*
Calls several 'mini'-dsp functions in rising time sequence. "restarts" the sequence if called with lower a.
*/
function Sequencer(seq) {
var b = 0;
var k = 0;
var live = [];
return function (t, a) {
var sum = 0.0;
if (a < b) {
for (k = 0; k < seq.length; k++) {
if (a <= seq[k][0]) break;
}
}
b = a;
var pos, func, dur, args;
while((k < seq.length) && (seq[k][0] < a)) {
pos = seq[k][0];
func = seq[k][1];
dur = 1.0 / seq[k][2];
args = [pos - a + t, dur].concat(seq[k].slice(3));
live.push(func.apply(null, args));
k++;
}
var resp, surv = [];
for(var i = 0; i < live.length; i++) {
func = live[i];
resp = func(t);
if (resp === undefined) continue;
sum += resp;
surv.push(func);
}
live = surv;
return sum;
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment