X-Git-Url: https://git.r.bdr.sh/rbdr/r.bdr.sh/blobdiff_plain/e6aee9be6b70daf4d3b4e0b1f0f41ce0626adaf0..429a1eb91c032790affd71b85ca670fa280ad17c:/jekyll/js/unlimited_pizza/pepperoni.js diff --git a/jekyll/js/unlimited_pizza/pepperoni.js b/jekyll/js/unlimited_pizza/pepperoni.js index 58bcc5f..9235794 100644 --- a/jekyll/js/unlimited_pizza/pepperoni.js +++ b/jekyll/js/unlimited_pizza/pepperoni.js @@ -1,318 +1,368 @@ -Class(UnlimitedPizza, "Pepperoni").inherits(Widget)({ - INNER_HTML : ' \ - \ -
\ -
\ -
\ -
\ -
\ - X \ -
\ - \ -
\ - \ - \ - \ - \ -
\ -
\ - ', - PAUSE : '▐▐', - RECORD : '⬤', - prototype : { - maxSize : 1048576, - recording : false, - source : null, - recorder : null, - context : null, - _delayNode : null, - _bandPassFilterNode : null, - _hiPassFilterNode : null, - _loPassFilterNode : null, - _convolverNode : null, - _distortionNode : null, - _activatedNodes : null, - workerPath : '/js/vendor/recorderjs/recorderWorker.js', - init : function init(config) { - Widget.prototype.init.call(this, config); - - if (!this.context) { - this.context = new (window.AudioContext || window.webkitAudioContext)(); - } - - this._delayNode = this.context.createDelay(1.0); - this._bandPassFilterNode = this.context.createBiquadFilter(); - this._hiPassFilterNode = this.context.createBiquadFilter(); - this._loPassFilterNode = this.context.createBiquadFilter(); - this._convolverNode = this.context.createConvolver(); - this._distortionNode = this.context.createWaveShaper(); - - this._distortionNode.curve = this._generateDistortion(400); - this._distortionNode.oversample = '4x'; - - this._activatedNodes = []; - - // config lo pass - this._loPassFilterNode.type = "lowpass"; - this._loPassFilterNode.frequency.value = 1000; - this._loPassFilterNode.gain.value = 25; - - // config hi pass - this._hiPassFilterNode.type = "highpass"; - this._hiPassFilterNode.frequency.value = 3000; - this._hiPassFilterNode.gain.value = 25; - - // config band pass - this._bandPassFilterNode.type = "bandpass"; - this._bandPassFilterNode.frequency.value = 2000; - this._bandPassFilterNode.gain.value = 25; - - if (!this.source) { - this._getUserMedia({ - audio : true - }, this._onUserMedia.bind(this), this._onUserMediaError.bind(this)) - } - - this.element.html(this.constructor.INNER_HTML); +'use strict'; - this.controlButton = this.element.find('.record-button'); - this.clearButton = this.element.find('.record-clear'); - this.audioElement = this.element.find('audio'); - this.progressBarContainer = this.element.find('.record-progress-bar-container'); - this.progressBar = this.element.find('.record-progress-bar'); - this.switches = this.element.find('.filter-switch'); +const internals = { - this._bindEvents(); + config: { + convolverNode: { + requestMethod: 'GET', + requestLocation: '/reverb.ogg', + responseType: 'arraybuffer' }, + distortionNode: { + amount: 400 + } + }, + + kTemplate: `Record. +
+
+
+
+
+ Clear recording. +
+ +
+ + + + + +
+
`, + kPauseLabel : 'Pause.', + kRecordLabel : 'Record.', +}; + +const Pepperoni = class Pepperoni { + constructor() { + + const context = new AudioContext(); + + const nodes = this._createNodes(context); + + this._activatedNodes = []; + }, + + _createNodes(context) { + + const delayNode = context.createDelay(1.0); + + const convolverNode = context.createConvolver(); + this._initializeConvolverNode(convolverNode); + + const loPassFilterNode = context.createBiquadFilter(); + loPassFilterNode.type = 'lowpass'; + loPassFilterNode.frequency.value = 1000; + loPassFilterNode.gain.value = 25; + + const hiPassFilterNode = context.createBiquadFilter(); + hiPassFilterNode.type = 'highpass'; + hiPassFilterNode.frequency.value = 3000; + hiPassFilterNode.gain.value = 25; + + const bandPassFilterNode = context.createBiquadFilter(); + bandPassFilterNode.type = 'bandpass'; + bandPassFilterNode.frequency.value = 2000; + bandPassFilterNode.gain.value = 25; + + const distortionNode = context.createWaveShaper(); + distortionNode.curve = this._generateDistortion(400); + distortionNode.oversample = '4x'; + } - record : function record() { - if (this.recorder && !this.recording) { - this._canRecord(function handleCanRecord(canRecord) { - if (canRecord) { - this.recording = true; - this.controlButton.addClass('recording') - this.controlButton.html(this.constructor.PAUSE); - this._interval = setInterval(this._onRecordCheck.bind(this), 100); - this.recorder.record(); - } - }.bind(this)); - } - }, + // Generates a wave to be used with the distortion wave shaper node - stop : function stop() { - if (this.recorder && this.recording) { - this.recording = false; - this.controlButton.removeClass('recording') - this.controlButton.html(this.constructor.RECORD); - clearInterval(this._interval); - this.recorder.stop(); - this.recorder.exportWAV(); - } - }, + _generateDistortion(amount = 50) { - clear : function clear() { - if (this.recorder) { - this.progressBar.width(0); - this.audioElement.attr('src', ''); - this.audioElement[0].load(); - this.recorder.clear() - } - }, + const sampleRate = 44100; + const curve = new Float32Array(sampleRate); + const angle = Math.PI / 180; - finalize : function finalize(callback) { - if (this.recorder) { - this.recorder.exportWAV(callback); - } - }, + for (let i = 0; i < sampleRate; ++i) { + const x = i * 2 / sampleRate - 1; + curve[i] = ( 3 + amount ) * x * 20 * angle / ( Math.PI + amount * Math.abs(x) ); + } - getBuffer : function getBuffer(callback) { - if (this.recorder) { - this.recorder.getBuffer(callback); - } - }, + return curve; + }, - _bindEvents : function bindEvents() { - var pepperoni = this; + // Initializes a convolver node by requesting the reverb sound, + // decoding it, and attaching it - this.controlButton.on('click', function () { - if (this.recording) { - this.stop(); - } else { - this.record(); - } - }.bind(this)) + _initializeConvolverNode(node) { - this.clearButton.on('click', function () { - if (!this.recording) { - this.clear(); - } - }.bind(this)) + const request = new XMLHttpRequest(); + request.open(config.convolverNode.requestMethod, config.convolverNode.requestLocation); + request.responseType = config.convolverNode.responseType; + request.addEventListener('load', (event) => { - this.switches.on('change', function (ev) { - if (!pepperoni.source) { - this.checked = false; - return false; - } - switch (this.name) { - case 'delay-filter': - if (this.checked) { - pepperoni._addNode(pepperoni._delayNode); - } else { - pepperoni._removeNode(pepperoni._delayNode); - } - break; - - case 'hipass-filter': - if (this.checked) { - pepperoni._addNode(pepperoni._hiPassFilterNode); - } else { - pepperoni._removeNode(pepperoni._hiPassFilterNode); - } - break; - - case 'bandpass-filter': - if (this.checked) { - pepperoni._addNode(pepperoni._bandPassFilterNode); - } else { - pepperoni._removeNode(pepperoni._bandPassFilterNode); - } - break; - - case 'lopass-filter': - if (this.checked) { - pepperoni._addNode(pepperoni._loPassFilterNode); - } else { - pepperoni._removeNode(pepperoni._loPassFilterNode); - } - break; - - case 'reverb-filter': - if (this.checked) { - pepperoni._addNode(pepperoni._convolverNode); - } else { - pepperoni._removeNode(pepperoni._convolverNode); - } - break; - - case 'distort-filter': - if (this.checked) { - pepperoni._addNode(pepperoni._distortionNode); - } else { - pepperoni._removeNode(pepperoni._distortionNode); - } - - break; - } + node.context.decodeAudioData(event.target.response, (buffer) => { + + node.buffer = buffer; }); - }, + }); + request.send(); + } - _onRecording : function _onRecording(buffer) { - this._buffer = buffer; + // Handles the XHR response to the reverb file - this.audioElement.attr('src', URL.createObjectURL(buffer)); - this.audioElement[0].load(); - }, - _onUserMedia : function _onUserMedia(localMediaStream) { - this.source = this.context.createMediaStreamSource(localMediaStream); - this.recorder = new Recorder(this.source, { - workerPath : this.workerPath, - callback : this._onRecording.bind(this) - }); - }, - _onUserMediaError : function _onUserMediaError(error) { - console.log("Something went wrong", error); - this.disable(); - }, + if (!this.source) { + this._getUserMedia({ + audio : true + }, this._onUserMedia.bind(this), this._onUserMediaError.bind(this)) + } - _onRecordCheck : function _onRecordCheck() { - this._canRecord(function (canRecord, bufferSize) { - var width = bufferSize * this.progressBarContainer.width() / this.maxSize; - this.progressBar.width(width); - if (!canRecord) { - this.stop(); - } - }.bind(this)); - }, + this.element.html(this.constructor.INNER_HTML); - _canRecord : function _canRecord(callback) { - this.recorder.getBuffer(function getBuffer(buffer) { - var bufferSize = buffer[0].length + buffer[1].length; - callback && callback(bufferSize <= this.maxSize, bufferSize); - }.bind(this)); - }, + this.controlButton = this.element.find('.record-button'); + this.clearButton = this.element.find('.record-clear'); + this.audioElement = this.element.find('audio'); + this.progressBarContainer = this.element.find('.record-progress-bar-container'); + this.progressBar = this.element.find('.record-progress-bar'); + this.switches = this.element.find('.filter-switch'); - _addNode : function _addNode(node) { - var i; + this._bindEvents(); +}; - i = this._activatedNodes.length; +const Pepperoni.prototype = { - this._activatedNodes.push(node); + _createNodes() { + + } - if (i === 0) { - this.source.disconnect(); - this.source.connect(node); + maxSize : 1048576, + recording : false, + source : null, + recorder : null, + context : null, + _delayNode : null, + _bandPassFilterNode : null, + _hiPassFilterNode : null, + _loPassFilterNode : null, + _convolverNode : null, + _distortionNode : null, + _activatedNodes : null, + workerPath : '/js/vendor/recorderjs/recorderWorker.js', + init : function init(config) { + }, + + record : function record() { + if (this.recorder && !this.recording) { + this._canRecord(function handleCanRecord(canRecord) { + if (canRecord) { + this.recording = true; + this.controlButton.addClass('recording') + this.controlButton.html(this.constructor.PAUSE); + this._interval = setInterval(this._onRecordCheck.bind(this), 100); + this.recorder.record(); + } + }.bind(this)); + } + }, + + stop : function stop() { + if (this.recorder && this.recording) { + this.recording = false; + this.controlButton.removeClass('recording') + this.controlButton.html(this.constructor.RECORD); + clearInterval(this._interval); + this.recorder.stop(); + this.recorder.exportWAV(); + } + }, + + clear : function clear() { + if (this.recorder) { + this.progressBar.width(0); + this.audioElement.attr('src', ''); + this.audioElement[0].load(); + this.recorder.clear() + } + }, + + finalize : function finalize(callback) { + if (this.recorder) { + this.recorder.exportWAV(callback); + } + }, + + getBuffer : function getBuffer(callback) { + if (this.recorder) { + this.recorder.getBuffer(callback); + } + }, + + _bindEvents : function bindEvents() { + var pepperoni = this; + + this.controlButton.on('click', function () { + if (this.recording) { + this.stop(); } else { - this._activatedNodes[i - 1].disconnect(); - this._activatedNodes[i - 1].connect(node); + this.record(); } + }.bind(this)) - node.connect(this.recorder.node); - this.recorder.context = node.context; - this.recorder.node.connect(this.recorder.context.destination) - - console.log("Adding: ", node); - }, + this.clearButton.on('click', function () { + if (!this.recording) { + this.clear(); + } + }.bind(this)) - _removeNode : function _removeNode(node) { - var i; - - i = this._activatedNodes.indexOf(node); - - node.disconnect(); - - if (i === 0 && i + 1 === this._activatedNodes.length) { - // It was the only one, connect source to recorder. - this.source.disconnect(); - this.source.connect(this.recorder.node); - } else if (i === 0) { - // Normal 0 case, connect source to node. Recorder stays the same - this.source.disconnect(); - this.source.connect(this._activatedNodes[i+1]); - } else if (i + 1 === this._activatedNodes.length) { - // It's not the 0 case, but we need to reconnect to recorder. - this._activatedNodes[i - 1].disconnect(); - this._activatedNodes[i - 1].connect(this.recorder.node); - } else { - // Normal case, connect previous node to node - this._activatedNodes[i - 1].disconnect(); - this._activatedNodes[i - 1].connect(this._activatedNodes[i + 1]); + this.switches.on('change', function (ev) { + if (!pepperoni.source) { + this.checked = false; + return false; } + switch (this.name) { + case 'delay-filter': + if (this.checked) { + pepperoni._addNode(pepperoni._delayNode); + } else { + pepperoni._removeNode(pepperoni._delayNode); + } + break; - this._activatedNodes.splice(i, 1); + case 'hipass-filter': + if (this.checked) { + pepperoni._addNode(pepperoni._hiPassFilterNode); + } else { + pepperoni._removeNode(pepperoni._hiPassFilterNode); + } + break; - console.log("Removing: ", node); - }, + case 'bandpass-filter': + if (this.checked) { + pepperoni._addNode(pepperoni._bandPassFilterNode); + } else { + pepperoni._removeNode(pepperoni._bandPassFilterNode); + } + break; - _generateDistortion : function generateDistortion(amount) { - var k = typeof amount === 'number' ? amount : 50, - n_samples = 44100, - curve = new Float32Array(n_samples), - deg = Math.PI / 180, - i = 0, - x; - for ( ; i < n_samples; ++i ) { - x = i * 2 / n_samples - 1; - curve[i] = ( 3 + k ) * x * 20 * deg / ( Math.PI + k * Math.abs(x) ); - } - return curve; - }, + case 'lopass-filter': + if (this.checked) { + pepperoni._addNode(pepperoni._loPassFilterNode); + } else { + pepperoni._removeNode(pepperoni._loPassFilterNode); + } + break; - // Normalize get user media - _getUserMedia : (navigator.getUserMedia || - navigator.webkitGetUserMedia || - navigator.mozGetUserMedia || - navigator.msGetUserMedia).bind(navigator) - } + case 'reverb-filter': + if (this.checked) { + pepperoni._addNode(pepperoni._convolverNode); + } else { + pepperoni._removeNode(pepperoni._convolverNode); + } + break; + + case 'distort-filter': + if (this.checked) { + pepperoni._addNode(pepperoni._distortionNode); + } else { + pepperoni._removeNode(pepperoni._distortionNode); + } + + break; + } + }); + }, + + _onRecording : function _onRecording(buffer) { + this._buffer = buffer; + + this.audioElement.attr('src', URL.createObjectURL(buffer)); + this.audioElement[0].load(); + }, + + _onUserMedia : function _onUserMedia(localMediaStream) { + this.source = this.context.createMediaStreamSource(localMediaStream); + this.recorder = new Recorder(this.source, { + workerPath : this.workerPath, + callback : this._onRecording.bind(this) + }); + }, + + _onUserMediaError : function _onUserMediaError(error) { + console.log("Something went wrong", error); + this.disable(); + }, + + _onRecordCheck : function _onRecordCheck() { + this._canRecord(function (canRecord, bufferSize) { + var width = bufferSize * this.progressBarContainer.width() / this.maxSize; + this.progressBar.width(width); + if (!canRecord) { + this.stop(); + } + }.bind(this)); + }, + + _canRecord : function _canRecord(callback) { + this.recorder.getBuffer(function getBuffer(buffer) { + var bufferSize = buffer[0].length + buffer[1].length; + callback && callback(bufferSize <= this.maxSize, bufferSize); + }.bind(this)); + }, + + _addNode : function _addNode(node) { + var i; + + i = this._activatedNodes.length; + + this._activatedNodes.push(node); + + if (i === 0) { + this.source.disconnect(); + this.source.connect(node); + } else { + this._activatedNodes[i - 1].disconnect(); + this._activatedNodes[i - 1].connect(node); + } + + node.connect(this.recorder.node); + this.recorder.context = node.context; + this.recorder.node.connect(this.recorder.context.destination) + + console.log("Adding: ", node); + }, + + _removeNode : function _removeNode(node) { + var i; + + i = this._activatedNodes.indexOf(node); + + node.disconnect(); + + if (i === 0 && i + 1 === this._activatedNodes.length) { + // It was the only one, connect source to recorder. + this.source.disconnect(); + this.source.connect(this.recorder.node); + } else if (i === 0) { + // Normal 0 case, connect source to node. Recorder stays the same + this.source.disconnect(); + this.source.connect(this._activatedNodes[i+1]); + } else if (i + 1 === this._activatedNodes.length) { + // It's not the 0 case, but we need to reconnect to recorder. + this._activatedNodes[i - 1].disconnect(); + this._activatedNodes[i - 1].connect(this.recorder.node); + } else { + // Normal case, connect previous node to node + this._activatedNodes[i - 1].disconnect(); + this._activatedNodes[i - 1].connect(this._activatedNodes[i + 1]); + } + + this._activatedNodes.splice(i, 1); + + console.log("Removing: ", node); + }, + + // Normalize get user media + _getUserMedia : (navigator.getUserMedia || + navigator.webkitGetUserMedia || + navigator.mozGetUserMedia || + navigator.msGetUserMedia).bind(navigator) +} });