Created
April 25, 2019 13:57
-
-
Save ryanlaws/e2260802ff3eff77c5aff75713cd2455 to your computer and use it in GitHub Desktop.
Playing around with the web AudioBuffer API
This file contains hidden or 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
{ | |
const audioCtx = new (window.AudioContext || window.webkitAudioContext)(); | |
const sr = audioCtx.sampleRate; | |
const hzInc = 1 / sr; | |
const noteToHz = (midiNote) => Math.pow(2, midiNote/12) * 8.17570643783345; | |
class Accumulator { | |
constructor() { | |
this.oscs = []; | |
} | |
addOsc(osc) { | |
this.oscs.push(osc); | |
} | |
accumulate() { | |
this.oscs.forEach(osc => osc.accumulate()) | |
} | |
} | |
class Phasor { | |
constructor(accumulator, freq=220) { | |
this.phase = 0; | |
this.freq = freq; | |
accumulator.addOsc(this); | |
} | |
value(offset=0) { | |
// Offset allows phase modulation (i.e. Yamaha FM) | |
return (this.phase + offset) % 1; | |
} | |
accumulate() { | |
var inc = hzInc * this.freq; | |
this.phase = (this.phase + inc) % 1; | |
} | |
} | |
class SawOsc extends Phasor { | |
constructor(accumulator, freq) { | |
super(accumulator, freq); | |
} | |
value() { | |
const value = (super.value() * 2) - 1; | |
return value; | |
} | |
} | |
class PulseOsc extends Phasor { | |
constructor(accumulator, freq, pulseWidth=0.5) { | |
super(accumulator, freq); | |
this.pulseWidth = pulseWidth; | |
} | |
value() { | |
const value = super.value() > this.pulseWidth ? 1 : -1; | |
return value; | |
} | |
} | |
class SinOsc extends Phasor { | |
constructor(accumulator, freq) { | |
super(accumulator, freq); | |
} | |
value() { | |
const value = Math.sin(super.value() * 2 * Math.PI); | |
return value; | |
} | |
} | |
var myArrayBuffer = audioCtx.createBuffer(2, sr * 8, sr); | |
// feedback delay (circular buffer) | |
var delaySeconds = 0.375; | |
var delayBufferLength = Math.floor(delaySeconds * sr); | |
var delayBuffer = new Array(delayBufferLength); | |
delayBuffer.fill(0); | |
console.log(delayBuffer) | |
var delayFeedback = 0.85; | |
// mess with these! | |
var pitchSequence = [50, 57, 53, 55]; | |
//pitchSequence = [50, 57, 53, 55, 50, 57, 53]; | |
//pitchSequence = pitchSequence.map(p => Math.random() * 10 + 50); | |
var gateSequence = [1, 0, 0, 1, 0, 0, 1, 0]; | |
//gateSequence = [1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1]; | |
var pitchSpeed = 1; | |
//pitchSpeed = 4; | |
for (var channel = 0; channel < myArrayBuffer.numberOfChannels; channel++) { | |
var nowBuffering = myArrayBuffer.getChannelData(channel); | |
var acc = new Accumulator(); | |
var pulse = new PulseOsc(acc, noteToHz(50) + channel); | |
var kickOsc = new SinOsc(acc, 50); | |
var lfo1 = new Phasor(acc, 0.5); | |
var lfo2 = new Phasor(acc, 8); | |
var lfo3 = new Phasor(acc, 2); | |
var lfo4 = new Phasor(acc, 0.7); | |
for (var i = 0; i < myArrayBuffer.length; i++) { | |
const env2 = Math.pow(1 - lfo2.value(), 2); | |
const env3 = Math.pow(1 - lfo3.value(), 8); | |
const env4 = Math.pow(1 - lfo3.value(), 3); | |
const tri1 = Math.abs(lfo1.value() * 2 - 1); | |
const tri2 = Math.abs(lfo4.value() * 2 - 1); | |
pulse.pulseWidth = tri1; | |
pulse.freq = noteToHz(pitchSequence[Math.floor(i * pitchSpeed / sr) % pitchSequence.length] + channel * 0.05 + tri2 * 0.5); | |
const pulseOut = pulse.value() * env2 * gateSequence[Math.floor((i * 8) / sr) % gateSequence.length]; | |
delayBuffer[i % delayBufferLength] = (delayFeedback * delayBuffer[i % delayBufferLength]) + ((1 - delayFeedback) * pulseOut); | |
kickOsc.freq = (env3 * 150) + 50; | |
// wrapping distortion | |
let kickOut = kickOsc.value() * 1.5; | |
if (kickOut > 1) { | |
let excess = kickOut % 1; | |
kickOut -= excess; | |
} | |
kickOut *= env4; | |
nowBuffering[i] = (delayBuffer[i % delayBufferLength] + pulseOut + kickOut) / 3; | |
acc.accumulate(); | |
} | |
} | |
var source = audioCtx.createBufferSource(); | |
source.buffer = myArrayBuffer; | |
source.connect(audioCtx.destination); | |
source.start(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment