Skip to content

Instantly share code, notes, and snippets.

@daylanKifky
Created January 27, 2018 23:00
Show Gist options
  • Save daylanKifky/b478d5b49ee7ec1698e0bf2f5120c814 to your computer and use it in GitHub Desktop.
Save daylanKifky/b478d5b49ee7ec1698e0bf2f5120c814 to your computer and use it in GitHub Desktop.
Web audio API MediaElement Mixer
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>AudioMixer</title>
</head>
<body>
<h1>GGJ18 AUDIO MIXER</h1>
<audio id="maria" src="radio_maria.mp3"></audio>
<audio id="track1" src="lala.mp3"></audio>
<audio id="track2" src="bob.mp3"></audio>
<audio id="track3" src="marley.mp3"></audio>
<h3>Master gain</h3>
<input type="range" id="master_ctl" target="master">
<h3>TRACK 1 gain</h3>
<input value=-90 min="-90" max="-60" type="range" id="track1_ctl" target="track1">
<h3>TRACK 2 gain</h3>
<input value=-90 min="-90" max="-60" type="range" id="track2_ctl" target="track2">
<h3>TRACK 3 gain</h3>
<input value=-90 min="-90" max="-60" type="range" id="track2_ctl" target="track3">
<script src="mixer.js"/>
</body>
</html>
var Perlin = function () {
var mask = 0xff;
var size = mask + 1;
var values = new Uint8Array(size * 2);
for (var i = 0; i < size; i++) {
values[i] = values[size + i] = 0|(Math.random() * 0xff);
}
var lerp = function (t, a, b) {
return a + t * (b - a);
};
var fade = function (t) {
return t * t * t * (t * (t * 6 - 15) + 10);
};
var grad1d = function (hash, x) {
return (hash & 1) === 0 ? x : -x;
};
var noise1d = function (x) {
var intX = (0|x) & mask;
var fracX = x - (0|x);
var t = fade(fracX);
var a = grad1d(values[intX], fracX);
var b = grad1d(values[intX + 1], fracX - 1);
return lerp(t, a, b);
};
return noise1d;
};
function Mixer() {
//////////////////////////////////
/// SETTINGS
//////////////////////////////////
this.maxDistance = -90;
this.minDistance = -60;
//Attention, the white noise can get pretty loud!! keep it low
this.maxNoiseGain = 0.05;
this.maxMariaGain = 1;
//////////////////////////////////
/// INTERNAL VARS
//////////////////////////////////
var self = this;
this.t = 0.0;
this.perlin = Perlin();
self.noise = 0;
this.audioCtx = new (window.AudioContext || window.webkitAudioContext)();
this.interference = 0;
this.sources = {};
this.gains = {};
this.gains['master'] = this.audioCtx.createGain();
//White noise
var whiteNoise = createWhiteNoise(this.audioCtx);
this.whiteGain = this.audioCtx.createGain();
this.whiteGain.gain.value = this.maxNoiseGain = 0.05;
whiteNoise.connect(this.whiteGain);
//Radio maria interference
var maria_src = document.querySelector('#maria');
maria_src.play();
maria_src.loop = true;
var maria = self.audioCtx.createMediaElementSource(maria_src);
this.mariaGain = this.audioCtx.createGain();
this.mariaGain.gain.value = this.maxMariaGain;
maria.connect(this.mariaGain);
//General noise combines white and maria
this.noiseGain = this.audioCtx.createGain();
this.whiteGain.connect(this.noiseGain);
this.mariaGain.connect(this.noiseGain);
this.noiseGain.connect(this.gains['master']);
document.querySelectorAll('audio').forEach(function(a){
if (a.id == "maria") return;
self.sources[a.id] = self.audioCtx.createMediaElementSource(a);
self.gains[a.id] = self.audioCtx.createGain();
a.play();
a.loop = true;
self.sources[a.id].connect(self.gains[a.id]);
self.gains[a.id].connect(self.gains['master']);
//Init all gain values to 0;
self.gains[a.id].gain.value = 0;
});
this.gains['master'].connect(this.audioCtx.destination);
this.setGain = function(g, key = "master"){
if (!this.gains.hasOwnProperty(key)){
console.log("WARNING [" + key + "] track doesn't exist");
return;
}
this.gains[key].gain.value = g;
console.log(key +" SETTTING GAIN VALUE : " + this.gains[key].gain.value)
var maxgain = 0
for (var g in this.gains) {
if (this.gains.hasOwnProperty(g) && g != "master") {
console.log(g +" VALUE : " + this.gains[g].gain.value)
maxgain = Math.max(this.gains[g].gain.value, maxgain);
this.interference = 1 - maxgain;
}
}
this.noiseGain.gain.value = this.interference ;
// console.log(maxgain);
}
this.distance = function(x, key){
if (!this.gains.hasOwnProperty(key)){
console.log("WARNING [" + key + "] track doesn't exist");
this.setGain(1, key);
return 1;
}
if (x < this.maxDistance){
console.log("MINOR: ", x)
this.setGain(0, key);
return 0;
} else if (x > this.minDistance){
console.log("MAYOR: ", x)
this.setGain(1);
return 1;
}
//Si, pippo
console.log("DISTANCE: ", x)
var pippo = (x - self.maxDistance) / (self.minDistance - self.maxDistance);
this.setGain(pippo, key);
return (pippo);
}
function animation(){
self.t+= 0.0015;
self.noise = self.perlin( (self.t+2) );
self.noise = Math.min(1, Math.max(self.noise, 0));
self.noise *= 5;
// console.log( self.noise );
self.mariaGain.gain.value = self.noise * self.maxMariaGain ;
window.requestAnimationFrame(animation);
}
console.log("ANIMATION");
window.requestAnimationFrame(animation);
};
function createWhiteNoise(audioContext){
var bufferSize = 4096;
var whiteNoise = audioContext.createScriptProcessor(bufferSize, 1, 1);
whiteNoise.onaudioprocess = function(e) {
var output = e.outputBuffer.getChannelData(0);
for (var i = 0; i < bufferSize; i++) {
output[i] = Math.random() * 2 - 1;
}
}
return whiteNoise;
}
//Create mixer
var m = new Mixer();
//Set master gain
m.setGain(0.5);
//Set distance min/max values
m.maxDistance = -90;
m.minDistance = -60;
//Audio tracks slider controllers
var gainCtl = document.querySelectorAll("input[type=range]");
gainCtl.forEach(function(ctl){
ctl.addEventListener("change", function(e){
var gainToChange = e.target.attributes.target.value;
//When controlling master gain change the value directly
if( gainToChange == "master"){
m.setGain(e.target.value/100);
return;
}
//When controlling the audio tracks use the distance mapping
m.distance( e.target.value, gainToChange);
// console.log(e.target.attributes);
// console.log( gainToChange + " gain: " + e.target.value/100)
});
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment