Skip to content

Instantly share code, notes, and snippets.

@terribleplan
Last active December 11, 2016 15:16
Show Gist options
  • Save terribleplan/69ad12da36a9e42fe85775f02bca518c to your computer and use it in GitHub Desktop.
Save terribleplan/69ad12da36a9e42fe85775f02bca518c to your computer and use it in GitHub Desktop.
//Constant helpers
const octave = (hz, octave) => Math.pow(2, octave) * hz;
//Constants
const NOTES = (() => {
const NOTES = {
C: 16.35,
"C#": 17.32,
D: 18.35,
"D#": 19.45,
E: 20.60,
F: 21.83,
"F#": 23.12,
G: 24.50,
"G#": 25.96,
A: 27.50,
"A#": 29.14,
B: 30.87
};
NOTES["E#"] = NOTES.F;
NOTES["B#"] = octave(NOTES.C, 1);
NOTES.Cb = octave(NOTES.B, -1);
NOTES.Db = NOTES["C#"];
NOTES.Eb = NOTES["D#"];
NOTES.Fb = NOTES.E;
NOTES.Gb = NOTES["F#"];
NOTES.Ab = NOTES["G#"];
NOTES.Bb = NOTES["A#"];
//Alias #'s to s's, so that you don't have to use square brackets
Object.keys(NOTES).forEach((k, v) => (k[1] === "#" && (NOTES[`${k[0]}s`] = v)));
return NOTES;
})();
//This assumes common time
//const TIME_SIGNATURE = [4, 4];
//const [BEATS_PER_BAR, BEAT_UNIT] = TIME_SIGNATURE;
const NOTE_LENGTHS = {
1: 4,
2: 4 / 2,
4: 4 / 4,
8: 4 / 8,
16: 4 / 16
};
const BPM = 120;
//Scoped to signal(s)
const hardLimit = (signal) => Math.min(Math.max(signal, -1), 1);
const combine = (...signals) => signals.reduce((a, b) => a + b);
const tanhLimit = (signal) => Math.tanh(signal);
const attenuate = (signal, amount) => signal / amount;
var hasLogged = false;
const softMix = (...signals) => {
const attenuationFactor = Math.sqrt(signals.length + 2);
!hasLogged && console.log("attenuation factor %s", attenuationFactor) || (hasLogged = true);
return combine(signals.map(s => attenuate(s, attenuationFactor)));
};
//Scoped to time
//Instrument helpers
const toFullRange = (signal) => (signal * 2) - 1;
//Instruments
const sine = (ti, hz) => Math.sin(ti * hz * Math.PI * 2);
const saw = (t, hz) => toFullRange((t * hz) % 1);
const square = (t, hz) => toFullRange(Math.floor((t * hz) % 2));
//todo
const triangle = (t, hz) => toFullRange(Math.abs((t * hz * 2) % 2));
const bpm = (bpm) => t / 60 * bpm;
const beatToTime = (beat, bpm) => beat * 60 / bpm;
//Scoped to beat
const pattern = (beat, signals = [], speed = NOTE_LENGTHS[4]) => {
if (signals.length < 2) {
return signals[0] || 0;
}
return signals[Math.floor(beat * speed) % signals.length];
};
const gate = (beat, signal = 0, gatePattern = [true, false], speed = NOTE_LENGTHS[4]) => {
return pattern(beat, gatePattern.map(on => (on && signal) || 0), speed);
};
const retrigger = (beat, beatCycle, generator) => {
return generator(t%1);
}
const envelope = (beat, signalGenerator, attack, decay, sustain, release) => {
}
const beat = bpm(BPM);
//"instruments"
const organ = (note) => combine(
sine(note),
sine(note),
sine(octave(note, 1)),
sine(octave(note, 2))
) / 3.5;
const overdrive = (amount, signal) => Math.tanh(signal * amount) * (amount < 1 ? 1 / amount : 1);
return sine(t % .1875, octave(NOTES.A, 4))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment