]>
git.r.bdr.sh - rbdr/r.bdr.sh/blob - jekyll/js/unlimited_pizza/pepperoni.js
8 requestLocation: '/reverb.ogg',
9 responseType: 'arraybuffer'
16 kTemplate: `<a class="record-button">Record.</a>
17 <div class="record-info">
18 <div class="record-progress">
19 <div class="record-progress-bar-container">
20 <div class="record-progress-bar"></div>
22 <a class="record-clear">Clear recording.</a>
24 <audio class="record-preview" controls></audio>
25 <div class="filter-switches">
26 <input class="filter-switch" name="bandpass-filter" type="checkbox" /> <label for="bandpass-filter">Band Pass</label>
27 <input class="filter-switch" name="hipass-filter" type="checkbox" /> <label for="hipass-filter">Hi Pass</label>
28 <input class="filter-switch" name="lopass-filter" type="checkbox" /> <label for="lopass-filter">Lo Pass</label>
29 <input class="filter-switch" name="reverb-filter" type="checkbox" /> <label for="reverb-filter">Reverb</label>
30 <input class="filter-switch" name="distort-filter" type="checkbox" /> <label for="distort-filter">Distort</label>
33 kPauseLabel : 'Pause.',
34 kRecordLabel : 'Record.',
37 const Pepperoni
= class Pepperoni
{
40 const context
= new AudioContext();
42 const nodes
= this._createNodes(context
);
44 this._activatedNodes
= [];
47 _createNodes(context
) {
49 const delayNode
= context
.createDelay(1.0);
51 const convolverNode
= context
.createConvolver();
52 this._initializeConvolverNode(convolverNode
);
54 const loPassFilterNode
= context
.createBiquadFilter();
55 loPassFilterNode
.type
= 'lowpass';
56 loPassFilterNode
.frequency
.value
= 1000;
57 loPassFilterNode
.gain
.value
= 25;
59 const hiPassFilterNode
= context
.createBiquadFilter();
60 hiPassFilterNode
.type
= 'highpass';
61 hiPassFilterNode
.frequency
.value
= 3000;
62 hiPassFilterNode
.gain
.value
= 25;
64 const bandPassFilterNode
= context
.createBiquadFilter();
65 bandPassFilterNode
.type
= 'bandpass';
66 bandPassFilterNode
.frequency
.value
= 2000;
67 bandPassFilterNode
.gain
.value
= 25;
69 const distortionNode
= context
.createWaveShaper();
70 distortionNode
.curve
= this._generateDistortion(400);
71 distortionNode
.oversample
= '4x';
74 // Generates a wave to be used with the distortion wave shaper node
76 _generateDistortion(amount
= 50) {
78 const sampleRate
= 44100;
79 const curve
= new Float32Array(sampleRate
);
80 const angle
= Math
.PI
/ 180;
82 for (let i
= 0; i
< sampleRate
; ++i
) {
83 const x
= i
* 2 / sampleRate
- 1;
84 curve
[i
] = ( 3 + amount
) * x
* 20 * angle
/ ( Math
.PI
+ amount
* Math
.abs(x
) );
90 // Initializes a convolver node by requesting the reverb sound,
91 // decoding it, and attaching it
93 _initializeConvolverNode(node
) {
95 const request
= new XMLHttpRequest();
96 request
.open(config
.convolverNode
.requestMethod
, config
.convolverNode
.requestLocation
);
97 request
.responseType
= config
.convolverNode
.responseType
;
98 request
.addEventListener('load', (event
) => {
100 node
.context
.decodeAudioData(event
.target
.response
, (buffer
) => {
102 node
.buffer
= buffer
;
108 // Handles the XHR response to the reverb file
115 }, this._onUserMedia
.bind(this), this._onUserMediaError
.bind(this))
118 this.element
.html(this.constructor.INNER_HTML
);
120 this.controlButton
= this.element
.find('.record-button');
121 this.clearButton
= this.element
.find('.record-clear');
122 this.audioElement
= this.element
.find('audio');
123 this.progressBarContainer
= this.element
.find('.record-progress-bar-container');
124 this.progressBar
= this.element
.find('.record-progress-bar');
125 this.switches
= this.element
.find('.filter-switch');
130 const Pepperoni
.prototype = {
142 _bandPassFilterNode : null,
143 _hiPassFilterNode : null,
144 _loPassFilterNode : null,
145 _convolverNode : null,
146 _distortionNode : null,
147 _activatedNodes : null,
148 workerPath : '/js/vendor/recorderjs/recorderWorker.js',
149 init : function init(config
) {
152 record : function record() {
153 if (this.recorder
&& !this.recording
) {
154 this._canRecord(function handleCanRecord(canRecord
) {
156 this.recording
= true;
157 this.controlButton
.addClass('recording')
158 this.controlButton
.html(this.constructor.PAUSE
);
159 this._interval
= setInterval(this._onRecordCheck
.bind(this), 100);
160 this.recorder
.record();
166 stop : function stop() {
167 if (this.recorder
&& this.recording
) {
168 this.recording
= false;
169 this.controlButton
.removeClass('recording')
170 this.controlButton
.html(this.constructor.RECORD
);
171 clearInterval(this._interval
);
172 this.recorder
.stop();
173 this.recorder
.exportWAV();
177 clear : function clear() {
179 this.progressBar
.width(0);
180 this.audioElement
.attr('src', '');
181 this.audioElement
[0].load();
182 this.recorder
.clear()
186 finalize : function finalize(callback
) {
188 this.recorder
.exportWAV(callback
);
192 getBuffer : function getBuffer(callback
) {
194 this.recorder
.getBuffer(callback
);
198 _bindEvents : function bindEvents() {
199 var pepperoni
= this;
201 this.controlButton
.on('click', function () {
202 if (this.recording
) {
209 this.clearButton
.on('click', function () {
210 if (!this.recording
) {
215 this.switches
.on('change', function (ev
) {
216 if (!pepperoni
.source
) {
217 this.checked
= false;
223 pepperoni
._addNode(pepperoni
._delayNode
);
225 pepperoni
._removeNode(pepperoni
._delayNode
);
229 case 'hipass-filter':
231 pepperoni
._addNode(pepperoni
._hiPassFilterNode
);
233 pepperoni
._removeNode(pepperoni
._hiPassFilterNode
);
237 case 'bandpass-filter':
239 pepperoni
._addNode(pepperoni
._bandPassFilterNode
);
241 pepperoni
._removeNode(pepperoni
._bandPassFilterNode
);
245 case 'lopass-filter':
247 pepperoni
._addNode(pepperoni
._loPassFilterNode
);
249 pepperoni
._removeNode(pepperoni
._loPassFilterNode
);
253 case 'reverb-filter':
255 pepperoni
._addNode(pepperoni
._convolverNode
);
257 pepperoni
._removeNode(pepperoni
._convolverNode
);
261 case 'distort-filter':
263 pepperoni
._addNode(pepperoni
._distortionNode
);
265 pepperoni
._removeNode(pepperoni
._distortionNode
);
273 _onRecording : function _onRecording(buffer
) {
274 this._buffer
= buffer
;
276 this.audioElement
.attr('src', URL
.createObjectURL(buffer
));
277 this.audioElement
[0].load();
280 _onUserMedia : function _onUserMedia(localMediaStream
) {
281 this.source
= this.context
.createMediaStreamSource(localMediaStream
);
282 this.recorder
= new Recorder(this.source
, {
283 workerPath : this.workerPath
,
284 callback : this._onRecording
.bind(this)
288 _onUserMediaError : function _onUserMediaError(error
) {
289 console
.log("Something went wrong", error
);
293 _onRecordCheck : function _onRecordCheck() {
294 this._canRecord(function (canRecord
, bufferSize
) {
295 var width
= bufferSize
* this.progressBarContainer
.width() / this.maxSize
;
296 this.progressBar
.width(width
);
303 _canRecord : function _canRecord(callback
) {
304 this.recorder
.getBuffer(function getBuffer(buffer
) {
305 var bufferSize
= buffer
[0].length
+ buffer
[1].length
;
306 callback
&& callback(bufferSize
<= this.maxSize
, bufferSize
);
310 _addNode : function _addNode(node
) {
313 i
= this._activatedNodes
.length
;
315 this._activatedNodes
.push(node
);
318 this.source
.disconnect();
319 this.source
.connect(node
);
321 this._activatedNodes
[i
- 1].disconnect();
322 this._activatedNodes
[i
- 1].connect(node
);
325 node
.connect(this.recorder
.node
);
326 this.recorder
.context
= node
.context
;
327 this.recorder
.node
.connect(this.recorder
.context
.destination
)
329 console
.log("Adding: ", node
);
332 _removeNode : function _removeNode(node
) {
335 i
= this._activatedNodes
.indexOf(node
);
339 if (i
=== 0 && i
+ 1 === this._activatedNodes
.length
) {
340 // It was the only one, connect source to recorder.
341 this.source
.disconnect();
342 this.source
.connect(this.recorder
.node
);
343 } else if (i
=== 0) {
344 // Normal 0 case, connect source to node. Recorder stays the same
345 this.source
.disconnect();
346 this.source
.connect(this._activatedNodes
[i
+1]);
347 } else if (i
+ 1 === this._activatedNodes
.length
) {
348 // It's not the 0 case, but we need to reconnect to recorder.
349 this._activatedNodes
[i
- 1].disconnect();
350 this._activatedNodes
[i
- 1].connect(this.recorder
.node
);
352 // Normal case, connect previous node to node
353 this._activatedNodes
[i
- 1].disconnect();
354 this._activatedNodes
[i
- 1].connect(this._activatedNodes
[i
+ 1]);
357 this._activatedNodes
.splice(i
, 1);
359 console
.log("Removing: ", node
);
362 // Normalize get user media
363 _getUserMedia : (navigator
.getUserMedia
||
364 navigator
.webkitGetUserMedia
||
365 navigator
.mozGetUserMedia
||
366 navigator
.msGetUserMedia
).bind(navigator
)