source: gs3-extensions/web-audio/trunk/js-dsp/dsp.js.fixed-for-chrome

Last change on this file 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: 36.1 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// Setup arrays for platforms which do not support byte arrays
10Float32Array = (typeof Float32Array === 'undefined') ? Array : Float32Array;
11Float64Array = (typeof Float64Array === 'undefined') ? Array : Float64Array;
12Uint32Array = (typeof Uint32Array === 'undefined') ? Array : Uint32Array;
13
14////////////////////////////////////////////////////////////////////////////////
15// CONSTANTS //
16////////////////////////////////////////////////////////////////////////////////
17
18/**
19 * DSP is an object which contains general purpose utility functions and constants
20 */
21DSP = {
22 // Channels
23 LEFT: 0,
24 RIGHT: 1,
25 MIX: 2,
26
27 // Waveforms
28 SINE: 1,
29 TRIANGLE: 2,
30 SAW: 3,
31 SQUARE: 4,
32
33 // Filters
34 LOWPASS: 0,
35 HIGHPASS: 1,
36 BANDPASS: 2,
37 NOTCH: 3,
38
39 // Window functions
40 BARTLETT: 1,
41 BARTLETTHANN: 2,
42 BLACKMAN: 3,
43 COSINE: 4,
44 GAUSS: 5,
45 HAMMING: 6,
46 HANN: 7,
47 LANCZOS: 8,
48 RECTANGULAR: 9,
49 TRIANGULAR: 10,
50
51 // Math
52 TWO_PI: 2*Math.PI
53};
54
55////////////////////////////////////////////////////////////////////////////////
56// DSP UTILITY FUNCTIONS //
57////////////////////////////////////////////////////////////////////////////////
58
59/**
60 * Inverts the phase of a signal
61 *
62 * @param {Array} buffer A sample buffer
63 *
64 * @returns The inverted sample buffer
65 */
66DSP.invert = function(buffer) {
67 for ( var i = 0, len = buffer.length; i < len; i++ ) {
68 buffer[i] *= -1;
69 }
70
71 return buffer;
72};
73
74/**
75 * Converts split-stereo (dual mono) sample buffers into a stereo interleaved sample buffer
76 *
77 * @param {Array} left A sample buffer
78 * @param {Array} right A sample buffer
79 *
80 * @returns The stereo interleaved buffer
81 */
82DSP.interleave = function(left, right) {
83 if ( left.length !== right.length ) {
84 throw "Can not interleave. Channel lengths differ.";
85 }
86
87 var stereoInterleaved = new Float32Array(left.length * 2);
88
89 for (var i = 0, len = left.length; i < len; i++ ) {
90 stereoInterleaved[2*i] = left[i];
91 stereoInterleaved[2*i+1] = right[i];
92 }
93
94 return stereoInterleaved;
95};
96
97/**
98 * Converts a stereo-interleaved sample buffer into split-stereo (dual mono) sample buffers
99 *
100 * @param {Array} buffer A stereo-interleaved sample buffer
101 *
102 * @returns an Array containing left and right channels
103 */
104DSP.deinterleave = function(buffer) {
105 var left = new Float32Array(buffer.length/2);
106 var right = new Float32Array(buffer.length/2);
107 var mix = new Float32Array(buffer.length/2);
108
109 for (var i = 0, len = buffer.length/2; i < len; i ++ ) {
110 left[i] = buffer[2*i];
111 right[i] = buffer[2*i+1];
112 mix[i] = (left[i] + right[i]) / 2;
113 }
114
115 return [left, right, mix];
116};
117
118/**
119 * Separates a channel from a stereo-interleaved sample buffer
120 *
121 * @param {Array} buffer A stereo-interleaved sample buffer
122 * @param {Number} channel A channel constant (LEFT, RIGHT, MIX)
123 *
124 * @returns an Array containing a signal mono sample buffer
125 */
126DSP.getChannel = function(channel, buffer) {
127 return DSP.deinterleave(buffer)[channel];
128};
129
130// Biquad filter types
131DSP.LPF = 0; // H(s) = 1 / (s^2 + s/Q + 1)
132DSP.HPF = 1; // H(s) = s^2 / (s^2 + s/Q + 1)
133DSP.BPF_CONSTANT_SKIRT = 2; // H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q)
134DSP.BPF_CONSTANT_PEAK = 3; // H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain)
135DSP.NOTCH = 4; // H(s) = (s^2 + 1) / (s^2 + s/Q + 1)
136DSP.APF = 5; // H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1)
137DSP.PEAKING_EQ = 6; // H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
138DSP.LOW_SHELF = 7; // H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1)
139DSP.HIGH_SHELF = 8; // H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A)
140
141// Biquad filter parameter types
142DSP.Q = 0;
143DSP.BW = 1;
144DSP.S = 2;
145
146
147/**
148 * DFT is a class for calculating the Discrete Fourier Transform of a signal.
149 *
150 * @param {Number} bufferSize The size of the sample buffer to be computed
151 * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
152 *
153 * @constructor
154 */
155DFT = function(bufferSize, sampleRate) {
156 this.bufferSize = bufferSize;
157 this.sampleRate = sampleRate;
158
159 var N = bufferSize/2 * bufferSize;
160
161 this.sinTable = new Float32Array(N);
162 this.cosTable = new Float32Array(N);
163
164 for ( var i = 0; i < N; i++ ) {
165 this.sinTable[i] = Math.sin(i * DSP.TWO_PI / bufferSize);
166 this.cosTable[i] = Math.cos(i * DSP.TWO_PI / bufferSize);
167 }
168
169 this.spectrum = new Float32Array(bufferSize/2);
170 this.complexValues = new Float32Array(bufferSize/2);
171};
172
173/**
174 * Performs a forward tranform on the sample buffer.
175 * Converts a time domain signal to frequency domain spectra.
176 *
177 * @param {Array} buffer The sample buffer
178 *
179 * @returns The frequency spectrum array
180 */
181DFT.prototype.forward = function(buffer) {
182 var real, imag;
183
184 for ( var k = 0; k < this.bufferSize/2; k++ ) {
185 real = 0.0;
186 imag = 0.0;
187
188 for ( var n = 0; n < buffer.length; n++ ) {
189 real += this.cosTable[k*n] * signal[n];
190 imag += this.sinTable[k*n] * signal[n];
191 }
192
193 this.complexValues[k] = {real: real, imag: imag};
194 }
195
196 for ( var i = 0; i < this.bufferSize/2; i++ ) {
197 this.spectrum[i] = 2 * Math.sqrt(Math.pow(this.complexValues[i].real, 2) + Math.pow(this.complexValues[i].imag, 2)) / this.bufferSize;
198 }
199
200 return this.spectrum;
201};
202
203
204/**
205 * FFT is a class for calculating the Discrete Fourier Transform of a signal
206 * with the Fast Fourier Transform algorithm.
207 *
208 * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
209 * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
210 *
211 * @constructor
212 */
213FFT = function(bufferSize, sampleRate) {
214 this.bufferSize = bufferSize;
215 this.sampleRate = sampleRate;
216 this.spectrum = new Float32Array(bufferSize/2);
217 this.real = new Float32Array(bufferSize);
218 this.imag = new Float32Array(bufferSize);
219
220 this.reverseTable = new Uint32Array(bufferSize);
221
222 var limit = 1;
223 var bit = bufferSize >> 1;
224
225 while ( limit < bufferSize ) {
226 for ( var i = 0; i < limit; i++ ) {
227 this.reverseTable[i + limit] = this.reverseTable[i] + bit;
228 }
229
230 limit = limit << 1;
231 bit = bit >> 1;
232 }
233
234 this.sinTable = new Float32Array(bufferSize);
235 this.cosTable = new Float32Array(bufferSize);
236
237 for ( var i = 0; i < bufferSize; i++ ) {
238 this.sinTable[i] = Math.sin(-Math.PI/i);
239 this.cosTable[i] = Math.cos(-Math.PI/i);
240 }
241};
242
243/**
244 * Performs a forward tranform on the sample buffer.
245 * Converts a time domain signal to frequency domain spectra.
246 *
247 * @param {Array} buffer The sample buffer. Buffer Length must be power of 2
248 *
249 * @returns The frequency spectrum array
250 */
251FFT.prototype.forward = function(buffer) {
252 // Locally scope variables for speed up
253 var bufferSize = this.bufferSize,
254 cosTable = this.cosTable,
255 sinTable = this.sinTable,
256 reverseTable = this.reverseTable,
257 real = this.real,
258 imag = this.imag,
259 spectrum = this.spectrum;
260
261 if ( bufferSize % 2 !== 0 ) { throw "Invalid buffer size, must be a power of 2."; }
262 if ( bufferSize !== buffer.length ) { throw "Supplied buffer is not the same size as defined FFT. FFT Size: " + bufferSize + " Buffer Size: " + buffer.length; }
263
264 for ( var i = 0; i < bufferSize; i++ ) {
265 real[i] = buffer[reverseTable[i]];
266 imag[i] = 0;
267 }
268
269 var halfSize = 1,
270 phaseShiftStepReal,
271 phaseShiftStepImag,
272 currentPhaseShiftReal,
273 currentPhaseShiftImag,
274 off,
275 tr,
276 ti,
277 tmpReal,
278 i;
279
280 while ( halfSize < bufferSize ) {
281 phaseShiftStepReal = cosTable[halfSize];
282 phaseShiftStepImag = sinTable[halfSize];
283 currentPhaseShiftReal = 1;
284 currentPhaseShiftImag = 0;
285
286 for ( var fftStep = 0; fftStep < halfSize; fftStep++ ) {
287 i = fftStep;
288
289 while ( i < bufferSize ) {
290 off = i + halfSize;
291 tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
292 ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);
293
294 real[off] = real[i] - tr;
295 imag[off] = imag[i] - ti;
296 real[i] += tr;
297 imag[i] += ti;
298
299 i += halfSize << 1;
300 }
301
302 tmpReal = currentPhaseShiftReal;
303 currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
304 currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
305 }
306
307 halfSize = halfSize << 1;
308 }
309
310 i = bufferSize/2;
311 while(i--) {
312 spectrum[i] = 2 * Math.sqrt(real[i] * real[i] + imag[i] * imag[i]) / bufferSize;
313 }
314};
315
316/**
317 * Oscillator class for generating and modifying signals
318 *
319 * @param {Number} type A waveform constant (eg. DSP.SINE)
320 * @param {Number} frequency Initial frequency of the signal
321 * @param {Number} amplitude Initial amplitude of the signal
322 * @param {Number} bufferSize Size of the sample buffer to generate
323 * @param {Number} sampleRate The sample rate of the signal
324 *
325 * @contructor
326 */
327Oscillator = function Oscillator(type, frequency, amplitude, bufferSize, sampleRate) {
328 this.frequency = frequency;
329 this.amplitude = amplitude;
330 this.bufferSize = bufferSize;
331 this.sampleRate = sampleRate;
332 //this.pulseWidth = pulseWidth;
333 this.frameCount = 0;
334
335 this.waveTableLength = 2048;
336
337 this.cyclesPerSample = frequency / sampleRate;
338
339 this.signal = new Float32Array(bufferSize);
340 this.envelope = null;
341
342
343 switch(parseInt(type)) {
344 case DSP.TRIANGLE:
345 this.func = Oscillator.Triangle;
346 break;
347
348 case DSP.SAW:
349 this.func = Oscillator.Saw;
350 break;
351
352 case DSP.SQUARE:
353 this.func = Oscillator.Square;
354 break;
355
356 case DSP.SINE:
357 default:
358 this.func = Oscillator.Sine;
359 break;
360 }
361
362 this.generateWaveTable = function() {
363 Oscillator.waveTable[this.func] = new Float32Array(2048);
364 var waveTableTime = this.waveTableLength / this.sampleRate;
365 var waveTableHz = 1 / waveTableTime;
366
367 for (var i = 0; i < this.waveTableLength; i++) {
368 Oscillator.waveTable[this.func][i] = this.func(i * waveTableHz/this.sampleRate);
369 }
370 };
371
372 if ( typeof Oscillator.waveTable === 'undefined' ) {
373 Oscillator.waveTable = {};
374 }
375
376 if ( typeof Oscillator.waveTable[this.func] === 'undefined' ) {
377 this.generateWaveTable();
378 }
379
380 this.waveTable = Oscillator.waveTable[this.func];
381};
382
383/**
384 * Set the amplitude of the signal
385 *
386 * @param {Number} amplitude The amplitude of the signal (between 0 and 1)
387 */
388Oscillator.prototype.setAmp = function(amplitude) {
389 if (amplitude >= 0 && amplitude <= 1) {
390 this.amplitude = amplitude;
391 } else {
392 throw "Amplitude out of range (0..1).";
393 }
394};
395
396/**
397 * Set the frequency of the signal
398 *
399 * @param {Number} frequency The frequency of the signal
400 */
401Oscillator.prototype.setFreq = function(frequency) {
402 this.frequency = frequency;
403 this.cyclesPerSample = frequency / this.sampleRate;
404};
405
406// Add an oscillator
407Oscillator.prototype.add = function(oscillator) {
408 for ( var i = 0; i < this.bufferSize; i++ ) {
409 //this.signal[i] += oscillator.valueAt(i);
410 this.signal[i] += oscillator.signal[i];
411 }
412
413 return this.signal;
414};
415
416// Add a signal to the current generated osc signal
417Oscillator.prototype.addSignal = function(signal) {
418 for ( var i = 0; i < signal.length; i++ ) {
419 if ( i >= this.bufferSize ) {
420 break;
421 }
422 this.signal[i] += signal[i];
423
424 /*
425 // Constrain amplitude
426 if ( this.signal[i] > 1 ) {
427 this.signal[i] = 1;
428 } else if ( this.signal[i] < -1 ) {
429 this.signal[i] = -1;
430 }
431 */
432 }
433 return this.signal;
434};
435
436// Add an envelope to the oscillator
437Oscillator.prototype.addEnvelope = function(envelope) {
438 this.envelope = envelope;
439};
440
441Oscillator.prototype.valueAt = function(offset) {
442 return this.waveTable[offset % this.waveTableLength];
443};
444
445Oscillator.prototype.generate = function() {
446 var frameOffset = this.frameCount * this.bufferSize;
447 var step = this.waveTableLength * this.frequency / this.sampleRate;
448 var offset;
449
450 for ( var i = 0; i < this.bufferSize; i++ ) {
451 //var step = (frameOffset + i) * this.cyclesPerSample % 1;
452 //this.signal[i] = this.func(step) * this.amplitude;
453 //this.signal[i] = this.valueAt(Math.round((frameOffset + i) * step)) * this.amplitude;
454 offset = Math.round((frameOffset + i) * step);
455 this.signal[i] = this.waveTable[offset % this.waveTableLength] * this.amplitude;
456 }
457
458 this.frameCount++;
459
460 return this.signal;
461};
462
463Oscillator.Sine = function(step) {
464 return Math.sin(DSP.TWO_PI * step);
465};
466
467Oscillator.Square = function(step) {
468 return step < 0.5 ? 1 : -1;
469};
470
471Oscillator.Saw = function(step) {
472 return 2 * (step - Math.round(step));
473};
474
475Oscillator.Triangle = function(step) {
476 return 1 - 4 * Math.abs(Math.round(step) - step);
477};
478
479Oscillator.Pulse = function(step) {
480 // stub
481};
482
483ADSR = function(attackLength, decayLength, sustainLevel, sustainLength, releaseLength, sampleRate) {
484 this.attackLength = attackLength;
485 this.decayLength = decayLength;
486 this.sustainLevel = sustainLevel;
487 this.sustainLength = sustainLength;
488 this.releaseLength = releaseLength;
489 this.sampleRate = sampleRate;
490
491 this.attackSamples = attackLength * sampleRate;
492 this.decaySamples = decayLength * sampleRate;
493 this.sustainSamples = sustainLength * sampleRate;
494 this.releaseSamples = releaseLength * sampleRate;
495
496 this.attack = this.attackSamples;
497 this.decay = this.attack + this.decaySamples;
498 this.sustain = this.decay + this.sustainSamples;
499 this.release = this.sustain + this.releaseSamples;
500
501 this.samplesProcessed = 0;
502};
503
504
505ADSR.prototype.trigger = function() {
506 this.samplesProcessed = 0;
507};
508
509ADSR.prototype.processSample = function(sample) {
510 var amplitude = 0;
511
512 if ( this.samplesProcessed <= this.attack ) {
513 amplitude = 0 + (1 - 0) * ((this.samplesProcessed - 0) / (this.attack - 0));
514 } else if ( this.samplesProcessed > this.attack && this.samplesProcessed <= this.decay ) {
515 amplitude = 1 + (this.sustainLevel - 1) * ((this.samplesProcessed - this.attack) / (this.decay - this.attack));
516 } else if ( this.samplesProcessed > this.decay && this.samplesProcessed <= this.sustain ) {
517 amplitude = this.sustainLevel;
518 } else if ( this.samplesProcessed > this.sustain && this.samplesProcessed <= this.release ) {
519 amplitude = this.sustainLevel + (0 - this.sustainLevel) * ((this.samplesProcessed - this.sustain) / (this.release - this.sustain));
520 }
521
522 return sample * amplitude;
523};
524
525ADSR.prototype.value = function() {
526 var amplitude = 0;
527
528 if ( this.samplesProcessed <= this.attack ) {
529 amplitude = 0 + (1 - 0) * ((this.samplesProcessed - 0) / (this.attack - 0));
530 } else if ( this.samplesProcessed > this.attack && this.samplesProcessed <= this.decay ) {
531 amplitude = 1 + (this.sustainLevel - 1) * ((this.samplesProcessed - this.attack) / (this.decay - this.attack));
532 } else if ( this.samplesProcessed > this.decay && this.samplesProcessed <= this.sustain ) {
533 amplitude = this.sustainLevel;
534 } else if ( this.samplesProcessed > this.sustain && this.samplesProcessed <= this.release ) {
535 amplitude = this.sustainLevel + (0 - this.sustainLevel) * ((this.samplesProcessed - this.sustain) / (this.release - this.sustain));
536 }
537
538 return amplitude;
539};
540
541ADSR.prototype.process = function(buffer) {
542 for ( var i = 0; i < buffer.length; i++ ) {
543 /*
544 var amplitude = 0;
545
546 if ( this.samplesProcessed <= this.attack ) {
547 amplitude = 0 + (1 - 0) * ((this.samplesProcessed - 0) / (this.attack - 0));
548 } else if ( this.samplesProcessed > this.attack && this.samplesProcessed <= this.decay ) {
549 amplitude = 1 + (this.sustainLevel - 1) * ((this.samplesProcessed - this.attack) / (this.decay - this.attack));
550 } else if ( this.samplesProcessed > this.decay && this.samplesProcessed <= this.sustain ) {
551 amplitude = this.sustainLevel;
552 } else if ( this.samplesProcessed > this.sustain && this.samplesProcessed <= this.release ) {
553 amplitude = this.sustainLevel + (0 - this.sustainLevel) * ((this.samplesProcessed - this.sustain) / (this.release - this.sustain));
554 }
555
556 buffer[i] *= amplitude;
557
558 this.samplesProcessed++;
559 */
560
561 buffer[i] *= this.value();
562
563 this.samplesProcessed++;
564 }
565
566 return buffer;
567};
568
569
570ADSR.prototype.isActive = function() {
571 if ( this.samplesProcessed > this.release ) {
572 return false;
573 } else {
574 return true;
575 }
576};
577
578IIRFilter = function(type, cutoff, resonance, sampleRate) {
579 this.sampleRate = sampleRate;
580 this.cutoff = cutoff;
581 this.resonance = resonance;
582
583 switch(type) {
584 case DSP.LOWPASS:
585 case DSP.LP12:
586 this.func = new IIRFilter.LP12(cutoff, resonance, sampleRate);
587 break;
588 }
589}
590
591IIRFilter.prototype.set = function(cutoff, resonance) {
592 this.func.calcCoeff(cutoff, resonance);
593}
594
595IIRFilter.prototype.process = function(buffer) {
596 this.func.process(buffer);
597}
598
599// Add an envelope to the filter
600IIRFilter.prototype.addEnvelope = function(envelope) {
601 if ( envelope instanceof ADSR ) {
602 this.func.addEnvelope(envelope);
603 } else {
604 throw "Not an envelope.";
605 }
606};
607
608IIRFilter.LP12 = function(cutoff, resonance, sampleRate) {
609 this.sampleRate = sampleRate;
610 this.vibraPos = 0;
611 this.vibraSpeed = 0;
612 this.envelope = false;
613
614 this.calcCoeff = function(cutoff, resonance) {
615 this.w = 2.0 * Math.PI * cutoff / this.sampleRate;
616 this.q = 1.0 - this.w / (2.0 * (resonance + 0.5 / (1.0 + this.w)) + this.w - 2.0);
617 this.r = this.q * this.q;
618 this.c = this.r + 1.0 - 2.0 * Math.cos(this.w) * this.q;
619
620 this.cutoff = cutoff;
621 this.resonance = resonance;
622 };
623
624 this.calcCoeff(cutoff, resonance);
625
626 this.process = function(buffer) {
627 for ( var i = 0; i < buffer.length; i++ ) {
628 this.vibraSpeed += (buffer[i] - this.vibraPos) * this.c;
629 this.vibraPos += this.vibraSpeed;
630 this.vibraSpeed *= this.r;
631
632 /*
633 var temp = this.vibraPos;
634
635 if ( temp > 1.0 ) {
636 temp = 1.0;
637 } else if ( temp < -1.0 ) {
638 temp = -1.0;
639 } else if ( temp != temp ) {
640 temp = 1;
641 }
642
643 buffer[i] = temp;
644 */
645
646 if (this.envelope) {
647 buffer[i] = (buffer[i] * (1 - this.envelope.value())) + (this.vibraPos * this.envelope.value());
648 this.envelope.samplesProcessed++;
649 } else {
650 buffer[i] = this.vibraPos;
651 }
652 }
653 }
654};
655
656IIRFilter.LP12.prototype.addEnvelope = function(envelope) {
657 this.envelope = envelope;
658};
659
660IIRFilter2 = function(type, cutoff, resonance, sampleRate) {
661 this.type = type;
662 this.cutoff = cutoff;
663 this.resonance = resonance;
664 this.sampleRate = sampleRate;
665
666 this.calcCoeff = function(cutoff, resonance) {
667 this.freq = 2 * Math.sin(Math.PI * Math.min(0.25, cutoff/(this.sampleRate*2)));
668 this.damp = Math.min(2 * (1 - Math.pow(resonance, 0.25)), Math.min(2, 2/this.freq - this.freq * 0.5));
669 };
670
671 this.calcCoeff(cutoff, resonance);
672};
673
674IIRFilter2.prototype.process = function(buffer) {
675 var input, output, lp, hp, bp, br;
676
677 var f = Array(4);
678 f[0] = 0; // lp
679 f[1] = 0; // hp
680 f[2] = 0; // bp
681 f[3] = 0; // br
682
683 for ( var i = 0; i < buffer.length; i++ ) {
684 input = buffer[i];
685
686 // first pass
687 f[3] = input - this.damp * f[2];
688 f[0] = f[0] + this.freq * f[2];
689 f[1] = f[3] - f[0];
690 f[2] = this.freq * f[1] + f[2];
691 output = 0.5 * f[this.type];
692
693 // second pass
694 f[3] = input - this.damp * f[2];
695 f[0] = f[0] + this.freq * f[2];
696 f[1] = f[3] - f[0];
697 f[2] = this.freq * f[1] + f[2];
698 output += 0.5 * f[this.type];
699
700 if (this.envelope) {
701 buffer[i] = (buffer[i] * (1 - this.envelope.value())) + (output * this.envelope.value());
702 this.envelope.samplesProcessed++;
703 } else {
704 buffer[i] = output;
705 }
706 }
707};
708
709IIRFilter2.prototype.addEnvelope = function(envelope) {
710 if ( envelope instanceof ADSR ) {
711 this.envelope = envelope;
712 } else {
713 throw "This is not an envelope.";
714 }
715};
716
717IIRFilter2.prototype.set = function(cutoff, resonance) {
718 this.calcCoeff(cutoff, resonance);
719};
720
721WindowFunction = function(type, alpha) {
722 this.alpha = alpha;
723
724 switch(type) {
725 case DSP.BARTLETT:
726 this.func = WindowFunction.Bartlett;
727 break;
728
729 case DSP.BARTLETTHANN:
730 this.func = WindowFunction.BartlettHann;
731 break;
732
733 case DSP.BLACKMAN:
734 this.func = WindowFunction.Blackman;
735 this.alpha = this.alpha || 0.16;
736 break;
737
738 case DSP.COSINE:
739 this.func = WindowFunction.Cosine;
740 break;
741
742 case DSP.GAUSS:
743 this.func = WindowFunction.Gauss;
744 this.alpha = this.alpha || 0.25;
745 break;
746
747 case DSP.HAMMING:
748 this.func = WindowFunction.Hamming;
749 break;
750
751 case DSP.HANN:
752 this.func = WindowFunction.Hann;
753 break;
754
755 case DSP.LANCZOS:
756 this.func = WindowFunction.Lanczoz;
757 break;
758
759 case DSP.RECTANGULAR:
760 this.func = WindowFunction.Rectangular;
761 break;
762
763 case DSP.TRIANGULAR:
764 this.func = WindowFunction.Triangular;
765 break;
766 }
767};
768
769WindowFunction.prototype.process = function(buffer) {
770 var length = buffer.length;
771 for ( var i = 0; i < length; i++ ) {
772 buffer[i] *= this.func(length, i, this.alpha);
773 }
774};
775
776WindowFunction.Bartlett = function(length, index) {
777 return 2 / (length - 1) * ((length - 1) / 2 - Math.abs(index - (length - 1) / 2));
778};
779
780WindowFunction.BartlettHann = function(length, index) {
781 return 0.62 - 0.48 * Math.abs(index / (length - 1) - 0.5) - 0.38 * Math.cos(DSP.TWO_PI * index / (length - 1));
782};
783
784WindowFunction.Blackman = function(length, index, alpha) {
785 var a0 = (1 - alpha) / 2;
786 var a1 = 0.5;
787 var a2 = alpha / 2;
788
789 return a0 - a1 * Math.cos(DSP.TWO_PI * index / (length - 1)) + a2 * Math.cos(4 * Math.PI * index / (length - 1));
790};
791
792WindowFunction.Cosine = function(length, index) {
793 return Math.cos(Math.PI * index / (length - 1) - Math.PI / 2);
794};
795
796WindowFunction.Gauss = function(length, index, alpha) {
797 return Math.pow(Math.E, -0.5 * Math.pow((index - (length - 1) / 2) / (alpha * (length - 1) / 2), 2));
798};
799
800WindowFunction.Hamming = function(length, index) {
801 return 0.54 - 0.46 * Math.cos(DSP.TWO_PI * index / (length - 1));
802};
803
804WindowFunction.Hann = function(length, index) {
805 return 0.5 * (1 - Math.cos(DSP.TWO_PI * index / (length - 1)));
806};
807
808WindowFunction.Lanczos = function(length, index) {
809 var x = 2 * index / (length - 1) - 1;
810 return Math.sin(Math.PI * x) / (Math.PI * x);
811};
812
813WindowFunction.Rectangular = function(length, index) {
814 return 1;
815};
816
817WindowFunction.Triangular = function(length, index) {
818 return 2 / length * (length / 2 - Math.abs(index - (length - 1) / 2));
819};
820
821function sinh (arg) {
822 // Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2
823 //
824 // version: 1004.2314
825 // discuss at: http://phpjs.org/functions/sinh // + original by: Onno Marsman
826 // * example 1: sinh(-0.9834330348825909);
827 // * returns 1: -1.1497971402636502
828 return (Math.exp(arg) - Math.exp(-arg))/2;
829}
830
831
832/*
833 * Biquad filter
834 *
835 * Created by Ricard Marxer <[email protected]> on 2010-05-23.
836 * Copyright 2010 Ricard Marxer. All rights reserved.
837 *
838 */
839// Implementation based on:
840// http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
841Biquad = function(type, sampleRate) {
842 this.Fs = sampleRate;
843 this.type = type; // type of the filter
844 this.parameterType = DSP.Q; // type of the parameter
845
846 this.x_1_l = 0;
847 this.x_2_l = 0;
848 this.y_1_l = 0;
849 this.y_2_l = 0;
850
851 this.x_1_r = 0;
852 this.x_2_r = 0;
853 this.y_1_r = 0;
854 this.y_2_r = 0;
855
856 this.b0 = 1;
857 this.a0 = 1;
858
859 this.b1 = 0;
860 this.a1 = 0;
861
862 this.b2 = 0;
863 this.a2 = 0;
864
865 this.b0a0 = this.b0 / this.a0;
866 this.b1a0 = this.b1 / this.a0;
867 this.b2a0 = this.b2 / this.a0;
868 this.a1a0 = this.a1 / this.a0;
869 this.a2a0 = this.a2 / this.a0;
870
871 this.f0 = 3000; // "wherever it's happenin', man." Center Frequency or
872 // Corner Frequency, or shelf midpoint frequency, depending
873 // on which filter type. The "significant frequency".
874
875 this.dBgain = 12; // used only for peaking and shelving filters
876
877 this.Q = 1; // the EE kind of definition, except for peakingEQ in which A*Q is
878 // the classic EE Q. That adjustment in definition was made so that
879 // a boost of N dB followed by a cut of N dB for identical Q and
880 // f0/Fs results in a precisely flat unity gain filter or "wire".
881
882 this.BW = -3; // the bandwidth in octaves (between -3 dB frequencies for BPF
883 // and notch or between midpoint (dBgain/2) gain frequencies for
884 // peaking EQ
885
886 this.S = 1; // a "shelf slope" parameter (for shelving EQ only). When S = 1,
887 // the shelf slope is as steep as it can be and remain monotonically
888 // increasing or decreasing gain with frequency. The shelf slope, in
889 // dB/octave, remains proportional to S for all other values for a
890 // fixed f0/Fs and dBgain.
891
892 this.coefficients = function() {
893 var b = [this.b0, this.b1, this.b2];
894 var a = [this.a0, this.a1, this.a2];
895 return {b: b, a:a};
896 }
897
898 this.setFilterType = function(type) {
899 this.type = type;
900 this.recalculateCoefficients();
901 }
902
903 this.setSampleRate = function(rate) {
904 this.Fs = rate;
905 this.recalculateCoefficients();
906 }
907
908 this.setQ = function(q) {
909 this.parameterType = DSP.Q;
910 this.Q = Math.max(Math.min(q, 115.0), 0.001);
911 this.recalculateCoefficients();
912 }
913
914 this.setBW = function(bw) {
915 this.parameterType = DSP.BW;
916 this.BW = bw;
917 this.recalculateCoefficients();
918 }
919
920 this.setS = function(s) {
921 this.parameterType = DSP.S;
922 this.S = Math.max(Math.min(s, 5.0), 0.0001);
923 this.recalculateCoefficients();
924 }
925
926 this.setF0 = function(freq) {
927 this.f0 = freq;
928 this.recalculateCoefficients();
929 }
930
931 this.setDbGain = function(g) {
932 this.dBgain = g;
933 this.recalculateCoefficients();
934 }
935
936 this.recalculateCoefficients = function() {
937 var A;
938 if (type == DSP.PEAKING_EQ || type == DSP.LOW_SHELF || type == DSP.HIGH_SHELF ) {
939 A = Math.pow(10, (this.dBgain/40)); // for peaking and shelving EQ filters only
940 } else {
941 A = Math.sqrt( Math.pow(10, (this.dBgain/20)) );
942 }
943
944 var w0 = DSP.TWO_PI * this.f0 / this.Fs;
945
946 var cosw0 = Math.cos(w0);
947 var sinw0 = Math.sin(w0);
948
949 var alpha = 0;
950
951 switch (this.parameterType) {
952 case DSP.Q:
953 alpha = sinw0/(2*this.Q);
954 break;
955
956 case DSP.BW:
957 alpha = sinw0 * sinh( Math.LN2/2 * this.BW * w0/sinw0 );
958 break;
959
960 case DSP.S:
961 alpha = sinw0/2 * Math.sqrt( (A + 1/A)*(1/this.S - 1) + 2 );
962 break;
963 }
964
965 /**
966 FYI: The relationship between bandwidth and Q is
967 1/Q = 2*sinh(ln(2)/2*BW*w0/sin(w0)) (digital filter w BLT)
968 or 1/Q = 2*sinh(ln(2)/2*BW) (analog filter prototype)
969
970 The relationship between shelf slope and Q is
971 1/Q = sqrt((A + 1/A)*(1/S - 1) + 2)
972 */
973
974 switch (this.type) {
975 case DSP.LPF: // H(s) = 1 / (s^2 + s/Q + 1)
976 this.b0 = (1 - cosw0)/2;
977 this.b1 = 1 - cosw0;
978 this.b2 = (1 - cosw0)/2;
979 this.a0 = 1 + alpha;
980 this.a1 = -2 * cosw0;
981 this.a2 = 1 - alpha;
982 break;
983
984 case DSP.HPF: // H(s) = s^2 / (s^2 + s/Q + 1)
985 this.b0 = (1 + cosw0)/2;
986 this.b1 = -(1 + cosw0);
987 this.b2 = (1 + cosw0)/2;
988 this.a0 = 1 + alpha;
989 this.a1 = -2 * cosw0;
990 this.a2 = 1 - alpha;
991 break;
992
993 case DSP.BPF_CONSTANT_SKIRT: // H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q)
994 this.b0 = sinw0/2;
995 this.b1 = 0;
996 this.b2 = -sinw0/2;
997 this.a0 = 1 + alpha;
998 this.a1 = -2*cosw0;
999 this.a2 = 1 - alpha;
1000 break;
1001
1002 case DSP.BPF_CONSTANT_PEAK: // H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain)
1003 this.b0 = alpha;
1004 this.b1 = 0;
1005 this.b2 = -alpha;
1006 this.a0 = 1 + alpha;
1007 this.a1 = -2*cosw0;
1008 this.a2 = 1 - alpha;
1009 break;
1010
1011 case DSP.NOTCH: // H(s) = (s^2 + 1) / (s^2 + s/Q + 1)
1012 this.b0 = 1;
1013 this.b1 = -2*cosw0;
1014 this.b2 = 1;
1015 this.a0 = 1 + alpha;
1016 this.a1 = -2*cosw0;
1017 this.a2 = 1 - alpha;
1018 break;
1019
1020 case DSP.APF: // H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1)
1021 this.b0 = 1 - alpha;
1022 this.b1 = -2*cosw0;
1023 this.b2 = 1 + alpha;
1024 this.a0 = 1 + alpha;
1025 this.a1 = -2*cosw0;
1026 this.a2 = 1 - alpha;
1027 break;
1028
1029 case DSP.PEAKING_EQ: // H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
1030 this.b0 = 1 + alpha*A;
1031 this.b1 = -2*cosw0;
1032 this.b2 = 1 - alpha*A;
1033 this.a0 = 1 + alpha/A;
1034 this.a1 = -2*cosw0;
1035 this.a2 = 1 - alpha/A;
1036 break;
1037
1038 case DSP.LOW_SHELF: // H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1)
1039 var coeff = sinw0 * Math.sqrt( (A^2 + 1)*(1/this.S - 1) + 2*A );
1040 this.b0 = A*( (A+1) - (A-1)*cosw0 + coeff );
1041 this.b1 = 2*A*( (A-1) - (A+1)*cosw0 );
1042 this.b2 = A*( (A+1) - (A-1)*cosw0 - coeff );
1043 this.a0 = (A+1) + (A-1)*cosw0 + coeff;
1044 this.a1 = -2*( (A-1) + (A+1)*cosw0 );
1045 this.a2 = (A+1) + (A-1)*cosw0 - coeff;
1046 break;
1047
1048 case DSP.HIGH_SHELF: // H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A)
1049 var coeff = sinw0 * Math.sqrt( (A^2 + 1)*(1/this.S - 1) + 2*A );
1050 this.b0 = A*( (A+1) + (A-1)*cosw0 + coeff );
1051 this.b1 = -2*A*( (A-1) + (A+1)*cosw0 );
1052 this.b2 = A*( (A+1) + (A-1)*cosw0 - coeff );
1053 this.a0 = (A+1) - (A-1)*cosw0 + coeff;
1054 this.a1 = 2*( (A-1) - (A+1)*cosw0 );
1055 this.a2 = (A+1) - (A-1)*cosw0 - coeff;
1056 break;
1057 }
1058
1059 this.b0a0 = this.b0/this.a0;
1060 this.b1a0 = this.b1/this.a0;
1061 this.b2a0 = this.b2/this.a0;
1062 this.a1a0 = this.a1/this.a0;
1063 this.a2a0 = this.a2/this.a0;
1064 }
1065
1066 this.process = function(buffer) {
1067 //y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2]
1068 // - (a1/a0)*y[n-1] - (a2/a0)*y[n-2]
1069
1070 var len = buffer.length;
1071 var output = new Float32Array(len);
1072
1073 for ( var i=0; i<buffer.length; i++ ) {
1074 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;
1075 this.y_2_l = this.y_1_l;
1076 this.y_1_l = output[i];
1077 this.x_2_l = this.x_1_l;
1078 this.x_1_l = buffer[i];
1079 }
1080
1081 return output;
1082 }
1083
1084 this.processStereo = function(buffer) {
1085 //y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2]
1086 // - (a1/a0)*y[n-1] - (a2/a0)*y[n-2]
1087
1088 var len = buffer.length;
1089 var output = new Float32Array(len);
1090
1091 for ( var i=0; i<len/2; i++ ) {
1092 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;
1093 this.y_2_l = this.y_1_l;
1094 this.y_1_l = output[2*i];
1095 this.x_2_l = this.x_1_l;
1096 this.x_1_l = buffer[2*i];
1097
1098 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;
1099 this.y_2_r = this.y_1_r;
1100 this.y_1_r = output[2*i+1];
1101 this.x_2_r = this.x_1_r;
1102 this.x_1_r = buffer[2*i+1];
1103 }
1104
1105 return output;
1106 }
1107};
1108
1109
1110/*
1111 * Magnitude to decibels
1112 *
1113 * Created by Ricard Marxer <[email protected]> on 2010-05-23.
1114 * Copyright 2010 Ricard Marxer. All rights reserved.
1115 *
1116 * @buffer array of magnitudes to convert to decibels
1117 *
1118 * @returns the array in decibels
1119 *
1120 */
1121DSP.mag2db = function(buffer) {
1122 var minDb = -120;
1123 var minMag = Math.pow(10.0, minDb / 20.0);
1124
1125 var log = Math.log;
1126 var max = Math.max;
1127
1128 var result = new Float32Array(buffer.length);
1129 for (var i=0; i<buffer.length; i++) {
1130 result[i] = 20.0*log(max(buffer[i], minMag));
1131 }
1132
1133 return result;
1134};
1135
1136/*
1137 * Frequency response
1138 *
1139 * Created by Ricard Marxer <[email protected]> on 2010-05-23.
1140 * Copyright 2010 Ricard Marxer. All rights reserved.
1141 *
1142 * Calculates the frequency response at the given points.
1143 *
1144 * @b b coefficients of the filter
1145 * @a a coefficients of the filter
1146 * @w w points (normally between -PI and PI) where to calculate the frequency response
1147 *
1148 * @returns the frequency response in magnitude
1149 *
1150 */
1151DSP.freqz = function(b, a, w) {
1152 if (!w) {
1153 w = new Float32Array(200);
1154 for (var i=0;i<w.length; i++) {
1155 w[i] = DSP.TWO_PI/w.length * i - Math.PI;
1156 }
1157 }
1158
1159 var result = new Float32Array(w.length);
1160
1161 var sqrt = Math.sqrt;
1162 var cos = Math.cos;
1163 var sin = Math.sin;
1164
1165 for (var i=0; i<w.length; i++) {
1166 var numerator = {real:0.0, imag:0.0};
1167 for (var j=0; j<b.length; j++) {
1168 numerator.real += b[j] * cos(-j*w[i]);
1169 numerator.imag += b[j] * sin(-j*w[i]);
1170 }
1171
1172 var denominator = {real:0.0, imag:0.0};
1173 for (var j=0; j<a.length; j++) {
1174 denominator.real += a[j] * cos(-j*w[i]);
1175 denominator.imag += a[j] * sin(-j*w[i]);
1176 }
1177
1178 result[i] = sqrt(numerator.real*numerator.real + numerator.imag*numerator.imag) / sqrt(denominator.real*denominator.real + denominator.imag*denominator.imag);
1179 }
1180
1181 return result;
1182};
1183
1184/*
1185 * Graphical Equalizer
1186 *
1187 * Created by Ricard Marxer <[email protected]> on 2010-05-23.
1188 * Copyright 2010 Ricard Marxer. All rights reserved.
1189 *
1190 */
1191// Implementation of a graphic equalizer with a configurable bands-per-octave
1192// and minimum and maximum frequencies
1193GraphicalEq = function(sampleRate) {
1194 this.FS = sampleRate;
1195 this.minFreq = 40.0;
1196 this.maxFreq = 16000.0;
1197
1198 this.bandsPerOctave = 1.0;
1199
1200 this.filters = []
1201 this.freqzs = []
1202
1203 this.calculateFreqzs = true;
1204
1205 this.recalculateFilters = function() {
1206 var bandCount = Math.round(Math.log(this.maxFreq/this.minFreq) * this.bandsPerOctave/ Math.LN2);
1207
1208 this.filters = [];
1209 for (var i=0; i<bandCount; i++) {
1210 var freq = this.minFreq*(Math.pow(2, i/this.bandsPerOctave));
1211 var newFilter = new Biquad(DSP.PEAKING_EQ, this.FS);
1212 newFilter.setDbGain(0);
1213 newFilter.setBW(1/this.bandsPerOctave);
1214 newFilter.setF0(freq);
1215 this.filters[i] = newFilter;
1216 this.recalculateFreqz(i);
1217 }
1218 }
1219
1220 this.setMinimumFrequency = function(freq) {
1221 this.minFreq = freq;
1222 this.recalculateFilters();
1223 }
1224
1225 this.setMaximumFrequency = function(freq) {
1226 this.maxFreq = freq;
1227 this.recalculateFilters();
1228 }
1229
1230 this.setBandsPerOctave = function(bands) {
1231 this.bandsPerOctave = bands;
1232 this.recalculateFilters();
1233 }
1234
1235 this.setBandGain = function(bandIndex, gain) {
1236 if (bandIndex < 0 || bandIndex > (this.filters.length-1)) {
1237 throw "The band index of the graphical equalizer is out of bounds.";
1238 return;
1239 }
1240
1241 if (!gain) {
1242 throw "A gain must be passed."
1243 return;
1244 }
1245
1246
1247 this.filters[bandIndex].setDbGain(gain);
1248 this.recalculateFreqz(bandIndex);
1249 }
1250
1251 this.recalculateFreqz = function(bandIndex) {
1252 if (!this.calculateFreqzs) {
1253 return;
1254 }
1255
1256
1257 if (bandIndex < 0 || bandIndex > (this.filters.length-1)) {
1258 throw "The band index of the graphical equalizer is out of bounds. " + bandIndex + " is out of [" + 0 + ", " + this.filters.length-1 + "]"
1259 return;
1260 }
1261
1262 if (!this.w) {
1263 this.w = new Float32Array(400);
1264 for (var i=0; i<this.w.length; i++) {
1265 this.w[i] = Math.PI/this.w.length * i;
1266 }
1267 }
1268
1269 var b = [this.filters[bandIndex].b0, this.filters[bandIndex].b1, this.filters[bandIndex].b2];
1270 var a = [this.filters[bandIndex].a0, this.filters[bandIndex].a1, this.filters[bandIndex].a2];
1271
1272 this.freqzs[bandIndex] = DSP.mag2db(DSP.freqz(b, a, this.w));
1273 }
1274
1275 this.process = function(buffer) {
1276 var output = buffer;
1277
1278 for ( var i=0; i<this.filters.length; i++ ) {
1279 output = this.filters[i].process(output);
1280 }
1281
1282 return output;
1283 }
1284
1285 this.processStereo = function(buffer) {
1286 var output = buffer;
1287
1288 for ( var i=0; i<this.filters.length; i++ ) {
1289 output = this.filters[i].processStereo(output);
1290 }
1291
1292 return output;
1293 }
1294
1295}
Note: See TracBrowser for help on using the repository browser.