source: gs3-extensions/web-audio/trunk/js-dsp/dsp.js@ 28388

Last change on this file since 28388 was 28388, checked in by davidb, 11 years ago

Set of JS, CSS, PNG etc web resources to support a mixture of audio player/document display capabilities

File size: 65.2 KB
Line 
1/*
2 * DSP.js - a comprehensive digital signal processing library for javascript
3 *
4 * Created by Corban Brook <[email protected]> on 2010-01-01.
5 * Copyright 2010 Corban Brook. All rights reserved.
6 *
7 */
8
9////////////////////////////////////////////////////////////////////////////////
10// CONSTANTS //
11////////////////////////////////////////////////////////////////////////////////
12
13/**
14 * DSP is an object which contains general purpose utility functions and constants
15 */
16var DSP = {
17 // Channels
18 LEFT: 0,
19 RIGHT: 1,
20 MIX: 2,
21
22 // Waveforms
23 SINE: 1,
24 TRIANGLE: 2,
25 SAW: 3,
26 SQUARE: 4,
27
28 // Filters
29 LOWPASS: 0,
30 HIGHPASS: 1,
31 BANDPASS: 2,
32 NOTCH: 3,
33
34 // Window functions
35 BARTLETT: 1,
36 BARTLETTHANN: 2,
37 BLACKMAN: 3,
38 COSINE: 4,
39 GAUSS: 5,
40 HAMMING: 6,
41 HANN: 7,
42 LANCZOS: 8,
43 RECTANGULAR: 9,
44 TRIANGULAR: 10,
45
46 // Loop modes
47 OFF: 0,
48 FW: 1,
49 BW: 2,
50 FWBW: 3,
51
52 // Math
53 TWO_PI: 2*Math.PI
54};
55
56// Setup arrays for platforms which do not support byte arrays
57function setupTypedArray(name, fallback) {
58 // check if TypedArray exists
59 // typeof on Minefield and Chrome return function, typeof on Webkit returns object.
60 if (typeof this[name] !== "function" && typeof this[name] !== "object") {
61 // nope.. check if WebGLArray exists
62 if (typeof this[fallback] === "function" && typeof this[fallback] !== "object") {
63 this[name] = this[fallback];
64 } else {
65 // nope.. set as Native JS array
66 this[name] = function(obj) {
67 if (obj instanceof Array) {
68 return obj;
69 } else if (typeof obj === "number") {
70 return new Array(obj);
71 }
72 };
73 }
74 }
75}
76
77setupTypedArray("Float32Array", "WebGLFloatArray");
78setupTypedArray("Int32Array", "WebGLIntArray");
79setupTypedArray("Uint16Array", "WebGLUnsignedShortArray");
80setupTypedArray("Uint8Array", "WebGLUnsignedByteArray");
81
82
83////////////////////////////////////////////////////////////////////////////////
84// DSP UTILITY FUNCTIONS //
85////////////////////////////////////////////////////////////////////////////////
86
87/**
88 * Inverts the phase of a signal
89 *
90 * @param {Array} buffer A sample buffer
91 *
92 * @returns The inverted sample buffer
93 */
94DSP.invert = function(buffer) {
95 for (var i = 0, len = buffer.length; i < len; i++) {
96 buffer[i] *= -1;
97 }
98
99 return buffer;
100};
101
102/**
103 * Converts split-stereo (dual mono) sample buffers into a stereo interleaved sample buffer
104 *
105 * @param {Array} left A sample buffer
106 * @param {Array} right A sample buffer
107 *
108 * @returns The stereo interleaved buffer
109 */
110DSP.interleave = function(left, right) {
111 if (left.length !== right.length) {
112 throw "Can not interleave. Channel lengths differ.";
113 }
114
115 var stereoInterleaved = new Float32Array(left.length * 2);
116
117 for (var i = 0, len = left.length; i < len; i++) {
118 stereoInterleaved[2*i] = left[i];
119 stereoInterleaved[2*i+1] = right[i];
120 }
121
122 return stereoInterleaved;
123};
124
125/**
126 * Converts a stereo-interleaved sample buffer into split-stereo (dual mono) sample buffers
127 *
128 * @param {Array} buffer A stereo-interleaved sample buffer
129 *
130 * @returns an Array containing left and right channels
131 */
132DSP.deinterleave = (function() {
133 var left, right, mix, deinterleaveChannel = [];
134
135 deinterleaveChannel[DSP.MIX] = function(buffer) {
136 for (var i = 0, len = buffer.length/2; i < len; i++) {
137 mix[i] = (buffer[2*i] + buffer[2*i+1]) / 2;
138 }
139 return mix;
140 };
141
142 deinterleaveChannel[DSP.LEFT] = function(buffer) {
143 for (var i = 0, len = buffer.length/2; i < len; i++) {
144 left[i] = buffer[2*i];
145 }
146 return left;
147 };
148
149 deinterleaveChannel[DSP.RIGHT] = function(buffer) {
150 for (var i = 0, len = buffer.length/2; i < len; i++) {
151 right[i] = buffer[2*i+1];
152 }
153 return right;
154 };
155
156 return function(channel, buffer) {
157 left = left || new Float32Array(buffer.length/2);
158 right = right || new Float32Array(buffer.length/2);
159 mix = mix || new Float32Array(buffer.length/2);
160
161 if (buffer.length/2 !== left.length) {
162 left = new Float32Array(buffer.length/2);
163 right = new Float32Array(buffer.length/2);
164 mix = new Float32Array(buffer.length/2);
165 }
166
167 return deinterleaveChannel[channel](buffer);
168 };
169}());
170
171/**
172 * Separates a channel from a stereo-interleaved sample buffer
173 *
174 * @param {Array} buffer A stereo-interleaved sample buffer
175 * @param {Number} channel A channel constant (LEFT, RIGHT, MIX)
176 *
177 * @returns an Array containing a signal mono sample buffer
178 */
179DSP.getChannel = DSP.deinterleave;
180
181/**
182 * Helper method (for Reverb) to mix two (interleaved) samplebuffers. It's possible
183 * to negate the second buffer while mixing and to perform a volume correction
184 * on the final signal.
185 *
186 * @param {Array} sampleBuffer1 Array containing Float values or a Float32Array
187 * @param {Array} sampleBuffer2 Array containing Float values or a Float32Array
188 * @param {Boolean} negate When true inverts/flips the audio signal
189 * @param {Number} volumeCorrection When you add multiple sample buffers, use this to tame your signal ;)
190 *
191 * @returns A new Float32Array interleaved buffer.
192 */
193DSP.mixSampleBuffers = function(sampleBuffer1, sampleBuffer2, negate, volumeCorrection){
194 var outputSamples = new Float32Array(sampleBuffer1);
195
196 for(var i = 0; i<sampleBuffer1.length; i++){
197 outputSamples[i] += (negate ? -sampleBuffer2[i] : sampleBuffer2[i]) / volumeCorrection;
198 }
199
200 return outputSamples;
201};
202
203// Biquad filter types
204DSP.LPF = 0; // H(s) = 1 / (s^2 + s/Q + 1)
205DSP.HPF = 1; // H(s) = s^2 / (s^2 + s/Q + 1)
206DSP.BPF_CONSTANT_SKIRT = 2; // H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q)
207DSP.BPF_CONSTANT_PEAK = 3; // H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain)
208DSP.NOTCH = 4; // H(s) = (s^2 + 1) / (s^2 + s/Q + 1)
209DSP.APF = 5; // H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1)
210DSP.PEAKING_EQ = 6; // H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
211DSP.LOW_SHELF = 7; // H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1)
212DSP.HIGH_SHELF = 8; // H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A)
213
214// Biquad filter parameter types
215DSP.Q = 1;
216DSP.BW = 2; // SHARED with BACKWARDS LOOP MODE
217DSP.S = 3;
218
219// Find RMS of signal
220DSP.RMS = function(buffer) {
221 var total = 0;
222
223 for (var i = 0, n = buffer.length; i < n; i++) {
224 total += buffer[i] * buffer[i];
225 }
226
227 return Math.sqrt(total / n);
228};
229
230// Find Peak of signal
231DSP.Peak = function(buffer) {
232 var peak = 0;
233
234 for (var i = 0, n = buffer.length; i < n; i++) {
235 peak = (Math.abs(buffer[i]) > peak) ? Math.abs(buffer[i]) : peak;
236 }
237
238 return peak;
239};
240
241// Fourier Transform Module used by DFT, FFT, RFFT
242function FourierTransform(bufferSize, sampleRate) {
243 this.bufferSize = bufferSize;
244 this.sampleRate = sampleRate;
245 this.bandwidth = 2 / bufferSize * sampleRate / 2;
246
247 this.spectrum = new Float32Array(bufferSize/2);
248 this.real = new Float32Array(bufferSize);
249 this.imag = new Float32Array(bufferSize);
250
251 this.peakBand = 0;
252 this.peak = 0;
253
254 /**
255 * Calculates the *middle* frequency of an FFT band.
256 *
257 * @param {Number} index The index of the FFT band.
258 *
259 * @returns The middle frequency in Hz.
260 */
261 this.getBandFrequency = function(index) {
262 return this.bandwidth * index + this.bandwidth / 2;
263 };
264
265 this.calculateSpectrum = function() {
266 var spectrum = this.spectrum,
267 real = this.real,
268 imag = this.imag,
269 bSi = 2 / this.bufferSize,
270 sqrt = Math.sqrt,
271 rval,
272 ival,
273 mag;
274
275 for (var i = 0, N = bufferSize/2; i < N; i++) {
276 rval = real[i];
277 ival = imag[i];
278 mag = bSi * sqrt(rval * rval + ival * ival);
279
280 if (mag > this.peak) {
281 this.peakBand = i;
282 this.peak = mag;
283 }
284
285 spectrum[i] = mag;
286 }
287 };
288}
289
290/**
291 * DFT is a class for calculating the Discrete Fourier Transform of a signal.
292 *
293 * @param {Number} bufferSize The size of the sample buffer to be computed
294 * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
295 *
296 * @constructor
297 */
298function DFT(bufferSize, sampleRate) {
299 FourierTransform.call(this, bufferSize, sampleRate);
300
301 var N = bufferSize/2 * bufferSize;
302 var TWO_PI = 2 * Math.PI;
303
304 this.sinTable = new Float32Array(N);
305 this.cosTable = new Float32Array(N);
306
307 for (var i = 0; i < N; i++) {
308 this.sinTable[i] = Math.sin(i * TWO_PI / bufferSize);
309 this.cosTable[i] = Math.cos(i * TWO_PI / bufferSize);
310 }
311}
312
313/**
314 * Performs a forward transform on the sample buffer.
315 * Converts a time domain signal to frequency domain spectra.
316 *
317 * @param {Array} buffer The sample buffer
318 *
319 * @returns The frequency spectrum array
320 */
321DFT.prototype.forward = function(buffer) {
322 var real = this.real,
323 imag = this.imag,
324 rval,
325 ival;
326
327 for (var k = 0; k < this.bufferSize/2; k++) {
328 rval = 0.0;
329 ival = 0.0;
330
331 for (var n = 0; n < buffer.length; n++) {
332 rval += this.cosTable[k*n] * buffer[n];
333 ival += this.sinTable[k*n] * buffer[n];
334 }
335
336 real[k] = rval;
337 imag[k] = ival;
338 }
339
340 return this.calculateSpectrum();
341};
342
343
344/**
345 * FFT is a class for calculating the Discrete Fourier Transform of a signal
346 * with the Fast Fourier Transform algorithm.
347 *
348 * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
349 * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
350 *
351 * @constructor
352 */
353function FFT(bufferSize, sampleRate) {
354 FourierTransform.call(this, bufferSize, sampleRate);
355
356 this.reverseTable = new Uint32Array(bufferSize);
357
358 var limit = 1;
359 var bit = bufferSize >> 1;
360
361 var i;
362
363 while (limit < bufferSize) {
364 for (i = 0; i < limit; i++) {
365 this.reverseTable[i + limit] = this.reverseTable[i] + bit;
366 }
367
368 limit = limit << 1;
369 bit = bit >> 1;
370 }
371
372 this.sinTable = new Float32Array(bufferSize);
373 this.cosTable = new Float32Array(bufferSize);
374
375 for (i = 0; i < bufferSize; i++) {
376 this.sinTable[i] = Math.sin(-Math.PI/i);
377 this.cosTable[i] = Math.cos(-Math.PI/i);
378 }
379}
380
381/**
382 * Performs a forward transform on the sample buffer.
383 * Converts a time domain signal to frequency domain spectra.
384 *
385 * @param {Array} buffer The sample buffer. Buffer Length must be power of 2
386 *
387 * @returns The frequency spectrum array
388 */
389FFT.prototype.forward = function(buffer) {
390 // Locally scope variables for speed up
391 var bufferSize = this.bufferSize,
392 cosTable = this.cosTable,
393 sinTable = this.sinTable,
394 reverseTable = this.reverseTable,
395 real = this.real,
396 imag = this.imag,
397 spectrum = this.spectrum;
398
399 var k = Math.floor(Math.log(bufferSize) / Math.LN2);
400
401 if (Math.pow(2, k) !== bufferSize) { throw "Invalid buffer size, must be a power of 2."; }
402 if (bufferSize !== buffer.length) { throw "Supplied buffer is not the same size as defined FFT. FFT Size: " + bufferSize + " Buffer Size: " + buffer.length; }
403
404 var halfSize = 1,
405 phaseShiftStepReal,
406 phaseShiftStepImag,
407 currentPhaseShiftReal,
408 currentPhaseShiftImag,
409 off,
410 tr,
411 ti,
412 tmpReal,
413 i;
414
415 for (i = 0; i < bufferSize; i++) {
416 real[i] = buffer[reverseTable[i]];
417 imag[i] = 0;
418 }
419
420 while (halfSize < bufferSize) {
421 //phaseShiftStepReal = Math.cos(-Math.PI/halfSize);
422 //phaseShiftStepImag = Math.sin(-Math.PI/halfSize);
423 phaseShiftStepReal = cosTable[halfSize];
424 phaseShiftStepImag = sinTable[halfSize];
425
426 currentPhaseShiftReal = 1;
427 currentPhaseShiftImag = 0;
428
429 for (var fftStep = 0; fftStep < halfSize; fftStep++) {
430 i = fftStep;
431
432 while (i < bufferSize) {
433 off = i + halfSize;
434 tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
435 ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);
436
437 real[off] = real[i] - tr;
438 imag[off] = imag[i] - ti;
439 real[i] += tr;
440 imag[i] += ti;
441
442 i += halfSize << 1;
443 }
444
445 tmpReal = currentPhaseShiftReal;
446 currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
447 currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
448 }
449
450 halfSize = halfSize << 1;
451 }
452
453 return this.calculateSpectrum();
454};
455
456FFT.prototype.inverse = function(real, imag) {
457 // Locally scope variables for speed up
458 var bufferSize = this.bufferSize,
459 cosTable = this.cosTable,
460 sinTable = this.sinTable,
461 reverseTable = this.reverseTable,
462 spectrum = this.spectrum;
463
464 real = real || this.real;
465 imag = imag || this.imag;
466
467 var halfSize = 1,
468 phaseShiftStepReal,
469 phaseShiftStepImag,
470 currentPhaseShiftReal,
471 currentPhaseShiftImag,
472 off,
473 tr,
474 ti,
475 tmpReal,
476 i;
477
478 for (i = 0; i < bufferSize; i++) {
479 imag[i] *= -1;
480 }
481
482 var revReal = new Float32Array(bufferSize);
483 var revImag = new Float32Array(bufferSize);
484
485 for (i = 0; i < real.length; i++) {
486 revReal[i] = real[reverseTable[i]];
487 revImag[i] = imag[reverseTable[i]];
488 }
489
490 real = revReal;
491 imag = revImag;
492
493 while (halfSize < bufferSize) {
494 phaseShiftStepReal = cosTable[halfSize];
495 phaseShiftStepImag = sinTable[halfSize];
496 currentPhaseShiftReal = 1;
497 currentPhaseShiftImag = 0;
498
499 for (var fftStep = 0; fftStep < halfSize; fftStep++) {
500 i = fftStep;
501
502 while (i < bufferSize) {
503 off = i + halfSize;
504 tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
505 ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);
506
507 real[off] = real[i] - tr;
508 imag[off] = imag[i] - ti;
509 real[i] += tr;
510 imag[i] += ti;
511
512 i += halfSize << 1;
513 }
514
515 tmpReal = currentPhaseShiftReal;
516 currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
517 currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
518 }
519
520 halfSize = halfSize << 1;
521 }
522
523 var buffer = new Float32Array(bufferSize); // this should be reused instead
524 for (i = 0; i < bufferSize; i++) {
525 buffer[i] = real[i] / bufferSize;
526 }
527
528 return buffer;
529};
530
531/**
532 * RFFT is a class for calculating the Discrete Fourier Transform of a signal
533 * with the Fast Fourier Transform algorithm.
534 *
535 * This method currently only contains a forward transform but is highly optimized.
536 *
537 * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
538 * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
539 *
540 * @constructor
541 */
542
543// lookup tables don't really gain us any speed, but they do increase
544// cache footprint, so don't use them in here
545
546// also we don't use sepearate arrays for real/imaginary parts
547
548// this one a little more than twice as fast as the one in FFT
549// however I only did the forward transform
550
551// the rest of this was translated from C, see http://www.jjj.de/fxt/
552// this is the real split radix FFT
553
554function RFFT(bufferSize, sampleRate) {
555 FourierTransform.call(this, bufferSize, sampleRate);
556
557 this.trans = new Float32Array(bufferSize);
558
559 this.reverseTable = new Uint32Array(bufferSize);
560
561 // don't use a lookup table to do the permute, use this instead
562 this.reverseBinPermute = function (dest, source) {
563 var bufferSize = this.bufferSize,
564 halfSize = bufferSize >>> 1,
565 nm1 = bufferSize - 1,
566 i = 1, r = 0, h;
567
568 dest[0] = source[0];
569
570 do {
571 r += halfSize;
572 dest[i] = source[r];
573 dest[r] = source[i];
574
575 i++;
576
577 h = halfSize << 1;
578 while (h = h >> 1, !((r ^= h) & h));
579
580 if (r >= i) {
581 dest[i] = source[r];
582 dest[r] = source[i];
583
584 dest[nm1-i] = source[nm1-r];
585 dest[nm1-r] = source[nm1-i];
586 }
587 i++;
588 } while (i < halfSize);
589 dest[nm1] = source[nm1];
590 };
591
592 this.generateReverseTable = function () {
593 var bufferSize = this.bufferSize,
594 halfSize = bufferSize >>> 1,
595 nm1 = bufferSize - 1,
596 i = 1, r = 0, h;
597
598 this.reverseTable[0] = 0;
599
600 do {
601 r += halfSize;
602
603 this.reverseTable[i] = r;
604 this.reverseTable[r] = i;
605
606 i++;
607
608 h = halfSize << 1;
609 while (h = h >> 1, !((r ^= h) & h));
610
611 if (r >= i) {
612 this.reverseTable[i] = r;
613 this.reverseTable[r] = i;
614
615 this.reverseTable[nm1-i] = nm1-r;
616 this.reverseTable[nm1-r] = nm1-i;
617 }
618 i++;
619 } while (i < halfSize);
620
621 this.reverseTable[nm1] = nm1;
622 };
623
624 this.generateReverseTable();
625}
626
627
628// Ordering of output:
629//
630// trans[0] = re[0] (==zero frequency, purely real)
631// trans[1] = re[1]
632// ...
633// trans[n/2-1] = re[n/2-1]
634// trans[n/2] = re[n/2] (==nyquist frequency, purely real)
635//
636// trans[n/2+1] = im[n/2-1]
637// trans[n/2+2] = im[n/2-2]
638// ...
639// trans[n-1] = im[1]
640
641RFFT.prototype.forward = function(buffer) {
642 var n = this.bufferSize,
643 spectrum = this.spectrum,
644 x = this.trans,
645 TWO_PI = 2*Math.PI,
646 sqrt = Math.sqrt,
647 i = n >>> 1,
648 bSi = 2 / n,
649 n2, n4, n8, nn,
650 t1, t2, t3, t4,
651 i1, i2, i3, i4, i5, i6, i7, i8,
652 st1, cc1, ss1, cc3, ss3,
653 e,
654 a,
655 rval, ival, mag;
656
657 this.reverseBinPermute(x, buffer);
658
659 /*
660 var reverseTable = this.reverseTable;
661
662 for (var k = 0, len = reverseTable.length; k < len; k++) {
663 x[k] = buffer[reverseTable[k]];
664 }
665 */
666
667 for (var ix = 0, id = 4; ix < n; id *= 4) {
668 for (var i0 = ix; i0 < n; i0 += id) {
669 //sumdiff(x[i0], x[i0+1]); // {a, b} <--| {a+b, a-b}
670 st1 = x[i0] - x[i0+1];
671 x[i0] += x[i0+1];
672 x[i0+1] = st1;
673 }
674 ix = 2*(id-1);
675 }
676
677 n2 = 2;
678 nn = n >>> 1;
679
680 while((nn = nn >>> 1)) {
681 ix = 0;
682 n2 = n2 << 1;
683 id = n2 << 1;
684 n4 = n2 >>> 2;
685 n8 = n2 >>> 3;
686 do {
687 if(n4 !== 1) {
688 for(i0 = ix; i0 < n; i0 += id) {
689 i1 = i0;
690 i2 = i1 + n4;
691 i3 = i2 + n4;
692 i4 = i3 + n4;
693
694 //diffsum3_r(x[i3], x[i4], t1); // {a, b, s} <--| {a, b-a, a+b}
695 t1 = x[i3] + x[i4];
696 x[i4] -= x[i3];
697 //sumdiff3(x[i1], t1, x[i3]); // {a, b, d} <--| {a+b, b, a-b}
698 x[i3] = x[i1] - t1;
699 x[i1] += t1;
700
701 i1 += n8;
702 i2 += n8;
703 i3 += n8;
704 i4 += n8;
705
706 //sumdiff(x[i3], x[i4], t1, t2); // {s, d} <--| {a+b, a-b}
707 t1 = x[i3] + x[i4];
708 t2 = x[i3] - x[i4];
709
710 t1 = -t1 * Math.SQRT1_2;
711 t2 *= Math.SQRT1_2;
712
713 // sumdiff(t1, x[i2], x[i4], x[i3]); // {s, d} <--| {a+b, a-b}
714 st1 = x[i2];
715 x[i4] = t1 + st1;
716 x[i3] = t1 - st1;
717
718 //sumdiff3(x[i1], t2, x[i2]); // {a, b, d} <--| {a+b, b, a-b}
719 x[i2] = x[i1] - t2;
720 x[i1] += t2;
721 }
722 } else {
723 for(i0 = ix; i0 < n; i0 += id) {
724 i1 = i0;
725 i2 = i1 + n4;
726 i3 = i2 + n4;
727 i4 = i3 + n4;
728
729 //diffsum3_r(x[i3], x[i4], t1); // {a, b, s} <--| {a, b-a, a+b}
730 t1 = x[i3] + x[i4];
731 x[i4] -= x[i3];
732
733 //sumdiff3(x[i1], t1, x[i3]); // {a, b, d} <--| {a+b, b, a-b}
734 x[i3] = x[i1] - t1;
735 x[i1] += t1;
736 }
737 }
738
739 ix = (id << 1) - n2;
740 id = id << 2;
741 } while (ix < n);
742
743 e = TWO_PI / n2;
744
745 for (var j = 1; j < n8; j++) {
746 a = j * e;
747 ss1 = Math.sin(a);
748 cc1 = Math.cos(a);
749
750 //ss3 = sin(3*a); cc3 = cos(3*a);
751 cc3 = 4*cc1*(cc1*cc1-0.75);
752 ss3 = 4*ss1*(0.75-ss1*ss1);
753
754 ix = 0; id = n2 << 1;
755 do {
756 for (i0 = ix; i0 < n; i0 += id) {
757 i1 = i0 + j;
758 i2 = i1 + n4;
759 i3 = i2 + n4;
760 i4 = i3 + n4;
761
762 i5 = i0 + n4 - j;
763 i6 = i5 + n4;
764 i7 = i6 + n4;
765 i8 = i7 + n4;
766
767 //cmult(c, s, x, y, &u, &v)
768 //cmult(cc1, ss1, x[i7], x[i3], t2, t1); // {u,v} <--| {x*c-y*s, x*s+y*c}
769 t2 = x[i7]*cc1 - x[i3]*ss1;
770 t1 = x[i7]*ss1 + x[i3]*cc1;
771
772 //cmult(cc3, ss3, x[i8], x[i4], t4, t3);
773 t4 = x[i8]*cc3 - x[i4]*ss3;
774 t3 = x[i8]*ss3 + x[i4]*cc3;
775
776 //sumdiff(t2, t4); // {a, b} <--| {a+b, a-b}
777 st1 = t2 - t4;
778 t2 += t4;
779 t4 = st1;
780
781 //sumdiff(t2, x[i6], x[i8], x[i3]); // {s, d} <--| {a+b, a-b}
782 //st1 = x[i6]; x[i8] = t2 + st1; x[i3] = t2 - st1;
783 x[i8] = t2 + x[i6];
784 x[i3] = t2 - x[i6];
785
786 //sumdiff_r(t1, t3); // {a, b} <--| {a+b, b-a}
787 st1 = t3 - t1;
788 t1 += t3;
789 t3 = st1;
790
791 //sumdiff(t3, x[i2], x[i4], x[i7]); // {s, d} <--| {a+b, a-b}
792 //st1 = x[i2]; x[i4] = t3 + st1; x[i7] = t3 - st1;
793 x[i4] = t3 + x[i2];
794 x[i7] = t3 - x[i2];
795
796 //sumdiff3(x[i1], t1, x[i6]); // {a, b, d} <--| {a+b, b, a-b}
797 x[i6] = x[i1] - t1;
798 x[i1] += t1;
799
800 //diffsum3_r(t4, x[i5], x[i2]); // {a, b, s} <--| {a, b-a, a+b}
801 x[i2] = t4 + x[i5];
802 x[i5] -= t4;
803 }
804
805 ix = (id << 1) - n2;
806 id = id << 2;
807
808 } while (ix < n);
809 }
810 }
811
812 while (--i) {
813 rval = x[i];
814 ival = x[n-i-1];
815 mag = bSi * sqrt(rval * rval + ival * ival);
816
817 if (mag > this.peak) {
818 this.peakBand = i;
819 this.peak = mag;
820 }
821
822 spectrum[i] = mag;
823 }
824
825 spectrum[0] = bSi * x[0];
826
827 return spectrum;
828};
829
830function Sampler(file, bufferSize, sampleRate, playStart, playEnd, loopStart, loopEnd, loopMode) {
831 this.file = file;
832 this.bufferSize = bufferSize;
833 this.sampleRate = sampleRate;
834 this.playStart = playStart || 0; // 0%
835 this.playEnd = playEnd || 1; // 100%
836 this.loopStart = loopStart || 0;
837 this.loopEnd = loopEnd || 1;
838 this.loopMode = loopMode || DSP.OFF;
839 this.loaded = false;
840 this.samples = [];
841 this.signal = new Float32Array(bufferSize);
842 this.frameCount = 0;
843 this.envelope = null;
844 this.amplitude = 1;
845 this.rootFrequency = 110; // A2 110
846 this.frequency = 550;
847 this.step = this.frequency / this.rootFrequency;
848 this.duration = 0;
849 this.samplesProcessed = 0;
850 this.playhead = 0;
851
852 var audio = /* new Audio();*/ document.createElement("AUDIO");
853 var self = this;
854
855 this.loadSamples = function(event) {
856 var buffer = DSP.getChannel(DSP.MIX, event.frameBuffer);
857 for ( var i = 0; i < buffer.length; i++) {
858 self.samples.push(buffer[i]);
859 }
860 };
861
862 this.loadComplete = function() {
863 // convert flexible js array into a fast typed array
864 self.samples = new Float32Array(self.samples);
865 self.loaded = true;
866 };
867
868 this.loadMetaData = function() {
869 self.duration = audio.duration;
870 };
871
872 audio.addEventListener("MozAudioAvailable", this.loadSamples, false);
873 audio.addEventListener("loadedmetadata", this.loadMetaData, false);
874 audio.addEventListener("ended", this.loadComplete, false);
875 audio.muted = true;
876 audio.src = file;
877 audio.play();
878}
879
880Sampler.prototype.applyEnvelope = function() {
881 this.envelope.process(this.signal);
882 return this.signal;
883};
884
885Sampler.prototype.generate = function() {
886 var frameOffset = this.frameCount * this.bufferSize;
887
888 var loopWidth = this.playEnd * this.samples.length - this.playStart * this.samples.length;
889 var playStartSamples = this.playStart * this.samples.length; // ie 0.5 -> 50% of the length
890 var playEndSamples = this.playEnd * this.samples.length; // ie 0.5 -> 50% of the length
891 var offset;
892
893 for ( var i = 0; i < this.bufferSize; i++ ) {
894 switch (this.loopMode) {
895 case DSP.OFF:
896 this.playhead = Math.round(this.samplesProcessed * this.step + playStartSamples);
897 if (this.playhead < (this.playEnd * this.samples.length) ) {
898 this.signal[i] = this.samples[this.playhead] * this.amplitude;
899 } else {
900 this.signal[i] = 0;
901 }
902 break;
903
904 case DSP.FW:
905 this.playhead = Math.round((this.samplesProcessed * this.step) % loopWidth + playStartSamples);
906 if (this.playhead < (this.playEnd * this.samples.length) ) {
907 this.signal[i] = this.samples[this.playhead] * this.amplitude;
908 }
909 break;
910
911 case DSP.BW:
912 this.playhead = playEndSamples - Math.round((this.samplesProcessed * this.step) % loopWidth);
913 if (this.playhead < (this.playEnd * this.samples.length) ) {
914 this.signal[i] = this.samples[this.playhead] * this.amplitude;
915 }
916 break;
917
918 case DSP.FWBW:
919 if ( Math.floor(this.samplesProcessed * this.step / loopWidth) % 2 === 0 ) {
920 this.playhead = Math.round((this.samplesProcessed * this.step) % loopWidth + playStartSamples);
921 } else {
922 this.playhead = playEndSamples - Math.round((this.samplesProcessed * this.step) % loopWidth);
923 }
924 if (this.playhead < (this.playEnd * this.samples.length) ) {
925 this.signal[i] = this.samples[this.playhead] * this.amplitude;
926 }
927 break;
928 }
929 this.samplesProcessed++;
930 }
931
932 this.frameCount++;
933
934 return this.signal;
935};
936
937Sampler.prototype.setFreq = function(frequency) {
938 var totalProcessed = this.samplesProcessed * this.step;
939 this.frequency = frequency;
940 this.step = this.frequency / this.rootFrequency;
941 this.samplesProcessed = Math.round(totalProcessed/this.step);
942};
943
944Sampler.prototype.reset = function() {
945 this.samplesProcessed = 0;
946 this.playhead = 0;
947};
948
949/**
950 * Oscillator class for generating and modifying signals
951 *
952 * @param {Number} type A waveform constant (eg. DSP.SINE)
953 * @param {Number} frequency Initial frequency of the signal
954 * @param {Number} amplitude Initial amplitude of the signal
955 * @param {Number} bufferSize Size of the sample buffer to generate
956 * @param {Number} sampleRate The sample rate of the signal
957 *
958 * @contructor
959 */
960function Oscillator(type, frequency, amplitude, bufferSize, sampleRate) {
961 this.frequency = frequency;
962 this.amplitude = amplitude;
963 this.bufferSize = bufferSize;
964 this.sampleRate = sampleRate;
965 //this.pulseWidth = pulseWidth;
966 this.frameCount = 0;
967
968 this.waveTableLength = 2048;
969
970 this.cyclesPerSample = frequency / sampleRate;
971
972 this.signal = new Float32Array(bufferSize);
973 this.envelope = null;
974
975 switch(parseInt(type, 10)) {
976 case DSP.TRIANGLE:
977 this.func = Oscillator.Triangle;
978 break;
979
980 case DSP.SAW:
981 this.func = Oscillator.Saw;
982 break;
983
984 case DSP.SQUARE:
985 this.func = Oscillator.Square;
986 break;
987
988 default:
989 case DSP.SINE:
990 this.func = Oscillator.Sine;
991 break;
992 }
993
994 this.generateWaveTable = function() {
995 Oscillator.waveTable[this.func] = new Float32Array(2048);
996 var waveTableTime = this.waveTableLength / this.sampleRate;
997 var waveTableHz = 1 / waveTableTime;
998
999 for (var i = 0; i < this.waveTableLength; i++) {
1000 Oscillator.waveTable[this.func][i] = this.func(i * waveTableHz/this.sampleRate);
1001 }
1002 };
1003
1004 if ( typeof Oscillator.waveTable === 'undefined' ) {
1005 Oscillator.waveTable = {};
1006 }
1007
1008 if ( typeof Oscillator.waveTable[this.func] === 'undefined' ) {
1009 this.generateWaveTable();
1010 }
1011
1012 this.waveTable = Oscillator.waveTable[this.func];
1013}
1014
1015/**
1016 * Set the amplitude of the signal
1017 *
1018 * @param {Number} amplitude The amplitude of the signal (between 0 and 1)
1019 */
1020Oscillator.prototype.setAmp = function(amplitude) {
1021 if (amplitude >= 0 && amplitude <= 1) {
1022 this.amplitude = amplitude;
1023 } else {
1024 throw "Amplitude out of range (0..1).";
1025 }
1026};
1027
1028/**
1029 * Set the frequency of the signal
1030 *
1031 * @param {Number} frequency The frequency of the signal
1032 */
1033Oscillator.prototype.setFreq = function(frequency) {
1034 this.frequency = frequency;
1035 this.cyclesPerSample = frequency / this.sampleRate;
1036};
1037
1038// Add an oscillator
1039Oscillator.prototype.add = function(oscillator) {
1040 for ( var i = 0; i < this.bufferSize; i++ ) {
1041 //this.signal[i] += oscillator.valueAt(i);
1042 this.signal[i] += oscillator.signal[i];
1043 }
1044
1045 return this.signal;
1046};
1047
1048// Add a signal to the current generated osc signal
1049Oscillator.prototype.addSignal = function(signal) {
1050 for ( var i = 0; i < signal.length; i++ ) {
1051 if ( i >= this.bufferSize ) {
1052 break;
1053 }
1054 this.signal[i] += signal[i];
1055
1056 /*
1057 // Constrain amplitude
1058 if ( this.signal[i] > 1 ) {
1059 this.signal[i] = 1;
1060 } else if ( this.signal[i] < -1 ) {
1061 this.signal[i] = -1;
1062 }
1063 */
1064 }
1065 return this.signal;
1066};
1067
1068// Add an envelope to the oscillator
1069Oscillator.prototype.addEnvelope = function(envelope) {
1070 this.envelope = envelope;
1071};
1072
1073Oscillator.prototype.applyEnvelope = function() {
1074 this.envelope.process(this.signal);
1075};
1076
1077Oscillator.prototype.valueAt = function(offset) {
1078 return this.waveTable[offset % this.waveTableLength];
1079};
1080
1081Oscillator.prototype.generate = function() {
1082 var frameOffset = this.frameCount * this.bufferSize;
1083 var step = this.waveTableLength * this.frequency / this.sampleRate;
1084 var offset;
1085
1086 for ( var i = 0; i < this.bufferSize; i++ ) {
1087 //var step = (frameOffset + i) * this.cyclesPerSample % 1;
1088 //this.signal[i] = this.func(step) * this.amplitude;
1089 //this.signal[i] = this.valueAt(Math.round((frameOffset + i) * step)) * this.amplitude;
1090 offset = Math.round((frameOffset + i) * step);
1091 this.signal[i] = this.waveTable[offset % this.waveTableLength] * this.amplitude;
1092 }
1093
1094 this.frameCount++;
1095
1096 return this.signal;
1097};
1098
1099Oscillator.Sine = function(step) {
1100 return Math.sin(DSP.TWO_PI * step);
1101};
1102
1103Oscillator.Square = function(step) {
1104 return step < 0.5 ? 1 : -1;
1105};
1106
1107Oscillator.Saw = function(step) {
1108 return 2 * (step - Math.round(step));
1109};
1110
1111Oscillator.Triangle = function(step) {
1112 return 1 - 4 * Math.abs(Math.round(step) - step);
1113};
1114
1115Oscillator.Pulse = function(step) {
1116 // stub
1117};
1118
1119function ADSR(attackLength, decayLength, sustainLevel, sustainLength, releaseLength, sampleRate) {
1120 this.sampleRate = sampleRate;
1121 // Length in seconds
1122 this.attackLength = attackLength;
1123 this.decayLength = decayLength;
1124 this.sustainLevel = sustainLevel;
1125 this.sustainLength = sustainLength;
1126 this.releaseLength = releaseLength;
1127 this.sampleRate = sampleRate;
1128
1129 // Length in samples
1130 this.attackSamples = attackLength * sampleRate;
1131 this.decaySamples = decayLength * sampleRate;
1132 this.sustainSamples = sustainLength * sampleRate;
1133 this.releaseSamples = releaseLength * sampleRate;
1134
1135 // Updates the envelope sample positions
1136 this.update = function() {
1137 this.attack = this.attackSamples;
1138 this.decay = this.attack + this.decaySamples;
1139 this.sustain = this.decay + this.sustainSamples;
1140 this.release = this.sustain + this.releaseSamples;
1141 };
1142
1143 this.update();
1144
1145 this.samplesProcessed = 0;
1146}
1147
1148ADSR.prototype.noteOn = function() {
1149 this.samplesProcessed = 0;
1150 this.sustainSamples = this.sustainLength * this.sampleRate;
1151 this.update();
1152};
1153
1154// Send a note off when using a sustain of infinity to let the envelope enter the release phase
1155ADSR.prototype.noteOff = function() {
1156 this.sustainSamples = this.samplesProcessed - this.decaySamples;
1157 this.update();
1158};
1159
1160ADSR.prototype.processSample = function(sample) {
1161 var amplitude = 0;
1162
1163 if ( this.samplesProcessed <= this.attack ) {
1164 amplitude = 0 + (1 - 0) * ((this.samplesProcessed - 0) / (this.attack - 0));
1165 } else if ( this.samplesProcessed > this.attack && this.samplesProcessed <= this.decay ) {
1166 amplitude = 1 + (this.sustainLevel - 1) * ((this.samplesProcessed - this.attack) / (this.decay - this.attack));
1167 } else if ( this.samplesProcessed > this.decay && this.samplesProcessed <= this.sustain ) {
1168 amplitude = this.sustainLevel;
1169 } else if ( this.samplesProcessed > this.sustain && this.samplesProcessed <= this.release ) {
1170 amplitude = this.sustainLevel + (0 - this.sustainLevel) * ((this.samplesProcessed - this.sustain) / (this.release - this.sustain));
1171 }
1172
1173 return sample * amplitude;
1174};
1175
1176ADSR.prototype.value = function() {
1177 var amplitude = 0;
1178
1179 if ( this.samplesProcessed <= this.attack ) {
1180 amplitude = 0 + (1 - 0) * ((this.samplesProcessed - 0) / (this.attack - 0));
1181 } else if ( this.samplesProcessed > this.attack && this.samplesProcessed <= this.decay ) {
1182 amplitude = 1 + (this.sustainLevel - 1) * ((this.samplesProcessed - this.attack) / (this.decay - this.attack));
1183 } else if ( this.samplesProcessed > this.decay && this.samplesProcessed <= this.sustain ) {
1184 amplitude = this.sustainLevel;
1185 } else if ( this.samplesProcessed > this.sustain && this.samplesProcessed <= this.release ) {
1186 amplitude = this.sustainLevel + (0 - this.sustainLevel) * ((this.samplesProcessed - this.sustain) / (this.release - this.sustain));
1187 }
1188
1189 return amplitude;
1190};
1191
1192ADSR.prototype.process = function(buffer) {
1193 for ( var i = 0; i < buffer.length; i++ ) {
1194 buffer[i] *= this.value();
1195
1196 this.samplesProcessed++;
1197 }
1198
1199 return buffer;
1200};
1201
1202
1203ADSR.prototype.isActive = function() {
1204 if ( this.samplesProcessed > this.release || this.samplesProcessed === -1 ) {
1205 return false;
1206 } else {
1207 return true;
1208 }
1209};
1210
1211ADSR.prototype.disable = function() {
1212 this.samplesProcessed = -1;
1213};
1214
1215function IIRFilter(type, cutoff, resonance, sampleRate) {
1216 this.sampleRate = sampleRate;
1217
1218 switch(type) {
1219 case DSP.LOWPASS:
1220 case DSP.LP12:
1221 this.func = new IIRFilter.LP12(cutoff, resonance, sampleRate);
1222 break;
1223 }
1224}
1225
1226IIRFilter.prototype.__defineGetter__('cutoff',
1227 function() {
1228 return this.func.cutoff;
1229 }
1230);
1231
1232IIRFilter.prototype.__defineGetter__('resonance',
1233 function() {
1234 return this.func.resonance;
1235 }
1236);
1237
1238IIRFilter.prototype.set = function(cutoff, resonance) {
1239 this.func.calcCoeff(cutoff, resonance);
1240};
1241
1242IIRFilter.prototype.process = function(buffer) {
1243 this.func.process(buffer);
1244};
1245
1246// Add an envelope to the filter
1247IIRFilter.prototype.addEnvelope = function(envelope) {
1248 if ( envelope instanceof ADSR ) {
1249 this.func.addEnvelope(envelope);
1250 } else {
1251 throw "Not an envelope.";
1252 }
1253};
1254
1255IIRFilter.LP12 = function(cutoff, resonance, sampleRate) {
1256 this.sampleRate = sampleRate;
1257 this.vibraPos = 0;
1258 this.vibraSpeed = 0;
1259 this.envelope = false;
1260
1261 this.calcCoeff = function(cutoff, resonance) {
1262 this.w = 2.0 * Math.PI * cutoff / this.sampleRate;
1263 this.q = 1.0 - this.w / (2.0 * (resonance + 0.5 / (1.0 + this.w)) + this.w - 2.0);
1264 this.r = this.q * this.q;
1265 this.c = this.r + 1.0 - 2.0 * Math.cos(this.w) * this.q;
1266
1267 this.cutoff = cutoff;
1268 this.resonance = resonance;
1269 };
1270
1271 this.calcCoeff(cutoff, resonance);
1272
1273 this.process = function(buffer) {
1274 for ( var i = 0; i < buffer.length; i++ ) {
1275 this.vibraSpeed += (buffer[i] - this.vibraPos) * this.c;
1276 this.vibraPos += this.vibraSpeed;
1277 this.vibraSpeed *= this.r;
1278
1279 /*
1280 var temp = this.vibraPos;
1281
1282 if ( temp > 1.0 ) {
1283 temp = 1.0;
1284 } else if ( temp < -1.0 ) {
1285 temp = -1.0;
1286 } else if ( temp != temp ) {
1287 temp = 1;
1288 }
1289
1290 buffer[i] = temp;
1291 */
1292
1293 if (this.envelope) {
1294 buffer[i] = (buffer[i] * (1 - this.envelope.value())) + (this.vibraPos * this.envelope.value());
1295 this.envelope.samplesProcessed++;
1296 } else {
1297 buffer[i] = this.vibraPos;
1298 }
1299 }
1300 };
1301};
1302
1303IIRFilter.LP12.prototype.addEnvelope = function(envelope) {
1304 this.envelope = envelope;
1305};
1306
1307function IIRFilter2(type, cutoff, resonance, sampleRate) {
1308 this.type = type;
1309 this.cutoff = cutoff;
1310 this.resonance = resonance;
1311 this.sampleRate = sampleRate;
1312
1313 this.f = new Float32Array(4);
1314 this.f[0] = 0.0; // lp
1315 this.f[1] = 0.0; // hp
1316 this.f[2] = 0.0; // bp
1317 this.f[3] = 0.0; // br
1318
1319 this.calcCoeff = function(cutoff, resonance) {
1320 this.freq = 2 * Math.sin(Math.PI * Math.min(0.25, cutoff/(this.sampleRate*2)));
1321 this.damp = Math.min(2 * (1 - Math.pow(resonance, 0.25)), Math.min(2, 2/this.freq - this.freq * 0.5));
1322 };
1323
1324 this.calcCoeff(cutoff, resonance);
1325}
1326
1327IIRFilter2.prototype.process = function(buffer) {
1328 var input, output;
1329 var f = this.f;
1330
1331 for ( var i = 0; i < buffer.length; i++ ) {
1332 input = buffer[i];
1333
1334 // first pass
1335 f[3] = input - this.damp * f[2];
1336 f[0] = f[0] + this.freq * f[2];
1337 f[1] = f[3] - f[0];
1338 f[2] = this.freq * f[1] + f[2];
1339 output = 0.5 * f[this.type];
1340
1341 // second pass
1342 f[3] = input - this.damp * f[2];
1343 f[0] = f[0] + this.freq * f[2];
1344 f[1] = f[3] - f[0];
1345 f[2] = this.freq * f[1] + f[2];
1346 output += 0.5 * f[this.type];
1347
1348 if (this.envelope) {
1349 buffer[i] = (buffer[i] * (1 - this.envelope.value())) + (output * this.envelope.value());
1350 this.envelope.samplesProcessed++;
1351 } else {
1352 buffer[i] = output;
1353 }
1354 }
1355};
1356
1357IIRFilter2.prototype.addEnvelope = function(envelope) {
1358 if ( envelope instanceof ADSR ) {
1359 this.envelope = envelope;
1360 } else {
1361 throw "This is not an envelope.";
1362 }
1363};
1364
1365IIRFilter2.prototype.set = function(cutoff, resonance) {
1366 this.calcCoeff(cutoff, resonance);
1367};
1368
1369
1370
1371function WindowFunction(type, alpha) {
1372 this.alpha = alpha;
1373
1374 switch(type) {
1375 case DSP.BARTLETT:
1376 this.func = WindowFunction.Bartlett;
1377 break;
1378
1379 case DSP.BARTLETTHANN:
1380 this.func = WindowFunction.BartlettHann;
1381 break;
1382
1383 case DSP.BLACKMAN:
1384 this.func = WindowFunction.Blackman;
1385 this.alpha = this.alpha || 0.16;
1386 break;
1387
1388 case DSP.COSINE:
1389 this.func = WindowFunction.Cosine;
1390 break;
1391
1392 case DSP.GAUSS:
1393 this.func = WindowFunction.Gauss;
1394 this.alpha = this.alpha || 0.25;
1395 break;
1396
1397 case DSP.HAMMING:
1398 this.func = WindowFunction.Hamming;
1399 break;
1400
1401 case DSP.HANN:
1402 this.func = WindowFunction.Hann;
1403 break;
1404
1405 case DSP.LANCZOS:
1406 this.func = WindowFunction.Lanczoz;
1407 break;
1408
1409 case DSP.RECTANGULAR:
1410 this.func = WindowFunction.Rectangular;
1411 break;
1412
1413 case DSP.TRIANGULAR:
1414 this.func = WindowFunction.Triangular;
1415 break;
1416 }
1417}
1418
1419WindowFunction.prototype.process = function(buffer) {
1420 var length = buffer.length;
1421 for ( var i = 0; i < length; i++ ) {
1422 buffer[i] *= this.func(length, i, this.alpha);
1423 }
1424 return buffer;
1425};
1426
1427WindowFunction.Bartlett = function(length, index) {
1428 return 2 / (length - 1) * ((length - 1) / 2 - Math.abs(index - (length - 1) / 2));
1429};
1430
1431WindowFunction.BartlettHann = function(length, index) {
1432 return 0.62 - 0.48 * Math.abs(index / (length - 1) - 0.5) - 0.38 * Math.cos(DSP.TWO_PI * index / (length - 1));
1433};
1434
1435WindowFunction.Blackman = function(length, index, alpha) {
1436 var a0 = (1 - alpha) / 2;
1437 var a1 = 0.5;
1438 var a2 = alpha / 2;
1439
1440 return a0 - a1 * Math.cos(DSP.TWO_PI * index / (length - 1)) + a2 * Math.cos(4 * Math.PI * index / (length - 1));
1441};
1442
1443WindowFunction.Cosine = function(length, index) {
1444 return Math.cos(Math.PI * index / (length - 1) - Math.PI / 2);
1445};
1446
1447WindowFunction.Gauss = function(length, index, alpha) {
1448 return Math.pow(Math.E, -0.5 * Math.pow((index - (length - 1) / 2) / (alpha * (length - 1) / 2), 2));
1449};
1450
1451WindowFunction.Hamming = function(length, index) {
1452 return 0.54 - 0.46 * Math.cos(DSP.TWO_PI * index / (length - 1));
1453};
1454
1455WindowFunction.Hann = function(length, index) {
1456 return 0.5 * (1 - Math.cos(DSP.TWO_PI * index / (length - 1)));
1457};
1458
1459WindowFunction.Lanczos = function(length, index) {
1460 var x = 2 * index / (length - 1) - 1;
1461 return Math.sin(Math.PI * x) / (Math.PI * x);
1462};
1463
1464WindowFunction.Rectangular = function(length, index) {
1465 return 1;
1466};
1467
1468WindowFunction.Triangular = function(length, index) {
1469 return 2 / length * (length / 2 - Math.abs(index - (length - 1) / 2));
1470};
1471
1472function sinh (arg) {
1473 // Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2
1474 //
1475 // version: 1004.2314
1476 // discuss at: http://phpjs.org/functions/sinh // + original by: Onno Marsman
1477 // * example 1: sinh(-0.9834330348825909);
1478 // * returns 1: -1.1497971402636502
1479 return (Math.exp(arg) - Math.exp(-arg))/2;
1480}
1481
1482/*
1483 * Biquad filter
1484 *
1485 * Created by Ricard Marxer <[email protected]> on 2010-05-23.
1486 * Copyright 2010 Ricard Marxer. All rights reserved.
1487 *
1488 */
1489// Implementation based on:
1490// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
1491function Biquad(type, sampleRate) {
1492 this.Fs = sampleRate;
1493 this.type = type; // type of the filter
1494 this.parameterType = DSP.Q; // type of the parameter
1495
1496 this.x_1_l = 0;
1497 this.x_2_l = 0;
1498 this.y_1_l = 0;
1499 this.y_2_l = 0;
1500
1501 this.x_1_r = 0;
1502 this.x_2_r = 0;
1503 this.y_1_r = 0;
1504 this.y_2_r = 0;
1505
1506 this.b0 = 1;
1507 this.a0 = 1;
1508
1509 this.b1 = 0;
1510 this.a1 = 0;
1511
1512 this.b2 = 0;
1513 this.a2 = 0;
1514
1515 this.b0a0 = this.b0 / this.a0;
1516 this.b1a0 = this.b1 / this.a0;
1517 this.b2a0 = this.b2 / this.a0;
1518 this.a1a0 = this.a1 / this.a0;
1519 this.a2a0 = this.a2 / this.a0;
1520
1521 this.f0 = 3000; // "wherever it's happenin', man." Center Frequency or
1522 // Corner Frequency, or shelf midpoint frequency, depending
1523 // on which filter type. The "significant frequency".
1524
1525 this.dBgain = 12; // used only for peaking and shelving filters
1526
1527 this.Q = 1; // the EE kind of definition, except for peakingEQ in which A*Q is
1528 // the classic EE Q. That adjustment in definition was made so that
1529 // a boost of N dB followed by a cut of N dB for identical Q and
1530 // f0/Fs results in a precisely flat unity gain filter or "wire".
1531
1532 this.BW = -3; // the bandwidth in octaves (between -3 dB frequencies for BPF
1533 // and notch or between midpoint (dBgain/2) gain frequencies for
1534 // peaking EQ
1535
1536 this.S = 1; // a "shelf slope" parameter (for shelving EQ only). When S = 1,
1537 // the shelf slope is as steep as it can be and remain monotonically
1538 // increasing or decreasing gain with frequency. The shelf slope, in
1539 // dB/octave, remains proportional to S for all other values for a
1540 // fixed f0/Fs and dBgain.
1541
1542 this.coefficients = function() {
1543 var b = [this.b0, this.b1, this.b2];
1544 var a = [this.a0, this.a1, this.a2];
1545 return {b: b, a:a};
1546 };
1547
1548 this.setFilterType = function(type) {
1549 this.type = type;
1550 this.recalculateCoefficients();
1551 };
1552
1553 this.setSampleRate = function(rate) {
1554 this.Fs = rate;
1555 this.recalculateCoefficients();
1556 };
1557
1558 this.setQ = function(q) {
1559 this.parameterType = DSP.Q;
1560 this.Q = Math.max(Math.min(q, 115.0), 0.001);
1561 this.recalculateCoefficients();
1562 };
1563
1564 this.setBW = function(bw) {
1565 this.parameterType = DSP.BW;
1566 this.BW = bw;
1567 this.recalculateCoefficients();
1568 };
1569
1570 this.setS = function(s) {
1571 this.parameterType = DSP.S;
1572 this.S = Math.max(Math.min(s, 5.0), 0.0001);
1573 this.recalculateCoefficients();
1574 };
1575
1576 this.setF0 = function(freq) {
1577 this.f0 = freq;
1578 this.recalculateCoefficients();
1579 };
1580
1581 this.setDbGain = function(g) {
1582 this.dBgain = g;
1583 this.recalculateCoefficients();
1584 };
1585
1586 this.recalculateCoefficients = function() {
1587 var A;
1588 if (type === DSP.PEAKING_EQ || type === DSP.LOW_SHELF || type === DSP.HIGH_SHELF ) {
1589 A = Math.pow(10, (this.dBgain/40)); // for peaking and shelving EQ filters only
1590 } else {
1591 A = Math.sqrt( Math.pow(10, (this.dBgain/20)) );
1592 }
1593
1594 var w0 = DSP.TWO_PI * this.f0 / this.Fs;
1595
1596 var cosw0 = Math.cos(w0);
1597 var sinw0 = Math.sin(w0);
1598
1599 var alpha = 0;
1600
1601 switch (this.parameterType) {
1602 case DSP.Q:
1603 alpha = sinw0/(2*this.Q);
1604 break;
1605
1606 case DSP.BW:
1607 alpha = sinw0 * sinh( Math.LN2/2 * this.BW * w0/sinw0 );
1608 break;
1609
1610 case DSP.S:
1611 alpha = sinw0/2 * Math.sqrt( (A + 1/A)*(1/this.S - 1) + 2 );
1612 break;
1613 }
1614
1615 /**
1616 FYI: The relationship between bandwidth and Q is
1617 1/Q = 2*sinh(ln(2)/2*BW*w0/sin(w0)) (digital filter w BLT)
1618 or 1/Q = 2*sinh(ln(2)/2*BW) (analog filter prototype)
1619
1620 The relationship between shelf slope and Q is
1621 1/Q = sqrt((A + 1/A)*(1/S - 1) + 2)
1622 */
1623
1624 var coeff;
1625
1626 switch (this.type) {
1627 case DSP.LPF: // H(s) = 1 / (s^2 + s/Q + 1)
1628 this.b0 = (1 - cosw0)/2;
1629 this.b1 = 1 - cosw0;
1630 this.b2 = (1 - cosw0)/2;
1631 this.a0 = 1 + alpha;
1632 this.a1 = -2 * cosw0;
1633 this.a2 = 1 - alpha;
1634 break;
1635
1636 case DSP.HPF: // H(s) = s^2 / (s^2 + s/Q + 1)
1637 this.b0 = (1 + cosw0)/2;
1638 this.b1 = -(1 + cosw0);
1639 this.b2 = (1 + cosw0)/2;
1640 this.a0 = 1 + alpha;
1641 this.a1 = -2 * cosw0;
1642 this.a2 = 1 - alpha;
1643 break;
1644
1645 case DSP.BPF_CONSTANT_SKIRT: // H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q)
1646 this.b0 = sinw0/2;
1647 this.b1 = 0;
1648 this.b2 = -sinw0/2;
1649 this.a0 = 1 + alpha;
1650 this.a1 = -2*cosw0;
1651 this.a2 = 1 - alpha;
1652 break;
1653
1654 case DSP.BPF_CONSTANT_PEAK: // H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain)
1655 this.b0 = alpha;
1656 this.b1 = 0;
1657 this.b2 = -alpha;
1658 this.a0 = 1 + alpha;
1659 this.a1 = -2*cosw0;
1660 this.a2 = 1 - alpha;
1661 break;
1662
1663 case DSP.NOTCH: // H(s) = (s^2 + 1) / (s^2 + s/Q + 1)
1664 this.b0 = 1;
1665 this.b1 = -2*cosw0;
1666 this.b2 = 1;
1667 this.a0 = 1 + alpha;
1668 this.a1 = -2*cosw0;
1669 this.a2 = 1 - alpha;
1670 break;
1671
1672 case DSP.APF: // H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1)
1673 this.b0 = 1 - alpha;
1674 this.b1 = -2*cosw0;
1675 this.b2 = 1 + alpha;
1676 this.a0 = 1 + alpha;
1677 this.a1 = -2*cosw0;
1678 this.a2 = 1 - alpha;
1679 break;
1680
1681 case DSP.PEAKING_EQ: // H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
1682 this.b0 = 1 + alpha*A;
1683 this.b1 = -2*cosw0;
1684 this.b2 = 1 - alpha*A;
1685 this.a0 = 1 + alpha/A;
1686 this.a1 = -2*cosw0;
1687 this.a2 = 1 - alpha/A;
1688 break;
1689
1690 case DSP.LOW_SHELF: // H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1)
1691 coeff = sinw0 * Math.sqrt( (A^2 + 1)*(1/this.S - 1) + 2*A );
1692 this.b0 = A*((A+1) - (A-1)*cosw0 + coeff);
1693 this.b1 = 2*A*((A-1) - (A+1)*cosw0);
1694 this.b2 = A*((A+1) - (A-1)*cosw0 - coeff);
1695 this.a0 = (A+1) + (A-1)*cosw0 + coeff;
1696 this.a1 = -2*((A-1) + (A+1)*cosw0);
1697 this.a2 = (A+1) + (A-1)*cosw0 - coeff;
1698 break;
1699
1700 case DSP.HIGH_SHELF: // H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A)
1701 coeff = sinw0 * Math.sqrt( (A^2 + 1)*(1/this.S - 1) + 2*A );
1702 this.b0 = A*((A+1) + (A-1)*cosw0 + coeff);
1703 this.b1 = -2*A*((A-1) + (A+1)*cosw0);
1704 this.b2 = A*((A+1) + (A-1)*cosw0 - coeff);
1705 this.a0 = (A+1) - (A-1)*cosw0 + coeff;
1706 this.a1 = 2*((A-1) - (A+1)*cosw0);
1707 this.a2 = (A+1) - (A-1)*cosw0 - coeff;
1708 break;
1709 }
1710
1711 this.b0a0 = this.b0/this.a0;
1712 this.b1a0 = this.b1/this.a0;
1713 this.b2a0 = this.b2/this.a0;
1714 this.a1a0 = this.a1/this.a0;
1715 this.a2a0 = this.a2/this.a0;
1716 };
1717
1718 this.process = function(buffer) {
1719 //y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2]
1720 // - (a1/a0)*y[n-1] - (a2/a0)*y[n-2]
1721
1722 var len = buffer.length;
1723 var output = new Float32Array(len);
1724
1725 for ( var i=0; i<buffer.length; i++ ) {
1726 output[i] = this.b0a0*buffer[i] + this.b1a0*this.x_1_l + this.b2a0*this.x_2_l - this.a1a0*this.y_1_l - this.a2a0*this.y_2_l;
1727 this.y_2_l = this.y_1_l;
1728 this.y_1_l = output[i];
1729 this.x_2_l = this.x_1_l;
1730 this.x_1_l = buffer[i];
1731 }
1732
1733 return output;
1734 };
1735
1736 this.processStereo = function(buffer) {
1737 //y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2]
1738 // - (a1/a0)*y[n-1] - (a2/a0)*y[n-2]
1739
1740 var len = buffer.length;
1741 var output = new Float32Array(len);
1742
1743 for (var i = 0; i < len/2; i++) {
1744 output[2*i] = this.b0a0*buffer[2*i] + this.b1a0*this.x_1_l + this.b2a0*this.x_2_l - this.a1a0*this.y_1_l - this.a2a0*this.y_2_l;
1745 this.y_2_l = this.y_1_l;
1746 this.y_1_l = output[2*i];
1747 this.x_2_l = this.x_1_l;
1748 this.x_1_l = buffer[2*i];
1749
1750 output[2*i+1] = this.b0a0*buffer[2*i+1] + this.b1a0*this.x_1_r + this.b2a0*this.x_2_r - this.a1a0*this.y_1_r - this.a2a0*this.y_2_r;
1751 this.y_2_r = this.y_1_r;
1752 this.y_1_r = output[2*i+1];
1753 this.x_2_r = this.x_1_r;
1754 this.x_1_r = buffer[2*i+1];
1755 }
1756
1757 return output;
1758 };
1759}
1760
1761/*
1762 * Magnitude to decibels
1763 *
1764 * Created by Ricard Marxer <[email protected]> on 2010-05-23.
1765 * Copyright 2010 Ricard Marxer. All rights reserved.
1766 *
1767 * @buffer array of magnitudes to convert to decibels
1768 *
1769 * @returns the array in decibels
1770 *
1771 */
1772DSP.mag2db = function(buffer) {
1773 var minDb = -120;
1774 var minMag = Math.pow(10.0, minDb / 20.0);
1775
1776 var log = Math.log;
1777 var max = Math.max;
1778
1779 var result = new Float32Array(buffer.length);
1780 for (var i=0; i<buffer.length; i++) {
1781 result[i] = 20.0*log(max(buffer[i], minMag));
1782 }
1783
1784 return result;
1785};
1786
1787/*
1788 * Frequency response
1789 *
1790 * Created by Ricard Marxer <[email protected]> on 2010-05-23.
1791 * Copyright 2010 Ricard Marxer. All rights reserved.
1792 *
1793 * Calculates the frequency response at the given points.
1794 *
1795 * @b b coefficients of the filter
1796 * @a a coefficients of the filter
1797 * @w w points (normally between -PI and PI) where to calculate the frequency response
1798 *
1799 * @returns the frequency response in magnitude
1800 *
1801 */
1802DSP.freqz = function(b, a, w) {
1803 var i, j;
1804
1805 if (!w) {
1806 w = new Float32Array(200);
1807 for (i=0;i<w.length; i++) {
1808 w[i] = DSP.TWO_PI/w.length * i - Math.PI;
1809 }
1810 }
1811
1812 var result = new Float32Array(w.length);
1813
1814 var sqrt = Math.sqrt;
1815 var cos = Math.cos;
1816 var sin = Math.sin;
1817
1818 for (i=0; i<w.length; i++) {
1819 var numerator = {real:0.0, imag:0.0};
1820 for (j=0; j<b.length; j++) {
1821 numerator.real += b[j] * cos(-j*w[i]);
1822 numerator.imag += b[j] * sin(-j*w[i]);
1823 }
1824
1825 var denominator = {real:0.0, imag:0.0};
1826 for (j=0; j<a.length; j++) {
1827 denominator.real += a[j] * cos(-j*w[i]);
1828 denominator.imag += a[j] * sin(-j*w[i]);
1829 }
1830
1831 result[i] = sqrt(numerator.real*numerator.real + numerator.imag*numerator.imag) / sqrt(denominator.real*denominator.real + denominator.imag*denominator.imag);
1832 }
1833
1834 return result;
1835};
1836
1837/*
1838 * Graphical Equalizer
1839 *
1840 * Implementation of a graphic equalizer with a configurable bands-per-octave
1841 * and minimum and maximum frequencies
1842 *
1843 * Created by Ricard Marxer <[email protected]> on 2010-05-23.
1844 * Copyright 2010 Ricard Marxer. All rights reserved.
1845 *
1846 */
1847function GraphicalEq(sampleRate) {
1848 this.FS = sampleRate;
1849 this.minFreq = 40.0;
1850 this.maxFreq = 16000.0;
1851
1852 this.bandsPerOctave = 1.0;
1853
1854 this.filters = [];
1855 this.freqzs = [];
1856
1857 this.calculateFreqzs = true;
1858
1859 this.recalculateFilters = function() {
1860 var bandCount = Math.round(Math.log(this.maxFreq/this.minFreq) * this.bandsPerOctave/ Math.LN2);
1861
1862 this.filters = [];
1863 for (var i=0; i<bandCount; i++) {
1864 var freq = this.minFreq*(Math.pow(2, i/this.bandsPerOctave));
1865 var newFilter = new Biquad(DSP.PEAKING_EQ, this.FS);
1866 newFilter.setDbGain(0);
1867 newFilter.setBW(1/this.bandsPerOctave);
1868 newFilter.setF0(freq);
1869 this.filters[i] = newFilter;
1870 this.recalculateFreqz(i);
1871 }
1872 };
1873
1874 this.setMinimumFrequency = function(freq) {
1875 this.minFreq = freq;
1876 this.recalculateFilters();
1877 };
1878
1879 this.setMaximumFrequency = function(freq) {
1880 this.maxFreq = freq;
1881 this.recalculateFilters();
1882 };
1883
1884 this.setBandsPerOctave = function(bands) {
1885 this.bandsPerOctave = bands;
1886 this.recalculateFilters();
1887 };
1888
1889 this.setBandGain = function(bandIndex, gain) {
1890 if (bandIndex < 0 || bandIndex > (this.filters.length-1)) {
1891 throw "The band index of the graphical equalizer is out of bounds.";
1892 }
1893
1894 if (!gain) {
1895 throw "A gain must be passed.";
1896 }
1897
1898 this.filters[bandIndex].setDbGain(gain);
1899 this.recalculateFreqz(bandIndex);
1900 };
1901
1902 this.recalculateFreqz = function(bandIndex) {
1903 if (!this.calculateFreqzs) {
1904 return;
1905 }
1906
1907 if (bandIndex < 0 || bandIndex > (this.filters.length-1)) {
1908 throw "The band index of the graphical equalizer is out of bounds. " + bandIndex + " is out of [" + 0 + ", " + this.filters.length-1 + "]";
1909 }
1910
1911 if (!this.w) {
1912 this.w = new Float32Array(400);
1913 for (var i=0; i<this.w.length; i++) {
1914 this.w[i] = Math.PI/this.w.length * i;
1915 }
1916 }
1917
1918 var b = [this.filters[bandIndex].b0, this.filters[bandIndex].b1, this.filters[bandIndex].b2];
1919 var a = [this.filters[bandIndex].a0, this.filters[bandIndex].a1, this.filters[bandIndex].a2];
1920
1921 this.freqzs[bandIndex] = DSP.mag2db(DSP.freqz(b, a, this.w));
1922 };
1923
1924 this.process = function(buffer) {
1925 var output = buffer;
1926
1927 for (var i = 0; i < this.filters.length; i++) {
1928 output = this.filters[i].process(output);
1929 }
1930
1931 return output;
1932 };
1933
1934 this.processStereo = function(buffer) {
1935 var output = buffer;
1936
1937 for (var i = 0; i < this.filters.length; i++) {
1938 output = this.filters[i].processStereo(output);
1939 }
1940
1941 return output;
1942 };
1943}
1944
1945/**
1946 * MultiDelay effect by Almer Thie (http://code.almeros.com).
1947 * Copyright 2010 Almer Thie. All rights reserved.
1948 * Example: http://code.almeros.com/code-examples/delay-firefox-audio-api/
1949 *
1950 * This is a delay that feeds it's own delayed signal back into its circular
1951 * buffer. Also known as a CombFilter.
1952 *
1953 * Compatible with interleaved stereo (or more channel) buffers and
1954 * non-interleaved mono buffers.
1955 *
1956 * @param {Number} maxDelayInSamplesSize Maximum possible delay in samples (size of circular buffer)
1957 * @param {Number} delayInSamples Initial delay in samples
1958 * @param {Number} masterVolume Initial master volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
1959 * @param {Number} delayVolume Initial feedback delay volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
1960 *
1961 * @constructor
1962 */
1963function MultiDelay(maxDelayInSamplesSize, delayInSamples, masterVolume, delayVolume) {
1964 this.delayBufferSamples = new Float32Array(maxDelayInSamplesSize); // The maximum size of delay
1965 this.delayInputPointer = delayInSamples;
1966 this.delayOutputPointer = 0;
1967
1968 this.delayInSamples = delayInSamples;
1969 this.masterVolume = masterVolume;
1970 this.delayVolume = delayVolume;
1971}
1972
1973/**
1974 * Change the delay time in samples.
1975 *
1976 * @param {Number} delayInSamples Delay in samples
1977 */
1978MultiDelay.prototype.setDelayInSamples = function (delayInSamples) {
1979 this.delayInSamples = delayInSamples;
1980
1981 this.delayInputPointer = this.delayOutputPointer + delayInSamples;
1982
1983 if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
1984 this.delayInputPointer = this.delayInputPointer - this.delayBufferSamples.length;
1985 }
1986};
1987
1988/**
1989 * Change the master volume.
1990 *
1991 * @param {Number} masterVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
1992 */
1993MultiDelay.prototype.setMasterVolume = function(masterVolume) {
1994 this.masterVolume = masterVolume;
1995};
1996
1997/**
1998 * Change the delay feedback volume.
1999 *
2000 * @param {Number} delayVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
2001 */
2002MultiDelay.prototype.setDelayVolume = function(delayVolume) {
2003 this.delayVolume = delayVolume;
2004};
2005
2006/**
2007 * Process a given interleaved or mono non-interleaved float value Array and adds the delayed audio.
2008 *
2009 * @param {Array} samples Array containing Float values or a Float32Array
2010 *
2011 * @returns A new Float32Array interleaved or mono non-interleaved as was fed to this function.
2012 */
2013MultiDelay.prototype.process = function(samples) {
2014 // NB. Make a copy to put in the output samples to return.
2015 var outputSamples = new Float32Array(samples.length);
2016
2017 for (var i=0; i<samples.length; i++) {
2018 // delayBufferSamples could contain initial NULL's, return silence in that case
2019 var delaySample = (this.delayBufferSamples[this.delayOutputPointer] === null ? 0.0 : this.delayBufferSamples[this.delayOutputPointer]);
2020
2021 // Mix normal audio data with delayed audio
2022 var sample = (delaySample * this.delayVolume) + samples[i];
2023
2024 // Add audio data with the delay in the delay buffer
2025 this.delayBufferSamples[this.delayInputPointer] = sample;
2026
2027 // Return the audio with delay mix
2028 outputSamples[i] = sample * this.masterVolume;
2029
2030 // Manage circulair delay buffer pointers
2031 this.delayInputPointer++;
2032 if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
2033 this.delayInputPointer = 0;
2034 }
2035
2036 this.delayOutputPointer++;
2037 if (this.delayOutputPointer >= this.delayBufferSamples.length-1) {
2038 this.delayOutputPointer = 0;
2039 }
2040 }
2041
2042 return outputSamples;
2043};
2044
2045/**
2046 * SingleDelay effect by Almer Thie (http://code.almeros.com).
2047 * Copyright 2010 Almer Thie. All rights reserved.
2048 * Example: See usage in Reverb class
2049 *
2050 * This is a delay that does NOT feeds it's own delayed signal back into its
2051 * circular buffer, neither does it return the original signal. Also known as
2052 * an AllPassFilter(?).
2053 *
2054 * Compatible with interleaved stereo (or more channel) buffers and
2055 * non-interleaved mono buffers.
2056 *
2057 * @param {Number} maxDelayInSamplesSize Maximum possible delay in samples (size of circular buffer)
2058 * @param {Number} delayInSamples Initial delay in samples
2059 * @param {Number} delayVolume Initial feedback delay volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
2060 *
2061 * @constructor
2062 */
2063
2064function SingleDelay(maxDelayInSamplesSize, delayInSamples, delayVolume) {
2065 this.delayBufferSamples = new Float32Array(maxDelayInSamplesSize); // The maximum size of delay
2066 this.delayInputPointer = delayInSamples;
2067 this.delayOutputPointer = 0;
2068
2069 this.delayInSamples = delayInSamples;
2070 this.delayVolume = delayVolume;
2071}
2072
2073/**
2074 * Change the delay time in samples.
2075 *
2076 * @param {Number} delayInSamples Delay in samples
2077 */
2078SingleDelay.prototype.setDelayInSamples = function(delayInSamples) {
2079 this.delayInSamples = delayInSamples;
2080 this.delayInputPointer = this.delayOutputPointer + delayInSamples;
2081
2082 if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
2083 this.delayInputPointer = this.delayInputPointer - this.delayBufferSamples.length;
2084 }
2085};
2086
2087/**
2088 * Change the return signal volume.
2089 *
2090 * @param {Number} delayVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
2091 */
2092SingleDelay.prototype.setDelayVolume = function(delayVolume) {
2093 this.delayVolume = delayVolume;
2094};
2095
2096/**
2097 * Process a given interleaved or mono non-interleaved float value Array and
2098 * returns the delayed audio.
2099 *
2100 * @param {Array} samples Array containing Float values or a Float32Array
2101 *
2102 * @returns A new Float32Array interleaved or mono non-interleaved as was fed to this function.
2103 */
2104SingleDelay.prototype.process = function(samples) {
2105 // NB. Make a copy to put in the output samples to return.
2106 var outputSamples = new Float32Array(samples.length);
2107
2108 for (var i=0; i<samples.length; i++) {
2109
2110 // Add audio data with the delay in the delay buffer
2111 this.delayBufferSamples[this.delayInputPointer] = samples[i];
2112
2113 // delayBufferSamples could contain initial NULL's, return silence in that case
2114 var delaySample = this.delayBufferSamples[this.delayOutputPointer];
2115
2116 // Return the audio with delay mix
2117 outputSamples[i] = delaySample * this.delayVolume;
2118
2119 // Manage circulair delay buffer pointers
2120 this.delayInputPointer++;
2121
2122 if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
2123 this.delayInputPointer = 0;
2124 }
2125
2126 this.delayOutputPointer++;
2127
2128 if (this.delayOutputPointer >= this.delayBufferSamples.length-1) {
2129 this.delayOutputPointer = 0;
2130 }
2131 }
2132
2133 return outputSamples;
2134};
2135
2136/**
2137 * Reverb effect by Almer Thie (http://code.almeros.com).
2138 * Copyright 2010 Almer Thie. All rights reserved.
2139 * Example: http://code.almeros.com/code-examples/reverb-firefox-audio-api/
2140 *
2141 * This reverb consists of 6 SingleDelays, 6 MultiDelays and an IIRFilter2
2142 * for each of the two stereo channels.
2143 *
2144 * Compatible with interleaved stereo buffers only!
2145 *
2146 * @param {Number} maxDelayInSamplesSize Maximum possible delay in samples (size of circular buffers)
2147 * @param {Number} delayInSamples Initial delay in samples for internal (Single/Multi)delays
2148 * @param {Number} masterVolume Initial master volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
2149 * @param {Number} mixVolume Initial reverb signal mix volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
2150 * @param {Number} delayVolume Initial feedback delay volume for internal (Single/Multi)delays. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
2151 * @param {Number} dampFrequency Initial low pass filter frequency. 0 to 44100 (depending on your maximum sampling frequency)
2152 *
2153 * @constructor
2154 */
2155function Reverb(maxDelayInSamplesSize, delayInSamples, masterVolume, mixVolume, delayVolume, dampFrequency) {
2156 this.delayInSamples = delayInSamples;
2157 this.masterVolume = masterVolume;
2158 this.mixVolume = mixVolume;
2159 this.delayVolume = delayVolume;
2160 this.dampFrequency = dampFrequency;
2161
2162 this.NR_OF_MULTIDELAYS = 6;
2163 this.NR_OF_SINGLEDELAYS = 6;
2164
2165 this.LOWPASSL = new IIRFilter2(DSP.LOWPASS, dampFrequency, 0, 44100);
2166 this.LOWPASSR = new IIRFilter2(DSP.LOWPASS, dampFrequency, 0, 44100);
2167
2168 this.singleDelays = [];
2169
2170 var i, delayMultiply;
2171
2172 for (i = 0; i < this.NR_OF_SINGLEDELAYS; i++) {
2173 delayMultiply = 1.0 + (i/7.0); // 1.0, 1.1, 1.2...
2174 this.singleDelays[i] = new SingleDelay(maxDelayInSamplesSize, Math.round(this.delayInSamples * delayMultiply), this.delayVolume);
2175 }
2176
2177 this.multiDelays = [];
2178
2179 for (i = 0; i < this.NR_OF_MULTIDELAYS; i++) {
2180 delayMultiply = 1.0 + (i/10.0); // 1.0, 1.1, 1.2...
2181 this.multiDelays[i] = new MultiDelay(maxDelayInSamplesSize, Math.round(this.delayInSamples * delayMultiply), this.masterVolume, this.delayVolume);
2182 }
2183}
2184
2185/**
2186 * Change the delay time in samples as a base for all delays.
2187 *
2188 * @param {Number} delayInSamples Delay in samples
2189 */
2190Reverb.prototype.setDelayInSamples = function (delayInSamples){
2191 this.delayInSamples = delayInSamples;
2192
2193 var i, delayMultiply;
2194
2195 for (i = 0; i < this.NR_OF_SINGLEDELAYS; i++) {
2196 delayMultiply = 1.0 + (i/7.0); // 1.0, 1.1, 1.2...
2197 this.singleDelays[i].setDelayInSamples( Math.round(this.delayInSamples * delayMultiply) );
2198 }
2199
2200 for (i = 0; i < this.NR_OF_MULTIDELAYS; i++) {
2201 delayMultiply = 1.0 + (i/10.0); // 1.0, 1.1, 1.2...
2202 this.multiDelays[i].setDelayInSamples( Math.round(this.delayInSamples * delayMultiply) );
2203 }
2204};
2205
2206/**
2207 * Change the master volume.
2208 *
2209 * @param {Number} masterVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
2210 */
2211Reverb.prototype.setMasterVolume = function (masterVolume){
2212 this.masterVolume = masterVolume;
2213};
2214
2215/**
2216 * Change the reverb signal mix level.
2217 *
2218 * @param {Number} mixVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
2219 */
2220Reverb.prototype.setMixVolume = function (mixVolume){
2221 this.mixVolume = mixVolume;
2222};
2223
2224/**
2225 * Change all delays feedback volume.
2226 *
2227 * @param {Number} delayVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
2228 */
2229Reverb.prototype.setDelayVolume = function (delayVolume){
2230 this.delayVolume = delayVolume;
2231
2232 var i;
2233
2234 for (i = 0; i<this.NR_OF_SINGLEDELAYS; i++) {
2235 this.singleDelays[i].setDelayVolume(this.delayVolume);
2236 }
2237
2238 for (i = 0; i<this.NR_OF_MULTIDELAYS; i++) {
2239 this.multiDelays[i].setDelayVolume(this.delayVolume);
2240 }
2241};
2242
2243/**
2244 * Change the Low Pass filter frequency.
2245 *
2246 * @param {Number} dampFrequency low pass filter frequency. 0 to 44100 (depending on your maximum sampling frequency)
2247 */
2248Reverb.prototype.setDampFrequency = function (dampFrequency){
2249 this.dampFrequency = dampFrequency;
2250
2251 this.LOWPASSL.set(dampFrequency, 0);
2252 this.LOWPASSR.set(dampFrequency, 0);
2253};
2254
2255/**
2256 * Process a given interleaved float value Array and copies and adds the reverb signal.
2257 *
2258 * @param {Array} samples Array containing Float values or a Float32Array
2259 *
2260 * @returns A new Float32Array interleaved buffer.
2261 */
2262Reverb.prototype.process = function (interleavedSamples){
2263 // NB. Make a copy to put in the output samples to return.
2264 var outputSamples = new Float32Array(interleavedSamples.length);
2265
2266 // Perform low pass on the input samples to mimick damp
2267 var leftRightMix = DSP.deinterleave(interleavedSamples);
2268 this.LOWPASSL.process( leftRightMix[DSP.LEFT] );
2269 this.LOWPASSR.process( leftRightMix[DSP.RIGHT] );
2270 var filteredSamples = DSP.interleave(leftRightMix[DSP.LEFT], leftRightMix[DSP.RIGHT]);
2271
2272 var i;
2273
2274 // Process MultiDelays in parallel
2275 for (i = 0; i<this.NR_OF_MULTIDELAYS; i++) {
2276 // Invert the signal of every even multiDelay
2277 outputSamples = DSP.mixSampleBuffers(outputSamples, this.multiDelays[i].process(filteredSamples), 2%i === 0, this.NR_OF_MULTIDELAYS);
2278 }
2279
2280 // Process SingleDelays in series
2281 var singleDelaySamples = new Float32Array(outputSamples.length);
2282 for (i = 0; i<this.NR_OF_SINGLEDELAYS; i++) {
2283 // Invert the signal of every even singleDelay
2284 singleDelaySamples = DSP.mixSampleBuffers(singleDelaySamples, this.singleDelays[i].process(outputSamples), 2%i === 0, 1);
2285 }
2286
2287 // Apply the volume of the reverb signal
2288 for (i = 0; i<singleDelaySamples.length; i++) {
2289 singleDelaySamples[i] *= this.mixVolume;
2290 }
2291
2292 // Mix the original signal with the reverb signal
2293 outputSamples = DSP.mixSampleBuffers(singleDelaySamples, interleavedSamples, 0, 1);
2294
2295 // Apply the master volume to the complete signal
2296 for (i = 0; i<outputSamples.length; i++) {
2297 outputSamples[i] *= this.masterVolume;
2298 }
2299
2300 return outputSamples;
2301};
2302
Note: See TracBrowser for help on using the repository browser.