Last active
December 11, 2016 15:16
-
-
Save terribleplan/69ad12da36a9e42fe85775f02bca518c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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