Skip to content

Instantly share code, notes, and snippets.

@ryanlaws
Created April 25, 2019 13:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ryanlaws/e2260802ff3eff77c5aff75713cd2455 to your computer and use it in GitHub Desktop.
Save ryanlaws/e2260802ff3eff77c5aff75713cd2455 to your computer and use it in GitHub Desktop.
Playing around with the web AudioBuffer API
{
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