X-Git-Url: https://git.r.bdr.sh/rbdr/r.bdr.sh/blobdiff_plain/bd7473dd12925b32132a9113118361010baa2978..81003de3d5db449a97b4718f50d998f2f036ad6b:/jekyll/js/unlimited_pizza/pepperoni.js?ds=inline diff --git a/jekyll/js/unlimited_pizza/pepperoni.js b/jekyll/js/unlimited_pizza/pepperoni.js index d874243..9235794 100644 --- a/jekyll/js/unlimited_pizza/pepperoni.js +++ b/jekyll/js/unlimited_pizza/pepperoni.js @@ -1,152 +1,368 @@ -Class(UnlimitedPizza, "Pepperoni").inherits(Widget)({ - INNER_HTML : ' \ - \ -
\ -
\ -
\ -
\ -
\ - X \ -
\ - \ -
\ - ', - PAUSE : '▐▐', - RECORD : '⬤', - prototype : { - maxSize : 1048576, - recording : false, - source : null, - recorder : null, - context : 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)(); - } +'use strict'; - if (!this.source) { - this._getUserMedia({ - audio : true - }, this._onUserMedia.bind(this), this._onUserMediaError.bind(this)) - } +const internals = { + + config: { + convolverNode: { + requestMethod: 'GET', + requestLocation: '/reverb.ogg', + responseType: 'arraybuffer' + }, + distortionNode: { + amount: 400 + } + }, - this.element.html(this.constructor.INNER_HTML); + kTemplate: `Record. +
+
+
+
+
+ Clear recording. +
+ +
+ + + + + +
+
`, + kPauseLabel : 'Pause.', + kRecordLabel : 'Record.', +}; - 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'); +const Pepperoni = class Pepperoni { + constructor() { - this._bindEvents(); - }, + const context = new AudioContext(); - 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)); + 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'; + } + + // Generates a wave to be used with the distortion wave shaper node + + _generateDistortion(amount = 50) { + + const sampleRate = 44100; + const curve = new Float32Array(sampleRate); + const angle = Math.PI / 180; + + 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) ); + } + + return curve; + }, + + // Initializes a convolver node by requesting the reverb sound, + // decoding it, and attaching it + + _initializeConvolverNode(node) { + + const request = new XMLHttpRequest(); + request.open(config.convolverNode.requestMethod, config.convolverNode.requestLocation); + request.responseType = config.convolverNode.responseType; + request.addEventListener('load', (event) => { + + node.context.decodeAudioData(event.target.response, (buffer) => { + + node.buffer = buffer; + }); + }); + request.send(); + } + + // Handles the XHR response to the reverb file + + + + if (!this.source) { + this._getUserMedia({ + audio : true + }, this._onUserMedia.bind(this), this._onUserMediaError.bind(this)) + } + + this.element.html(this.constructor.INNER_HTML); + + 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'); + + this._bindEvents(); +}; + +const Pepperoni.prototype = { + + _createNodes() { + + } + + 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.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(); + this.clearButton.on('click', function () { + if (!this.recording) { + this.clear(); } - }, + }.bind(this)) - clear : function clear() { - if (this.recorder) { - this.progressBar.width(0); - this.audioElement.attr('src', ''); - this.audioElement[0].load(); - this.recorder.clear() + 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; - finalize : function finalize(callback) { - if (this.recorder) { - this.recorder.exportWAV(callback); + 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) + }); + }, - getBuffer : function getBuffer(callback) { - if (this.recorder) { - this.recorder.getBuffer(callback); + _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)); + }, - _bindEvents : function bindEvents() { - this.controlButton.on('click', function () { - if (this.recording) { - this.stop(); - } else { - this.record(); - } - }.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)); + }, - this.clearButton.on('click', function () { - if (!this.recording) { - this.clear(); - } - }.bind(this)) - }, + _addNode : function _addNode(node) { + var i; - _onRecording : function _onRecording(buffer) { - this._buffer = buffer; + i = this._activatedNodes.length; - this.audioElement.attr('src', URL.createObjectURL(buffer)); - this.audioElement[0].load(); - }, + this._activatedNodes.push(node); - _onUserMedia : function _onUserMedia(localMediaStream) { - this.source = this.context.createMediaStreamSource(localMediaStream); - this.recorder = new Recorder(this.source, { - workerPath : this.workerPath, - callback : this._onRecording.bind(this) - }); - }, + if (i === 0) { + this.source.disconnect(); + this.source.connect(node); + } else { + this._activatedNodes[i - 1].disconnect(); + this._activatedNodes[i - 1].connect(node); + } - _onUserMediaError : function _onUserMediaError(error) { - console.log("Something went wrong", error); - this.disable(); - }, + node.connect(this.recorder.node); + this.recorder.context = node.context; + this.recorder.node.connect(this.recorder.context.destination) - _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)); - }, + console.log("Adding: ", node); + }, - _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)); - }, + _removeNode : function _removeNode(node) { + var i; - // Normalize get user media - _getUserMedia : (navigator.getUserMedia || - navigator.webkitGetUserMedia || - navigator.mozGetUserMedia || - navigator.msGetUserMedia).bind(navigator) - } + 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) +} });