123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322 |
- /*
- * DSP.js - a comprehensive digital signal processing library for javascript
- *
- * Created by Corban Brook <corbanbrook@gmail.com> on 2010-01-01.
- * Copyright 2010 Corban Brook. All rights reserved.
- *
- */
- ////////////////////////////////////////////////////////////////////////////////
- // CONSTANTS //
- ////////////////////////////////////////////////////////////////////////////////
- /**
- * DSP is an object which contains general purpose utility functions and constants
- */
- var DSP = {
- // Channels
- LEFT: 0,
- RIGHT: 1,
- MIX: 2,
- // Waveforms
- SINE: 1,
- TRIANGLE: 2,
- SAW: 3,
- SQUARE: 4,
- // Filters
- LOWPASS: 0,
- HIGHPASS: 1,
- BANDPASS: 2,
- NOTCH: 3,
- // Window functions
- BARTLETT: 1,
- BARTLETTHANN: 2,
- BLACKMAN: 3,
- COSINE: 4,
- GAUSS: 5,
- HAMMING: 6,
- HANN: 7,
- LANCZOS: 8,
- RECTANGULAR: 9,
- TRIANGULAR: 10,
- // Loop modes
- OFF: 0,
- FW: 1,
- BW: 2,
- FWBW: 3,
- // Math
- TWO_PI: 2*Math.PI
- };
- // Setup arrays for platforms which do not support byte arrays
- function setupTypedArray(name, fallback) {
- // check if TypedArray exists
- // typeof on Minefield and Chrome return function, typeof on Webkit returns object.
- if (typeof this[name] !== "function" && typeof this[name] !== "object") {
- // nope.. check if WebGLArray exists
- if (typeof this[fallback] === "function" && typeof this[fallback] !== "object") {
- this[name] = this[fallback];
- } else {
- // nope.. set as Native JS array
- this[name] = function(obj) {
- if (obj instanceof Array) {
- return obj;
- } else if (typeof obj === "number") {
- return new Array(obj);
- }
- };
- }
- }
- }
- setupTypedArray("Float64Array", "WebGLFloatArray");
- setupTypedArray("Int32Array", "WebGLIntArray");
- setupTypedArray("Uint16Array", "WebGLUnsignedShortArray");
- setupTypedArray("Uint8Array", "WebGLUnsignedByteArray");
- ////////////////////////////////////////////////////////////////////////////////
- // DSP UTILITY FUNCTIONS //
- ////////////////////////////////////////////////////////////////////////////////
- /**
- * Inverts the phase of a signal
- *
- * @param {Array} buffer A sample buffer
- *
- * @returns The inverted sample buffer
- */
- DSP.invert = function(buffer) {
- for (var i = 0, len = buffer.length; i < len; i++) {
- buffer[i] *= -1;
- }
- return buffer;
- };
- /**
- * Converts split-stereo (dual mono) sample buffers into a stereo interleaved sample buffer
- *
- * @param {Array} left A sample buffer
- * @param {Array} right A sample buffer
- *
- * @returns The stereo interleaved buffer
- */
- DSP.interleave = function(left, right) {
- if (left.length !== right.length) {
- throw "Can not interleave. Channel lengths differ.";
- }
-
- var stereoInterleaved = new Float64Array(left.length * 2);
-
- for (var i = 0, len = left.length; i < len; i++) {
- stereoInterleaved[2*i] = left[i];
- stereoInterleaved[2*i+1] = right[i];
- }
-
- return stereoInterleaved;
- };
- /**
- * Converts a stereo-interleaved sample buffer into split-stereo (dual mono) sample buffers
- *
- * @param {Array} buffer A stereo-interleaved sample buffer
- *
- * @returns an Array containing left and right channels
- */
- DSP.deinterleave = (function() {
- var left, right, mix, deinterleaveChannel = [];
- deinterleaveChannel[DSP.MIX] = function(buffer) {
- for (var i = 0, len = buffer.length/2; i < len; i++) {
- mix[i] = (buffer[2*i] + buffer[2*i+1]) / 2;
- }
- return mix;
- };
- deinterleaveChannel[DSP.LEFT] = function(buffer) {
- for (var i = 0, len = buffer.length/2; i < len; i++) {
- left[i] = buffer[2*i];
- }
- return left;
- };
- deinterleaveChannel[DSP.RIGHT] = function(buffer) {
- for (var i = 0, len = buffer.length/2; i < len; i++) {
- right[i] = buffer[2*i+1];
- }
- return right;
- };
- return function(channel, buffer) {
- left = left || new Float64Array(buffer.length/2);
- right = right || new Float64Array(buffer.length/2);
- mix = mix || new Float64Array(buffer.length/2);
- if (buffer.length/2 !== left.length) {
- left = new Float64Array(buffer.length/2);
- right = new Float64Array(buffer.length/2);
- mix = new Float64Array(buffer.length/2);
- }
- return deinterleaveChannel[channel](buffer);
- };
- }());
- /**
- * Separates a channel from a stereo-interleaved sample buffer
- *
- * @param {Array} buffer A stereo-interleaved sample buffer
- * @param {Number} channel A channel constant (LEFT, RIGHT, MIX)
- *
- * @returns an Array containing a signal mono sample buffer
- */
- DSP.getChannel = DSP.deinterleave;
- /**
- * Helper method (for Reverb) to mix two (interleaved) samplebuffers. It's possible
- * to negate the second buffer while mixing and to perform a volume correction
- * on the final signal.
- *
- * @param {Array} sampleBuffer1 Array containing Float values or a Float64Array
- * @param {Array} sampleBuffer2 Array containing Float values or a Float64Array
- * @param {Boolean} negate When true inverts/flips the audio signal
- * @param {Number} volumeCorrection When you add multiple sample buffers, use this to tame your signal ;)
- *
- * @returns A new Float64Array interleaved buffer.
- */
- DSP.mixSampleBuffers = function(sampleBuffer1, sampleBuffer2, negate, volumeCorrection){
- var outputSamples = new Float64Array(sampleBuffer1);
- for(var i = 0; i<sampleBuffer1.length; i++){
- outputSamples[i] += (negate ? -sampleBuffer2[i] : sampleBuffer2[i]) / volumeCorrection;
- }
-
- return outputSamples;
- };
- // Biquad filter types
- DSP.LPF = 0; // H(s) = 1 / (s^2 + s/Q + 1)
- DSP.HPF = 1; // H(s) = s^2 / (s^2 + s/Q + 1)
- DSP.BPF_CONSTANT_SKIRT = 2; // H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q)
- DSP.BPF_CONSTANT_PEAK = 3; // H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain)
- DSP.NOTCH = 4; // H(s) = (s^2 + 1) / (s^2 + s/Q + 1)
- DSP.APF = 5; // H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1)
- DSP.PEAKING_EQ = 6; // H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
- DSP.LOW_SHELF = 7; // H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1)
- DSP.HIGH_SHELF = 8; // H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A)
- // Biquad filter parameter types
- DSP.Q = 1;
- DSP.BW = 2; // SHARED with BACKWARDS LOOP MODE
- DSP.S = 3;
- // Find RMS of signal
- DSP.RMS = function(buffer) {
- var total = 0;
-
- for (var i = 0, n = buffer.length; i < n; i++) {
- total += buffer[i] * buffer[i];
- }
-
- return Math.sqrt(total / n);
- };
- // Find Peak of signal
- DSP.Peak = function(buffer) {
- var peak = 0;
-
- for (var i = 0, n = buffer.length; i < n; i++) {
- peak = (Math.abs(buffer[i]) > peak) ? Math.abs(buffer[i]) : peak;
- }
-
- return peak;
- };
- // Fourier Transform Module used by DFT, FFT, RFFT
- function FourierTransform(bufferSize, sampleRate) {
- this.bufferSize = bufferSize;
- this.sampleRate = sampleRate;
- this.bandwidth = 2 / bufferSize * sampleRate / 2;
- this.spectrum = new Float64Array(bufferSize/2);
- this.real = new Float64Array(bufferSize);
- this.imag = new Float64Array(bufferSize);
- this.peakBand = 0;
- this.peak = 0;
- /**
- * Calculates the *middle* frequency of an FFT band.
- *
- * @param {Number} index The index of the FFT band.
- *
- * @returns The middle frequency in Hz.
- */
- this.getBandFrequency = function(index) {
- return this.bandwidth * index + this.bandwidth / 2;
- };
- this.calculateSpectrum = function() {
- var spectrum = this.spectrum,
- real = this.real,
- imag = this.imag,
- bSi = 2 / this.bufferSize,
- sqrt = Math.sqrt,
- rval,
- ival,
- mag;
- for (var i = 0, N = bufferSize/2; i < N; i++) {
- rval = real[i];
- ival = imag[i];
- mag = bSi * sqrt(rval * rval + ival * ival);
- if (mag > this.peak) {
- this.peakBand = i;
- this.peak = mag;
- }
- spectrum[i] = mag;
- }
- };
- }
- /**
- * DFT is a class for calculating the Discrete Fourier Transform of a signal.
- *
- * @param {Number} bufferSize The size of the sample buffer to be computed
- * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
- *
- * @constructor
- */
- function DFT(bufferSize, sampleRate) {
- FourierTransform.call(this, bufferSize, sampleRate);
- var N = bufferSize/2 * bufferSize;
- var TWO_PI = 2 * Math.PI;
- this.sinTable = new Float64Array(N);
- this.cosTable = new Float64Array(N);
- for (var i = 0; i < N; i++) {
- this.sinTable[i] = Math.sin(i * TWO_PI / bufferSize);
- this.cosTable[i] = Math.cos(i * TWO_PI / bufferSize);
- }
- }
- /**
- * Performs a forward transform on the sample buffer.
- * Converts a time domain signal to frequency domain spectra.
- *
- * @param {Array} buffer The sample buffer
- *
- * @returns The frequency spectrum array
- */
- DFT.prototype.forward = function(buffer) {
- var real = this.real,
- imag = this.imag,
- rval,
- ival;
- for (var k = 0; k < this.bufferSize/2; k++) {
- rval = 0.0;
- ival = 0.0;
- for (var n = 0; n < buffer.length; n++) {
- rval += this.cosTable[k*n] * buffer[n];
- ival += this.sinTable[k*n] * buffer[n];
- }
- real[k] = rval;
- imag[k] = ival;
- }
- return this.calculateSpectrum();
- };
- /**
- * FFT is a class for calculating the Discrete Fourier Transform of a signal
- * with the Fast Fourier Transform algorithm.
- *
- * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
- * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
- *
- * @constructor
- */
- function FFT(bufferSize, sampleRate) {
- FourierTransform.call(this, bufferSize, sampleRate);
-
- this.reverseTable = new Uint32Array(bufferSize);
- var limit = 1;
- var bit = bufferSize >> 1;
- var i;
- while (limit < bufferSize) {
- for (i = 0; i < limit; i++) {
- this.reverseTable[i + limit] = this.reverseTable[i] + bit;
- }
- limit = limit << 1;
- bit = bit >> 1;
- }
- this.sinTable = new Float64Array(bufferSize);
- this.cosTable = new Float64Array(bufferSize);
- for (i = 0; i < bufferSize; i++) {
- this.sinTable[i] = Math.sin(-Math.PI/i);
- this.cosTable[i] = Math.cos(-Math.PI/i);
- }
- }
- /**
- * Performs a forward transform on the sample buffer.
- * Converts a time domain signal to frequency domain spectra.
- *
- * @param {Array} buffer The sample buffer. Buffer Length must be power of 2
- *
- * @returns The frequency spectrum array
- */
- FFT.prototype.forward = function(buffer) {
- // Locally scope variables for speed up
- var bufferSize = this.bufferSize,
- cosTable = this.cosTable,
- sinTable = this.sinTable,
- reverseTable = this.reverseTable,
- real = this.real,
- imag = this.imag,
- spectrum = this.spectrum;
- var k = Math.floor(Math.log(bufferSize) / Math.LN2);
- if (Math.pow(2, k) !== bufferSize) { throw "Invalid buffer size, must be a power of 2."; }
- if (bufferSize !== buffer.length) { throw "Supplied buffer is not the same size as defined FFT. FFT Size: " + bufferSize + " Buffer Size: " + buffer.length; }
- var halfSize = 1,
- phaseShiftStepReal,
- phaseShiftStepImag,
- currentPhaseShiftReal,
- currentPhaseShiftImag,
- off,
- tr,
- ti,
- tmpReal,
- i;
- for (i = 0; i < bufferSize; i++) {
- real[i] = buffer[reverseTable[i]];
- imag[i] = 0;
- }
- while (halfSize < bufferSize) {
- //phaseShiftStepReal = Math.cos(-Math.PI/halfSize);
- //phaseShiftStepImag = Math.sin(-Math.PI/halfSize);
- phaseShiftStepReal = cosTable[halfSize];
- phaseShiftStepImag = sinTable[halfSize];
-
- currentPhaseShiftReal = 1;
- currentPhaseShiftImag = 0;
- for (var fftStep = 0; fftStep < halfSize; fftStep++) {
- i = fftStep;
- while (i < bufferSize) {
- off = i + halfSize;
- tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
- ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);
- real[off] = real[i] - tr;
- imag[off] = imag[i] - ti;
- real[i] += tr;
- imag[i] += ti;
- i += halfSize << 1;
- }
- tmpReal = currentPhaseShiftReal;
- currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
- currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
- }
- halfSize = halfSize << 1;
- }
- return this.calculateSpectrum();
- };
- FFT.prototype.inverse = function(real, imag) {
- // Locally scope variables for speed up
- var bufferSize = this.bufferSize,
- cosTable = this.cosTable,
- sinTable = this.sinTable,
- reverseTable = this.reverseTable,
- spectrum = this.spectrum;
-
- real = real || this.real;
- imag = imag || this.imag;
- var halfSize = 1,
- phaseShiftStepReal,
- phaseShiftStepImag,
- currentPhaseShiftReal,
- currentPhaseShiftImag,
- off,
- tr,
- ti,
- tmpReal,
- i;
- for (i = 0; i < bufferSize; i++) {
- imag[i] *= -1;
- }
- var revReal = new Float64Array(bufferSize);
- var revImag = new Float64Array(bufferSize);
-
- for (i = 0; i < real.length; i++) {
- revReal[i] = real[reverseTable[i]];
- revImag[i] = imag[reverseTable[i]];
- }
-
- real = revReal;
- imag = revImag;
- while (halfSize < bufferSize) {
- phaseShiftStepReal = cosTable[halfSize];
- phaseShiftStepImag = sinTable[halfSize];
- currentPhaseShiftReal = 1;
- currentPhaseShiftImag = 0;
- for (var fftStep = 0; fftStep < halfSize; fftStep++) {
- i = fftStep;
- while (i < bufferSize) {
- off = i + halfSize;
- tr = (currentPhaseShiftReal * real[off]) - (currentPhaseShiftImag * imag[off]);
- ti = (currentPhaseShiftReal * imag[off]) + (currentPhaseShiftImag * real[off]);
- real[off] = real[i] - tr;
- imag[off] = imag[i] - ti;
- real[i] += tr;
- imag[i] += ti;
- i += halfSize << 1;
- }
- tmpReal = currentPhaseShiftReal;
- currentPhaseShiftReal = (tmpReal * phaseShiftStepReal) - (currentPhaseShiftImag * phaseShiftStepImag);
- currentPhaseShiftImag = (tmpReal * phaseShiftStepImag) + (currentPhaseShiftImag * phaseShiftStepReal);
- }
- halfSize = halfSize << 1;
- }
- var buffer = new Float64Array(bufferSize); // this should be reused instead
- for (i = 0; i < bufferSize; i++) {
- buffer[i] = real[i] / bufferSize;
- }
- return buffer;
- };
- /**
- * RFFT is a class for calculating the Discrete Fourier Transform of a signal
- * with the Fast Fourier Transform algorithm.
- *
- * This method currently only contains a forward transform but is highly optimized.
- *
- * @param {Number} bufferSize The size of the sample buffer to be computed. Must be power of 2
- * @param {Number} sampleRate The sampleRate of the buffer (eg. 44100)
- *
- * @constructor
- */
- // lookup tables don't really gain us any speed, but they do increase
- // cache footprint, so don't use them in here
- // also we don't use sepearate arrays for real/imaginary parts
- // this one a little more than twice as fast as the one in FFT
- // however I only did the forward transform
- // the rest of this was translated from C, see http://www.jjj.de/fxt/
- // this is the real split radix FFT
- function RFFT(bufferSize, sampleRate) {
- FourierTransform.call(this, bufferSize, sampleRate);
- this.trans = new Float64Array(bufferSize);
- this.reverseTable = new Uint32Array(bufferSize);
- // don't use a lookup table to do the permute, use this instead
- this.reverseBinPermute = function (dest, source) {
- var bufferSize = this.bufferSize,
- halfSize = bufferSize >>> 1,
- nm1 = bufferSize - 1,
- i = 1, r = 0, h;
- dest[0] = source[0];
- do {
- r += halfSize;
- dest[i] = source[r];
- dest[r] = source[i];
-
- i++;
- h = halfSize << 1;
- while (h = h >> 1, !((r ^= h) & h));
- if (r >= i) {
- dest[i] = source[r];
- dest[r] = source[i];
- dest[nm1-i] = source[nm1-r];
- dest[nm1-r] = source[nm1-i];
- }
- i++;
- } while (i < halfSize);
- dest[nm1] = source[nm1];
- };
- this.generateReverseTable = function () {
- var bufferSize = this.bufferSize,
- halfSize = bufferSize >>> 1,
- nm1 = bufferSize - 1,
- i = 1, r = 0, h;
- this.reverseTable[0] = 0;
- do {
- r += halfSize;
-
- this.reverseTable[i] = r;
- this.reverseTable[r] = i;
- i++;
- h = halfSize << 1;
- while (h = h >> 1, !((r ^= h) & h));
- if (r >= i) {
- this.reverseTable[i] = r;
- this.reverseTable[r] = i;
- this.reverseTable[nm1-i] = nm1-r;
- this.reverseTable[nm1-r] = nm1-i;
- }
- i++;
- } while (i < halfSize);
- this.reverseTable[nm1] = nm1;
- };
- this.generateReverseTable();
- }
- // Ordering of output:
- //
- // trans[0] = re[0] (==zero frequency, purely real)
- // trans[1] = re[1]
- // ...
- // trans[n/2-1] = re[n/2-1]
- // trans[n/2] = re[n/2] (==nyquist frequency, purely real)
- //
- // trans[n/2+1] = im[n/2-1]
- // trans[n/2+2] = im[n/2-2]
- // ...
- // trans[n-1] = im[1]
- RFFT.prototype.forward = function(buffer) {
- var n = this.bufferSize,
- spectrum = this.spectrum,
- x = this.trans,
- TWO_PI = 2*Math.PI,
- sqrt = Math.sqrt,
- i = n >>> 1,
- bSi = 2 / n,
- n2, n4, n8, nn,
- t1, t2, t3, t4,
- i1, i2, i3, i4, i5, i6, i7, i8,
- st1, cc1, ss1, cc3, ss3,
- e,
- a,
- rval, ival, mag;
- this.reverseBinPermute(x, buffer);
- /*
- var reverseTable = this.reverseTable;
- for (var k = 0, len = reverseTable.length; k < len; k++) {
- x[k] = buffer[reverseTable[k]];
- }
- */
- for (var ix = 0, id = 4; ix < n; id *= 4) {
- for (var i0 = ix; i0 < n; i0 += id) {
- //sumdiff(x[i0], x[i0+1]); // {a, b} <--| {a+b, a-b}
- st1 = x[i0] - x[i0+1];
- x[i0] += x[i0+1];
- x[i0+1] = st1;
- }
- ix = 2*(id-1);
- }
- n2 = 2;
- nn = n >>> 1;
- while((nn = nn >>> 1)) {
- ix = 0;
- n2 = n2 << 1;
- id = n2 << 1;
- n4 = n2 >>> 2;
- n8 = n2 >>> 3;
- do {
- if(n4 !== 1) {
- for(i0 = ix; i0 < n; i0 += id) {
- i1 = i0;
- i2 = i1 + n4;
- i3 = i2 + n4;
- i4 = i3 + n4;
-
- //diffsum3_r(x[i3], x[i4], t1); // {a, b, s} <--| {a, b-a, a+b}
- t1 = x[i3] + x[i4];
- x[i4] -= x[i3];
- //sumdiff3(x[i1], t1, x[i3]); // {a, b, d} <--| {a+b, b, a-b}
- x[i3] = x[i1] - t1;
- x[i1] += t1;
-
- i1 += n8;
- i2 += n8;
- i3 += n8;
- i4 += n8;
-
- //sumdiff(x[i3], x[i4], t1, t2); // {s, d} <--| {a+b, a-b}
- t1 = x[i3] + x[i4];
- t2 = x[i3] - x[i4];
-
- t1 = -t1 * Math.SQRT1_2;
- t2 *= Math.SQRT1_2;
-
- // sumdiff(t1, x[i2], x[i4], x[i3]); // {s, d} <--| {a+b, a-b}
- st1 = x[i2];
- x[i4] = t1 + st1;
- x[i3] = t1 - st1;
-
- //sumdiff3(x[i1], t2, x[i2]); // {a, b, d} <--| {a+b, b, a-b}
- x[i2] = x[i1] - t2;
- x[i1] += t2;
- }
- } else {
- for(i0 = ix; i0 < n; i0 += id) {
- i1 = i0;
- i2 = i1 + n4;
- i3 = i2 + n4;
- i4 = i3 + n4;
-
- //diffsum3_r(x[i3], x[i4], t1); // {a, b, s} <--| {a, b-a, a+b}
- t1 = x[i3] + x[i4];
- x[i4] -= x[i3];
-
- //sumdiff3(x[i1], t1, x[i3]); // {a, b, d} <--| {a+b, b, a-b}
- x[i3] = x[i1] - t1;
- x[i1] += t1;
- }
- }
-
- ix = (id << 1) - n2;
- id = id << 2;
- } while (ix < n);
-
- e = TWO_PI / n2;
- for (var j = 1; j < n8; j++) {
- a = j * e;
- ss1 = Math.sin(a);
- cc1 = Math.cos(a);
- //ss3 = sin(3*a); cc3 = cos(3*a);
- cc3 = 4*cc1*(cc1*cc1-0.75);
- ss3 = 4*ss1*(0.75-ss1*ss1);
-
- ix = 0; id = n2 << 1;
- do {
- for (i0 = ix; i0 < n; i0 += id) {
- i1 = i0 + j;
- i2 = i1 + n4;
- i3 = i2 + n4;
- i4 = i3 + n4;
-
- i5 = i0 + n4 - j;
- i6 = i5 + n4;
- i7 = i6 + n4;
- i8 = i7 + n4;
-
- //cmult(c, s, x, y, &u, &v)
- //cmult(cc1, ss1, x[i7], x[i3], t2, t1); // {u,v} <--| {x*c-y*s, x*s+y*c}
- t2 = x[i7]*cc1 - x[i3]*ss1;
- t1 = x[i7]*ss1 + x[i3]*cc1;
-
- //cmult(cc3, ss3, x[i8], x[i4], t4, t3);
- t4 = x[i8]*cc3 - x[i4]*ss3;
- t3 = x[i8]*ss3 + x[i4]*cc3;
-
- //sumdiff(t2, t4); // {a, b} <--| {a+b, a-b}
- st1 = t2 - t4;
- t2 += t4;
- t4 = st1;
-
- //sumdiff(t2, x[i6], x[i8], x[i3]); // {s, d} <--| {a+b, a-b}
- //st1 = x[i6]; x[i8] = t2 + st1; x[i3] = t2 - st1;
- x[i8] = t2 + x[i6];
- x[i3] = t2 - x[i6];
-
- //sumdiff_r(t1, t3); // {a, b} <--| {a+b, b-a}
- st1 = t3 - t1;
- t1 += t3;
- t3 = st1;
-
- //sumdiff(t3, x[i2], x[i4], x[i7]); // {s, d} <--| {a+b, a-b}
- //st1 = x[i2]; x[i4] = t3 + st1; x[i7] = t3 - st1;
- x[i4] = t3 + x[i2];
- x[i7] = t3 - x[i2];
-
- //sumdiff3(x[i1], t1, x[i6]); // {a, b, d} <--| {a+b, b, a-b}
- x[i6] = x[i1] - t1;
- x[i1] += t1;
-
- //diffsum3_r(t4, x[i5], x[i2]); // {a, b, s} <--| {a, b-a, a+b}
- x[i2] = t4 + x[i5];
- x[i5] -= t4;
- }
-
- ix = (id << 1) - n2;
- id = id << 2;
-
- } while (ix < n);
- }
- }
- while (--i) {
- rval = x[i];
- ival = x[n-i-1];
- mag = bSi * sqrt(rval * rval + ival * ival);
- if (mag > this.peak) {
- this.peakBand = i;
- this.peak = mag;
- }
- spectrum[i] = mag;
- }
- spectrum[0] = bSi * x[0];
- return spectrum;
- };
- function Sampler(file, bufferSize, sampleRate, playStart, playEnd, loopStart, loopEnd, loopMode) {
- this.file = file;
- this.bufferSize = bufferSize;
- this.sampleRate = sampleRate;
- this.playStart = playStart || 0; // 0%
- this.playEnd = playEnd || 1; // 100%
- this.loopStart = loopStart || 0;
- this.loopEnd = loopEnd || 1;
- this.loopMode = loopMode || DSP.OFF;
- this.loaded = false;
- this.samples = [];
- this.signal = new Float64Array(bufferSize);
- this.frameCount = 0;
- this.envelope = null;
- this.amplitude = 1;
- this.rootFrequency = 110; // A2 110
- this.frequency = 550;
- this.step = this.frequency / this.rootFrequency;
- this.duration = 0;
- this.samplesProcessed = 0;
- this.playhead = 0;
-
- var audio = /* new Audio();*/ document.createElement("AUDIO");
- var self = this;
-
- this.loadSamples = function(event) {
- var buffer = DSP.getChannel(DSP.MIX, event.frameBuffer);
- for ( var i = 0; i < buffer.length; i++) {
- self.samples.push(buffer[i]);
- }
- };
-
- this.loadComplete = function() {
- // convert flexible js array into a fast typed array
- self.samples = new Float64Array(self.samples);
- self.loaded = true;
- };
-
- this.loadMetaData = function() {
- self.duration = audio.duration;
- };
-
- audio.addEventListener("MozAudioAvailable", this.loadSamples, false);
- audio.addEventListener("loadedmetadata", this.loadMetaData, false);
- audio.addEventListener("ended", this.loadComplete, false);
- audio.muted = true;
- audio.src = file;
- audio.play();
- }
- Sampler.prototype.applyEnvelope = function() {
- this.envelope.process(this.signal);
- return this.signal;
- };
- Sampler.prototype.generate = function() {
- var frameOffset = this.frameCount * this.bufferSize;
-
- var loopWidth = this.playEnd * this.samples.length - this.playStart * this.samples.length;
- var playStartSamples = this.playStart * this.samples.length; // ie 0.5 -> 50% of the length
- var playEndSamples = this.playEnd * this.samples.length; // ie 0.5 -> 50% of the length
- var offset;
- for ( var i = 0; i < this.bufferSize; i++ ) {
- switch (this.loopMode) {
- case DSP.OFF:
- this.playhead = Math.round(this.samplesProcessed * this.step + playStartSamples);
- if (this.playhead < (this.playEnd * this.samples.length) ) {
- this.signal[i] = this.samples[this.playhead] * this.amplitude;
- } else {
- this.signal[i] = 0;
- }
- break;
-
- case DSP.FW:
- this.playhead = Math.round((this.samplesProcessed * this.step) % loopWidth + playStartSamples);
- if (this.playhead < (this.playEnd * this.samples.length) ) {
- this.signal[i] = this.samples[this.playhead] * this.amplitude;
- }
- break;
-
- case DSP.BW:
- this.playhead = playEndSamples - Math.round((this.samplesProcessed * this.step) % loopWidth);
- if (this.playhead < (this.playEnd * this.samples.length) ) {
- this.signal[i] = this.samples[this.playhead] * this.amplitude;
- }
- break;
-
- case DSP.FWBW:
- if ( Math.floor(this.samplesProcessed * this.step / loopWidth) % 2 === 0 ) {
- this.playhead = Math.round((this.samplesProcessed * this.step) % loopWidth + playStartSamples);
- } else {
- this.playhead = playEndSamples - Math.round((this.samplesProcessed * this.step) % loopWidth);
- }
- if (this.playhead < (this.playEnd * this.samples.length) ) {
- this.signal[i] = this.samples[this.playhead] * this.amplitude;
- }
- break;
- }
- this.samplesProcessed++;
- }
- this.frameCount++;
- return this.signal;
- };
- Sampler.prototype.setFreq = function(frequency) {
- var totalProcessed = this.samplesProcessed * this.step;
- this.frequency = frequency;
- this.step = this.frequency / this.rootFrequency;
- this.samplesProcessed = Math.round(totalProcessed/this.step);
- };
- Sampler.prototype.reset = function() {
- this.samplesProcessed = 0;
- this.playhead = 0;
- };
- /**
- * Oscillator class for generating and modifying signals
- *
- * @param {Number} type A waveform constant (eg. DSP.SINE)
- * @param {Number} frequency Initial frequency of the signal
- * @param {Number} amplitude Initial amplitude of the signal
- * @param {Number} bufferSize Size of the sample buffer to generate
- * @param {Number} sampleRate The sample rate of the signal
- *
- * @contructor
- */
- function Oscillator(type, frequency, amplitude, bufferSize, sampleRate) {
- this.frequency = frequency;
- this.amplitude = amplitude;
- this.bufferSize = bufferSize;
- this.sampleRate = sampleRate;
- //this.pulseWidth = pulseWidth;
- this.frameCount = 0;
-
- this.waveTableLength = 2048;
- this.cyclesPerSample = frequency / sampleRate;
- this.signal = new Float64Array(bufferSize);
- this.envelope = null;
- switch(parseInt(type, 10)) {
- case DSP.TRIANGLE:
- this.func = Oscillator.Triangle;
- break;
- case DSP.SAW:
- this.func = Oscillator.Saw;
- break;
- case DSP.SQUARE:
- this.func = Oscillator.Square;
- break;
- default:
- case DSP.SINE:
- this.func = Oscillator.Sine;
- break;
- }
- this.generateWaveTable = function() {
- Oscillator.waveTable[this.func] = new Float64Array(2048);
- var waveTableTime = this.waveTableLength / this.sampleRate;
- var waveTableHz = 1 / waveTableTime;
- for (var i = 0; i < this.waveTableLength; i++) {
- Oscillator.waveTable[this.func][i] = this.func(i * waveTableHz/this.sampleRate);
- }
- };
- if ( typeof Oscillator.waveTable === 'undefined' ) {
- Oscillator.waveTable = {};
- }
- if ( typeof Oscillator.waveTable[this.func] === 'undefined' ) {
- this.generateWaveTable();
- }
-
- this.waveTable = Oscillator.waveTable[this.func];
- }
- /**
- * Set the amplitude of the signal
- *
- * @param {Number} amplitude The amplitude of the signal (between 0 and 1)
- */
- Oscillator.prototype.setAmp = function(amplitude) {
- if (amplitude >= 0 && amplitude <= 1) {
- this.amplitude = amplitude;
- } else {
- throw "Amplitude out of range (0..1).";
- }
- };
-
- /**
- * Set the frequency of the signal
- *
- * @param {Number} frequency The frequency of the signal
- */
- Oscillator.prototype.setFreq = function(frequency) {
- this.frequency = frequency;
- this.cyclesPerSample = frequency / this.sampleRate;
- };
-
- // Add an oscillator
- Oscillator.prototype.add = function(oscillator) {
- for ( var i = 0; i < this.bufferSize; i++ ) {
- //this.signal[i] += oscillator.valueAt(i);
- this.signal[i] += oscillator.signal[i];
- }
-
- return this.signal;
- };
-
- // Add a signal to the current generated osc signal
- Oscillator.prototype.addSignal = function(signal) {
- for ( var i = 0; i < signal.length; i++ ) {
- if ( i >= this.bufferSize ) {
- break;
- }
- this.signal[i] += signal[i];
-
- /*
- // Constrain amplitude
- if ( this.signal[i] > 1 ) {
- this.signal[i] = 1;
- } else if ( this.signal[i] < -1 ) {
- this.signal[i] = -1;
- }
- */
- }
- return this.signal;
- };
-
- // Add an envelope to the oscillator
- Oscillator.prototype.addEnvelope = function(envelope) {
- this.envelope = envelope;
- };
- Oscillator.prototype.applyEnvelope = function() {
- this.envelope.process(this.signal);
- };
-
- Oscillator.prototype.valueAt = function(offset) {
- return this.waveTable[offset % this.waveTableLength];
- };
-
- Oscillator.prototype.generate = function() {
- var frameOffset = this.frameCount * this.bufferSize;
- var step = this.waveTableLength * this.frequency / this.sampleRate;
- var offset;
- for ( var i = 0; i < this.bufferSize; i++ ) {
- //var step = (frameOffset + i) * this.cyclesPerSample % 1;
- //this.signal[i] = this.func(step) * this.amplitude;
- //this.signal[i] = this.valueAt(Math.round((frameOffset + i) * step)) * this.amplitude;
- offset = Math.round((frameOffset + i) * step);
- this.signal[i] = this.waveTable[offset % this.waveTableLength] * this.amplitude;
- }
- this.frameCount++;
- return this.signal;
- };
- Oscillator.Sine = function(step) {
- return Math.sin(DSP.TWO_PI * step);
- };
- Oscillator.Square = function(step) {
- return step < 0.5 ? 1 : -1;
- };
- Oscillator.Saw = function(step) {
- return 2 * (step - Math.round(step));
- };
- Oscillator.Triangle = function(step) {
- return 1 - 4 * Math.abs(Math.round(step) - step);
- };
- Oscillator.Pulse = function(step) {
- // stub
- };
-
- function ADSR(attackLength, decayLength, sustainLevel, sustainLength, releaseLength, sampleRate) {
- this.sampleRate = sampleRate;
- // Length in seconds
- this.attackLength = attackLength;
- this.decayLength = decayLength;
- this.sustainLevel = sustainLevel;
- this.sustainLength = sustainLength;
- this.releaseLength = releaseLength;
- this.sampleRate = sampleRate;
-
- // Length in samples
- this.attackSamples = attackLength * sampleRate;
- this.decaySamples = decayLength * sampleRate;
- this.sustainSamples = sustainLength * sampleRate;
- this.releaseSamples = releaseLength * sampleRate;
-
- // Updates the envelope sample positions
- this.update = function() {
- this.attack = this.attackSamples;
- this.decay = this.attack + this.decaySamples;
- this.sustain = this.decay + this.sustainSamples;
- this.release = this.sustain + this.releaseSamples;
- };
-
- this.update();
-
- this.samplesProcessed = 0;
- }
- ADSR.prototype.noteOn = function() {
- this.samplesProcessed = 0;
- this.sustainSamples = this.sustainLength * this.sampleRate;
- this.update();
- };
- // Send a note off when using a sustain of infinity to let the envelope enter the release phase
- ADSR.prototype.noteOff = function() {
- this.sustainSamples = this.samplesProcessed - this.decaySamples;
- this.update();
- };
- ADSR.prototype.processSample = function(sample) {
- var amplitude = 0;
- if ( this.samplesProcessed <= this.attack ) {
- amplitude = 0 + (1 - 0) * ((this.samplesProcessed - 0) / (this.attack - 0));
- } else if ( this.samplesProcessed > this.attack && this.samplesProcessed <= this.decay ) {
- amplitude = 1 + (this.sustainLevel - 1) * ((this.samplesProcessed - this.attack) / (this.decay - this.attack));
- } else if ( this.samplesProcessed > this.decay && this.samplesProcessed <= this.sustain ) {
- amplitude = this.sustainLevel;
- } else if ( this.samplesProcessed > this.sustain && this.samplesProcessed <= this.release ) {
- amplitude = this.sustainLevel + (0 - this.sustainLevel) * ((this.samplesProcessed - this.sustain) / (this.release - this.sustain));
- }
-
- return sample * amplitude;
- };
- ADSR.prototype.value = function() {
- var amplitude = 0;
- if ( this.samplesProcessed <= this.attack ) {
- amplitude = 0 + (1 - 0) * ((this.samplesProcessed - 0) / (this.attack - 0));
- } else if ( this.samplesProcessed > this.attack && this.samplesProcessed <= this.decay ) {
- amplitude = 1 + (this.sustainLevel - 1) * ((this.samplesProcessed - this.attack) / (this.decay - this.attack));
- } else if ( this.samplesProcessed > this.decay && this.samplesProcessed <= this.sustain ) {
- amplitude = this.sustainLevel;
- } else if ( this.samplesProcessed > this.sustain && this.samplesProcessed <= this.release ) {
- amplitude = this.sustainLevel + (0 - this.sustainLevel) * ((this.samplesProcessed - this.sustain) / (this.release - this.sustain));
- }
-
- return amplitude;
- };
-
- ADSR.prototype.process = function(buffer) {
- for ( var i = 0; i < buffer.length; i++ ) {
- buffer[i] *= this.value();
- this.samplesProcessed++;
- }
-
- return buffer;
- };
-
-
- ADSR.prototype.isActive = function() {
- if ( this.samplesProcessed > this.release || this.samplesProcessed === -1 ) {
- return false;
- } else {
- return true;
- }
- };
- ADSR.prototype.disable = function() {
- this.samplesProcessed = -1;
- };
-
- function IIRFilter(type, cutoff, resonance, sampleRate) {
- this.sampleRate = sampleRate;
- switch(type) {
- case DSP.LOWPASS:
- case DSP.LP12:
- this.func = new IIRFilter.LP12(cutoff, resonance, sampleRate);
- break;
- }
- }
- IIRFilter.prototype.__defineGetter__('cutoff',
- function() {
- return this.func.cutoff;
- }
- );
- IIRFilter.prototype.__defineGetter__('resonance',
- function() {
- return this.func.resonance;
- }
- );
- IIRFilter.prototype.set = function(cutoff, resonance) {
- this.func.calcCoeff(cutoff, resonance);
- };
- IIRFilter.prototype.process = function(buffer) {
- this.func.process(buffer);
- };
- // Add an envelope to the filter
- IIRFilter.prototype.addEnvelope = function(envelope) {
- if ( envelope instanceof ADSR ) {
- this.func.addEnvelope(envelope);
- } else {
- throw "Not an envelope.";
- }
- };
- IIRFilter.LP12 = function(cutoff, resonance, sampleRate) {
- this.sampleRate = sampleRate;
- this.vibraPos = 0;
- this.vibraSpeed = 0;
- this.envelope = false;
-
- this.calcCoeff = function(cutoff, resonance) {
- this.w = 2.0 * Math.PI * cutoff / this.sampleRate;
- this.q = 1.0 - this.w / (2.0 * (resonance + 0.5 / (1.0 + this.w)) + this.w - 2.0);
- this.r = this.q * this.q;
- this.c = this.r + 1.0 - 2.0 * Math.cos(this.w) * this.q;
-
- this.cutoff = cutoff;
- this.resonance = resonance;
- };
- this.calcCoeff(cutoff, resonance);
- this.process = function(buffer) {
- for ( var i = 0; i < buffer.length; i++ ) {
- this.vibraSpeed += (buffer[i] - this.vibraPos) * this.c;
- this.vibraPos += this.vibraSpeed;
- this.vibraSpeed *= this.r;
-
- /*
- var temp = this.vibraPos;
-
- if ( temp > 1.0 ) {
- temp = 1.0;
- } else if ( temp < -1.0 ) {
- temp = -1.0;
- } else if ( temp != temp ) {
- temp = 1;
- }
-
- buffer[i] = temp;
- */
- if (this.envelope) {
- buffer[i] = (buffer[i] * (1 - this.envelope.value())) + (this.vibraPos * this.envelope.value());
- this.envelope.samplesProcessed++;
- } else {
- buffer[i] = this.vibraPos;
- }
- }
- };
- };
- IIRFilter.LP12.prototype.addEnvelope = function(envelope) {
- this.envelope = envelope;
- };
- function IIRFilter2(type, cutoff, resonance, sampleRate) {
- this.type = type;
- this.cutoff = cutoff;
- this.resonance = resonance;
- this.sampleRate = sampleRate;
- this.f = Float64Array(4);
- this.f[0] = 0.0; // lp
- this.f[1] = 0.0; // hp
- this.f[2] = 0.0; // bp
- this.f[3] = 0.0; // br
-
- this.calcCoeff = function(cutoff, resonance) {
- this.freq = 2 * Math.sin(Math.PI * Math.min(0.25, cutoff/(this.sampleRate*2)));
- this.damp = Math.min(2 * (1 - Math.pow(resonance, 0.25)), Math.min(2, 2/this.freq - this.freq * 0.5));
- };
- this.calcCoeff(cutoff, resonance);
- }
- IIRFilter2.prototype.process = function(buffer) {
- var input, output;
- var f = this.f;
- for ( var i = 0; i < buffer.length; i++ ) {
- input = buffer[i];
- // first pass
- f[3] = input - this.damp * f[2];
- f[0] = f[0] + this.freq * f[2];
- f[1] = f[3] - f[0];
- f[2] = this.freq * f[1] + f[2];
- output = 0.5 * f[this.type];
- // second pass
- f[3] = input - this.damp * f[2];
- f[0] = f[0] + this.freq * f[2];
- f[1] = f[3] - f[0];
- f[2] = this.freq * f[1] + f[2];
- output += 0.5 * f[this.type];
- if (this.envelope) {
- buffer[i] = (buffer[i] * (1 - this.envelope.value())) + (output * this.envelope.value());
- this.envelope.samplesProcessed++;
- } else {
- buffer[i] = output;
- }
- }
- };
- IIRFilter2.prototype.addEnvelope = function(envelope) {
- if ( envelope instanceof ADSR ) {
- this.envelope = envelope;
- } else {
- throw "This is not an envelope.";
- }
- };
- IIRFilter2.prototype.set = function(cutoff, resonance) {
- this.calcCoeff(cutoff, resonance);
- };
- function WindowFunction(type, alpha) {
- this.alpha = alpha;
-
- switch(type) {
- case DSP.BARTLETT:
- this.func = WindowFunction.Bartlett;
- break;
-
- case DSP.BARTLETTHANN:
- this.func = WindowFunction.BartlettHann;
- break;
-
- case DSP.BLACKMAN:
- this.func = WindowFunction.Blackman;
- this.alpha = this.alpha || 0.16;
- break;
-
- case DSP.COSINE:
- this.func = WindowFunction.Cosine;
- break;
-
- case DSP.GAUSS:
- this.func = WindowFunction.Gauss;
- this.alpha = this.alpha || 0.25;
- break;
-
- case DSP.HAMMING:
- this.func = WindowFunction.Hamming;
- break;
-
- case DSP.HANN:
- this.func = WindowFunction.Hann;
- break;
-
- case DSP.LANCZOS:
- this.func = WindowFunction.Lanczoz;
- break;
-
- case DSP.RECTANGULAR:
- this.func = WindowFunction.Rectangular;
- break;
-
- case DSP.TRIANGULAR:
- this.func = WindowFunction.Triangular;
- break;
- }
- }
- WindowFunction.prototype.process = function(buffer) {
- var length = buffer.length;
- for ( var i = 0; i < length; i++ ) {
- buffer[i] *= this.func(length, i, this.alpha);
- }
- return buffer;
- };
- WindowFunction.Bartlett = function(length, index) {
- return 2 / (length - 1) * ((length - 1) / 2 - Math.abs(index - (length - 1) / 2));
- };
- WindowFunction.BartlettHann = function(length, index) {
- return 0.62 - 0.48 * Math.abs(index / (length - 1) - 0.5) - 0.38 * Math.cos(DSP.TWO_PI * index / (length - 1));
- };
- WindowFunction.Blackman = function(length, index, alpha) {
- var a0 = (1 - alpha) / 2;
- var a1 = 0.5;
- var a2 = alpha / 2;
- return a0 - a1 * Math.cos(DSP.TWO_PI * index / (length - 1)) + a2 * Math.cos(4 * Math.PI * index / (length - 1));
- };
- WindowFunction.Cosine = function(length, index) {
- return Math.cos(Math.PI * index / (length - 1) - Math.PI / 2);
- };
- WindowFunction.Gauss = function(length, index, alpha) {
- return Math.pow(Math.E, -0.5 * Math.pow((index - (length - 1) / 2) / (alpha * (length - 1) / 2), 2));
- };
- WindowFunction.Hamming = function(length, index) {
- return 0.54 - 0.46 * Math.cos(DSP.TWO_PI * index / (length - 1));
- };
- WindowFunction.Hann = function(length, index) {
- return 0.5 * (1 - Math.cos(DSP.TWO_PI * index / (length - 1)));
- };
- WindowFunction.Lanczos = function(length, index) {
- var x = 2 * index / (length - 1) - 1;
- return Math.sin(Math.PI * x) / (Math.PI * x);
- };
- WindowFunction.Rectangular = function(length, index) {
- return 1;
- };
- WindowFunction.Triangular = function(length, index) {
- return 2 / length * (length / 2 - Math.abs(index - (length - 1) / 2));
- };
- function sinh (arg) {
- // Returns the hyperbolic sine of the number, defined as (exp(number) - exp(-number))/2
- //
- // version: 1004.2314
- // discuss at: http://phpjs.org/functions/sinh // + original by: Onno Marsman
- // * example 1: sinh(-0.9834330348825909);
- // * returns 1: -1.1497971402636502
- return (Math.exp(arg) - Math.exp(-arg))/2;
- }
- /*
- * Biquad filter
- *
- * Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
- * Copyright 2010 Ricard Marxer. All rights reserved.
- *
- */
- // Implementation based on:
- // http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
- function Biquad(type, sampleRate) {
- this.Fs = sampleRate;
- this.type = type; // type of the filter
- this.parameterType = DSP.Q; // type of the parameter
- this.x_1_l = 0;
- this.x_2_l = 0;
- this.y_1_l = 0;
- this.y_2_l = 0;
- this.x_1_r = 0;
- this.x_2_r = 0;
- this.y_1_r = 0;
- this.y_2_r = 0;
- this.b0 = 1;
- this.a0 = 1;
- this.b1 = 0;
- this.a1 = 0;
- this.b2 = 0;
- this.a2 = 0;
- this.b0a0 = this.b0 / this.a0;
- this.b1a0 = this.b1 / this.a0;
- this.b2a0 = this.b2 / this.a0;
- this.a1a0 = this.a1 / this.a0;
- this.a2a0 = this.a2 / this.a0;
- this.f0 = 3000; // "wherever it's happenin', man." Center Frequency or
- // Corner Frequency, or shelf midpoint frequency, depending
- // on which filter type. The "significant frequency".
- this.dBgain = 12; // used only for peaking and shelving filters
- this.Q = 1; // the EE kind of definition, except for peakingEQ in which A*Q is
- // the classic EE Q. That adjustment in definition was made so that
- // a boost of N dB followed by a cut of N dB for identical Q and
- // f0/Fs results in a precisely flat unity gain filter or "wire".
- this.BW = -3; // the bandwidth in octaves (between -3 dB frequencies for BPF
- // and notch or between midpoint (dBgain/2) gain frequencies for
- // peaking EQ
- this.S = 1; // a "shelf slope" parameter (for shelving EQ only). When S = 1,
- // the shelf slope is as steep as it can be and remain monotonically
- // increasing or decreasing gain with frequency. The shelf slope, in
- // dB/octave, remains proportional to S for all other values for a
- // fixed f0/Fs and dBgain.
- this.coefficients = function() {
- var b = [this.b0, this.b1, this.b2];
- var a = [this.a0, this.a1, this.a2];
- return {b: b, a:a};
- };
- this.setFilterType = function(type) {
- this.type = type;
- this.recalculateCoefficients();
- };
- this.setSampleRate = function(rate) {
- this.Fs = rate;
- this.recalculateCoefficients();
- };
- this.setQ = function(q) {
- this.parameterType = DSP.Q;
- this.Q = Math.max(Math.min(q, 115.0), 0.001);
- this.recalculateCoefficients();
- };
- this.setBW = function(bw) {
- this.parameterType = DSP.BW;
- this.BW = bw;
- this.recalculateCoefficients();
- };
- this.setS = function(s) {
- this.parameterType = DSP.S;
- this.S = Math.max(Math.min(s, 5.0), 0.0001);
- this.recalculateCoefficients();
- };
- this.setF0 = function(freq) {
- this.f0 = freq;
- this.recalculateCoefficients();
- };
-
- this.setDbGain = function(g) {
- this.dBgain = g;
- this.recalculateCoefficients();
- };
- this.recalculateCoefficients = function() {
- var A;
- if (type === DSP.PEAKING_EQ || type === DSP.LOW_SHELF || type === DSP.HIGH_SHELF ) {
- A = Math.pow(10, (this.dBgain/40)); // for peaking and shelving EQ filters only
- } else {
- A = Math.sqrt( Math.pow(10, (this.dBgain/20)) );
- }
- var w0 = DSP.TWO_PI * this.f0 / this.Fs;
- var cosw0 = Math.cos(w0);
- var sinw0 = Math.sin(w0);
- var alpha = 0;
-
- switch (this.parameterType) {
- case DSP.Q:
- alpha = sinw0/(2*this.Q);
- break;
-
- case DSP.BW:
- alpha = sinw0 * sinh( Math.LN2/2 * this.BW * w0/sinw0 );
- break;
- case DSP.S:
- alpha = sinw0/2 * Math.sqrt( (A + 1/A)*(1/this.S - 1) + 2 );
- break;
- }
- /**
- FYI: The relationship between bandwidth and Q is
- 1/Q = 2*sinh(ln(2)/2*BW*w0/sin(w0)) (digital filter w BLT)
- or 1/Q = 2*sinh(ln(2)/2*BW) (analog filter prototype)
- The relationship between shelf slope and Q is
- 1/Q = sqrt((A + 1/A)*(1/S - 1) + 2)
- */
- var coeff;
- switch (this.type) {
- case DSP.LPF: // H(s) = 1 / (s^2 + s/Q + 1)
- this.b0 = (1 - cosw0)/2;
- this.b1 = 1 - cosw0;
- this.b2 = (1 - cosw0)/2;
- this.a0 = 1 + alpha;
- this.a1 = -2 * cosw0;
- this.a2 = 1 - alpha;
- break;
- case DSP.HPF: // H(s) = s^2 / (s^2 + s/Q + 1)
- this.b0 = (1 + cosw0)/2;
- this.b1 = -(1 + cosw0);
- this.b2 = (1 + cosw0)/2;
- this.a0 = 1 + alpha;
- this.a1 = -2 * cosw0;
- this.a2 = 1 - alpha;
- break;
- case DSP.BPF_CONSTANT_SKIRT: // H(s) = s / (s^2 + s/Q + 1) (constant skirt gain, peak gain = Q)
- this.b0 = sinw0/2;
- this.b1 = 0;
- this.b2 = -sinw0/2;
- this.a0 = 1 + alpha;
- this.a1 = -2*cosw0;
- this.a2 = 1 - alpha;
- break;
- case DSP.BPF_CONSTANT_PEAK: // H(s) = (s/Q) / (s^2 + s/Q + 1) (constant 0 dB peak gain)
- this.b0 = alpha;
- this.b1 = 0;
- this.b2 = -alpha;
- this.a0 = 1 + alpha;
- this.a1 = -2*cosw0;
- this.a2 = 1 - alpha;
- break;
- case DSP.NOTCH: // H(s) = (s^2 + 1) / (s^2 + s/Q + 1)
- this.b0 = 1;
- this.b1 = -2*cosw0;
- this.b2 = 1;
- this.a0 = 1 + alpha;
- this.a1 = -2*cosw0;
- this.a2 = 1 - alpha;
- break;
- case DSP.APF: // H(s) = (s^2 - s/Q + 1) / (s^2 + s/Q + 1)
- this.b0 = 1 - alpha;
- this.b1 = -2*cosw0;
- this.b2 = 1 + alpha;
- this.a0 = 1 + alpha;
- this.a1 = -2*cosw0;
- this.a2 = 1 - alpha;
- break;
- case DSP.PEAKING_EQ: // H(s) = (s^2 + s*(A/Q) + 1) / (s^2 + s/(A*Q) + 1)
- this.b0 = 1 + alpha*A;
- this.b1 = -2*cosw0;
- this.b2 = 1 - alpha*A;
- this.a0 = 1 + alpha/A;
- this.a1 = -2*cosw0;
- this.a2 = 1 - alpha/A;
- break;
- case DSP.LOW_SHELF: // H(s) = A * (s^2 + (sqrt(A)/Q)*s + A)/(A*s^2 + (sqrt(A)/Q)*s + 1)
- coeff = sinw0 * Math.sqrt( (A^2 + 1)*(1/this.S - 1) + 2*A );
- this.b0 = A*((A+1) - (A-1)*cosw0 + coeff);
- this.b1 = 2*A*((A-1) - (A+1)*cosw0);
- this.b2 = A*((A+1) - (A-1)*cosw0 - coeff);
- this.a0 = (A+1) + (A-1)*cosw0 + coeff;
- this.a1 = -2*((A-1) + (A+1)*cosw0);
- this.a2 = (A+1) + (A-1)*cosw0 - coeff;
- break;
- case DSP.HIGH_SHELF: // H(s) = A * (A*s^2 + (sqrt(A)/Q)*s + 1)/(s^2 + (sqrt(A)/Q)*s + A)
- coeff = sinw0 * Math.sqrt( (A^2 + 1)*(1/this.S - 1) + 2*A );
- this.b0 = A*((A+1) + (A-1)*cosw0 + coeff);
- this.b1 = -2*A*((A-1) + (A+1)*cosw0);
- this.b2 = A*((A+1) + (A-1)*cosw0 - coeff);
- this.a0 = (A+1) - (A-1)*cosw0 + coeff;
- this.a1 = 2*((A-1) - (A+1)*cosw0);
- this.a2 = (A+1) - (A-1)*cosw0 - coeff;
- break;
- }
-
- this.b0a0 = this.b0/this.a0;
- this.b1a0 = this.b1/this.a0;
- this.b2a0 = this.b2/this.a0;
- this.a1a0 = this.a1/this.a0;
- this.a2a0 = this.a2/this.a0;
- };
- this.process = function(buffer) {
- //y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2]
- // - (a1/a0)*y[n-1] - (a2/a0)*y[n-2]
- var len = buffer.length;
- var output = new Float64Array(len);
- for ( var i=0; i<buffer.length; i++ ) {
- 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;
- this.y_2_l = this.y_1_l;
- this.y_1_l = output[i];
- this.x_2_l = this.x_1_l;
- this.x_1_l = buffer[i];
- }
- return output;
- };
- this.processStereo = function(buffer) {
- //y[n] = (b0/a0)*x[n] + (b1/a0)*x[n-1] + (b2/a0)*x[n-2]
- // - (a1/a0)*y[n-1] - (a2/a0)*y[n-2]
- var len = buffer.length;
- var output = new Float64Array(len);
-
- for (var i = 0; i < len/2; i++) {
- 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;
- this.y_2_l = this.y_1_l;
- this.y_1_l = output[2*i];
- this.x_2_l = this.x_1_l;
- this.x_1_l = buffer[2*i];
- 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;
- this.y_2_r = this.y_1_r;
- this.y_1_r = output[2*i+1];
- this.x_2_r = this.x_1_r;
- this.x_1_r = buffer[2*i+1];
- }
- return output;
- };
- }
- /*
- * Magnitude to decibels
- *
- * Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
- * Copyright 2010 Ricard Marxer. All rights reserved.
- *
- * @buffer array of magnitudes to convert to decibels
- *
- * @returns the array in decibels
- *
- */
- DSP.mag2db = function(buffer) {
- var minDb = -120;
- var minMag = Math.pow(10.0, minDb / 20.0);
- var log = Math.log;
- var max = Math.max;
-
- var result = Float64Array(buffer.length);
- for (var i=0; i<buffer.length; i++) {
- result[i] = 20.0*log(max(buffer[i], minMag));
- }
- return result;
- };
- /*
- * Frequency response
- *
- * Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
- * Copyright 2010 Ricard Marxer. All rights reserved.
- *
- * Calculates the frequency response at the given points.
- *
- * @b b coefficients of the filter
- * @a a coefficients of the filter
- * @w w points (normally between -PI and PI) where to calculate the frequency response
- *
- * @returns the frequency response in magnitude
- *
- */
- DSP.freqz = function(b, a, w) {
- var i, j;
- if (!w) {
- w = Float64Array(200);
- for (i=0;i<w.length; i++) {
- w[i] = DSP.TWO_PI/w.length * i - Math.PI;
- }
- }
- var result = Float64Array(w.length);
-
- var sqrt = Math.sqrt;
- var cos = Math.cos;
- var sin = Math.sin;
-
- for (i=0; i<w.length; i++) {
- var numerator = {real:0.0, imag:0.0};
- for (j=0; j<b.length; j++) {
- numerator.real += b[j] * cos(-j*w[i]);
- numerator.imag += b[j] * sin(-j*w[i]);
- }
- var denominator = {real:0.0, imag:0.0};
- for (j=0; j<a.length; j++) {
- denominator.real += a[j] * cos(-j*w[i]);
- denominator.imag += a[j] * sin(-j*w[i]);
- }
-
- result[i] = sqrt(numerator.real*numerator.real + numerator.imag*numerator.imag) / sqrt(denominator.real*denominator.real + denominator.imag*denominator.imag);
- }
- return result;
- };
- /*
- * Graphical Equalizer
- *
- * Implementation of a graphic equalizer with a configurable bands-per-octave
- * and minimum and maximum frequencies
- *
- * Created by Ricard Marxer <email@ricardmarxer.com> on 2010-05-23.
- * Copyright 2010 Ricard Marxer. All rights reserved.
- *
- */
- function GraphicalEq(sampleRate) {
- this.FS = sampleRate;
- this.minFreq = 40.0;
- this.maxFreq = 16000.0;
- this.bandsPerOctave = 1.0;
- this.filters = [];
- this.freqzs = [];
- this.calculateFreqzs = true;
- this.recalculateFilters = function() {
- var bandCount = Math.round(Math.log(this.maxFreq/this.minFreq) * this.bandsPerOctave/ Math.LN2);
- this.filters = [];
- for (var i=0; i<bandCount; i++) {
- var freq = this.minFreq*(Math.pow(2, i/this.bandsPerOctave));
- var newFilter = new Biquad(DSP.PEAKING_EQ, this.FS);
- newFilter.setDbGain(0);
- newFilter.setBW(1/this.bandsPerOctave);
- newFilter.setF0(freq);
- this.filters[i] = newFilter;
- this.recalculateFreqz(i);
- }
- };
- this.setMinimumFrequency = function(freq) {
- this.minFreq = freq;
- this.recalculateFilters();
- };
- this.setMaximumFrequency = function(freq) {
- this.maxFreq = freq;
- this.recalculateFilters();
- };
- this.setBandsPerOctave = function(bands) {
- this.bandsPerOctave = bands;
- this.recalculateFilters();
- };
- this.setBandGain = function(bandIndex, gain) {
- if (bandIndex < 0 || bandIndex > (this.filters.length-1)) {
- throw "The band index of the graphical equalizer is out of bounds.";
- }
- if (!gain) {
- throw "A gain must be passed.";
- }
-
- this.filters[bandIndex].setDbGain(gain);
- this.recalculateFreqz(bandIndex);
- };
-
- this.recalculateFreqz = function(bandIndex) {
- if (!this.calculateFreqzs) {
- return;
- }
- if (bandIndex < 0 || bandIndex > (this.filters.length-1)) {
- throw "The band index of the graphical equalizer is out of bounds. " + bandIndex + " is out of [" + 0 + ", " + this.filters.length-1 + "]";
- }
-
- if (!this.w) {
- this.w = Float64Array(400);
- for (var i=0; i<this.w.length; i++) {
- this.w[i] = Math.PI/this.w.length * i;
- }
- }
-
- var b = [this.filters[bandIndex].b0, this.filters[bandIndex].b1, this.filters[bandIndex].b2];
- var a = [this.filters[bandIndex].a0, this.filters[bandIndex].a1, this.filters[bandIndex].a2];
- this.freqzs[bandIndex] = DSP.mag2db(DSP.freqz(b, a, this.w));
- };
- this.process = function(buffer) {
- var output = buffer;
- for (var i = 0; i < this.filters.length; i++) {
- output = this.filters[i].process(output);
- }
- return output;
- };
- this.processStereo = function(buffer) {
- var output = buffer;
- for (var i = 0; i < this.filters.length; i++) {
- output = this.filters[i].processStereo(output);
- }
- return output;
- };
- }
- /**
- * MultiDelay effect by Almer Thie (http://code.almeros.com).
- * Copyright 2010 Almer Thie. All rights reserved.
- * Example: http://code.almeros.com/code-examples/delay-firefox-audio-api/
- *
- * This is a delay that feeds it's own delayed signal back into its circular
- * buffer. Also known as a CombFilter.
- *
- * Compatible with interleaved stereo (or more channel) buffers and
- * non-interleaved mono buffers.
- *
- * @param {Number} maxDelayInSamplesSize Maximum possible delay in samples (size of circular buffer)
- * @param {Number} delayInSamples Initial delay in samples
- * @param {Number} masterVolume Initial master volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- * @param {Number} delayVolume Initial feedback delay volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- *
- * @constructor
- */
- function MultiDelay(maxDelayInSamplesSize, delayInSamples, masterVolume, delayVolume) {
- this.delayBufferSamples = new Float64Array(maxDelayInSamplesSize); // The maximum size of delay
- this.delayInputPointer = delayInSamples;
- this.delayOutputPointer = 0;
-
- this.delayInSamples = delayInSamples;
- this.masterVolume = masterVolume;
- this.delayVolume = delayVolume;
- }
- /**
- * Change the delay time in samples.
- *
- * @param {Number} delayInSamples Delay in samples
- */
- MultiDelay.prototype.setDelayInSamples = function (delayInSamples) {
- this.delayInSamples = delayInSamples;
-
- this.delayInputPointer = this.delayOutputPointer + delayInSamples;
- if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
- this.delayInputPointer = this.delayInputPointer - this.delayBufferSamples.length;
- }
- };
- /**
- * Change the master volume.
- *
- * @param {Number} masterVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- */
- MultiDelay.prototype.setMasterVolume = function(masterVolume) {
- this.masterVolume = masterVolume;
- };
- /**
- * Change the delay feedback volume.
- *
- * @param {Number} delayVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- */
- MultiDelay.prototype.setDelayVolume = function(delayVolume) {
- this.delayVolume = delayVolume;
- };
- /**
- * Process a given interleaved or mono non-interleaved float value Array and adds the delayed audio.
- *
- * @param {Array} samples Array containing Float values or a Float64Array
- *
- * @returns A new Float64Array interleaved or mono non-interleaved as was fed to this function.
- */
- MultiDelay.prototype.process = function(samples) {
- // NB. Make a copy to put in the output samples to return.
- var outputSamples = new Float64Array(samples.length);
- for (var i=0; i<samples.length; i++) {
- // delayBufferSamples could contain initial NULL's, return silence in that case
- var delaySample = (this.delayBufferSamples[this.delayOutputPointer] === null ? 0.0 : this.delayBufferSamples[this.delayOutputPointer]);
-
- // Mix normal audio data with delayed audio
- var sample = (delaySample * this.delayVolume) + samples[i];
-
- // Add audio data with the delay in the delay buffer
- this.delayBufferSamples[this.delayInputPointer] = sample;
-
- // Return the audio with delay mix
- outputSamples[i] = sample * this.masterVolume;
-
- // Manage circulair delay buffer pointers
- this.delayInputPointer++;
- if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
- this.delayInputPointer = 0;
- }
-
- this.delayOutputPointer++;
- if (this.delayOutputPointer >= this.delayBufferSamples.length-1) {
- this.delayOutputPointer = 0;
- }
- }
-
- return outputSamples;
- };
- /**
- * SingleDelay effect by Almer Thie (http://code.almeros.com).
- * Copyright 2010 Almer Thie. All rights reserved.
- * Example: See usage in Reverb class
- *
- * This is a delay that does NOT feeds it's own delayed signal back into its
- * circular buffer, neither does it return the original signal. Also known as
- * an AllPassFilter(?).
- *
- * Compatible with interleaved stereo (or more channel) buffers and
- * non-interleaved mono buffers.
- *
- * @param {Number} maxDelayInSamplesSize Maximum possible delay in samples (size of circular buffer)
- * @param {Number} delayInSamples Initial delay in samples
- * @param {Number} delayVolume Initial feedback delay volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- *
- * @constructor
- */
- function SingleDelay(maxDelayInSamplesSize, delayInSamples, delayVolume) {
- this.delayBufferSamples = new Float64Array(maxDelayInSamplesSize); // The maximum size of delay
- this.delayInputPointer = delayInSamples;
- this.delayOutputPointer = 0;
-
- this.delayInSamples = delayInSamples;
- this.delayVolume = delayVolume;
- }
- /**
- * Change the delay time in samples.
- *
- * @param {Number} delayInSamples Delay in samples
- */
- SingleDelay.prototype.setDelayInSamples = function(delayInSamples) {
- this.delayInSamples = delayInSamples;
- this.delayInputPointer = this.delayOutputPointer + delayInSamples;
- if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
- this.delayInputPointer = this.delayInputPointer - this.delayBufferSamples.length;
- }
- };
- /**
- * Change the return signal volume.
- *
- * @param {Number} delayVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- */
- SingleDelay.prototype.setDelayVolume = function(delayVolume) {
- this.delayVolume = delayVolume;
- };
- /**
- * Process a given interleaved or mono non-interleaved float value Array and
- * returns the delayed audio.
- *
- * @param {Array} samples Array containing Float values or a Float64Array
- *
- * @returns A new Float64Array interleaved or mono non-interleaved as was fed to this function.
- */
- SingleDelay.prototype.process = function(samples) {
- // NB. Make a copy to put in the output samples to return.
- var outputSamples = new Float64Array(samples.length);
- for (var i=0; i<samples.length; i++) {
- // Add audio data with the delay in the delay buffer
- this.delayBufferSamples[this.delayInputPointer] = samples[i];
-
- // delayBufferSamples could contain initial NULL's, return silence in that case
- var delaySample = this.delayBufferSamples[this.delayOutputPointer];
- // Return the audio with delay mix
- outputSamples[i] = delaySample * this.delayVolume;
- // Manage circulair delay buffer pointers
- this.delayInputPointer++;
- if (this.delayInputPointer >= this.delayBufferSamples.length-1) {
- this.delayInputPointer = 0;
- }
-
- this.delayOutputPointer++;
- if (this.delayOutputPointer >= this.delayBufferSamples.length-1) {
- this.delayOutputPointer = 0;
- }
- }
-
- return outputSamples;
- };
- /**
- * Reverb effect by Almer Thie (http://code.almeros.com).
- * Copyright 2010 Almer Thie. All rights reserved.
- * Example: http://code.almeros.com/code-examples/reverb-firefox-audio-api/
- *
- * This reverb consists of 6 SingleDelays, 6 MultiDelays and an IIRFilter2
- * for each of the two stereo channels.
- *
- * Compatible with interleaved stereo buffers only!
- *
- * @param {Number} maxDelayInSamplesSize Maximum possible delay in samples (size of circular buffers)
- * @param {Number} delayInSamples Initial delay in samples for internal (Single/Multi)delays
- * @param {Number} masterVolume Initial master volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- * @param {Number} mixVolume Initial reverb signal mix volume. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- * @param {Number} delayVolume Initial feedback delay volume for internal (Single/Multi)delays. Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- * @param {Number} dampFrequency Initial low pass filter frequency. 0 to 44100 (depending on your maximum sampling frequency)
- *
- * @constructor
- */
- function Reverb(maxDelayInSamplesSize, delayInSamples, masterVolume, mixVolume, delayVolume, dampFrequency) {
- this.delayInSamples = delayInSamples;
- this.masterVolume = masterVolume;
- this.mixVolume = mixVolume;
- this.delayVolume = delayVolume;
- this.dampFrequency = dampFrequency;
-
- this.NR_OF_MULTIDELAYS = 6;
- this.NR_OF_SINGLEDELAYS = 6;
-
- this.LOWPASSL = new IIRFilter2(DSP.LOWPASS, dampFrequency, 0, 44100);
- this.LOWPASSR = new IIRFilter2(DSP.LOWPASS, dampFrequency, 0, 44100);
-
- this.singleDelays = [];
-
- var i, delayMultiply;
- for (i = 0; i < this.NR_OF_SINGLEDELAYS; i++) {
- delayMultiply = 1.0 + (i/7.0); // 1.0, 1.1, 1.2...
- this.singleDelays[i] = new SingleDelay(maxDelayInSamplesSize, Math.round(this.delayInSamples * delayMultiply), this.delayVolume);
- }
-
- this.multiDelays = [];
- for (i = 0; i < this.NR_OF_MULTIDELAYS; i++) {
- delayMultiply = 1.0 + (i/10.0); // 1.0, 1.1, 1.2...
- this.multiDelays[i] = new MultiDelay(maxDelayInSamplesSize, Math.round(this.delayInSamples * delayMultiply), this.masterVolume, this.delayVolume);
- }
- }
- /**
- * Change the delay time in samples as a base for all delays.
- *
- * @param {Number} delayInSamples Delay in samples
- */
- Reverb.prototype.setDelayInSamples = function (delayInSamples){
- this.delayInSamples = delayInSamples;
- var i, delayMultiply;
-
- for (i = 0; i < this.NR_OF_SINGLEDELAYS; i++) {
- delayMultiply = 1.0 + (i/7.0); // 1.0, 1.1, 1.2...
- this.singleDelays[i].setDelayInSamples( Math.round(this.delayInSamples * delayMultiply) );
- }
-
- for (i = 0; i < this.NR_OF_MULTIDELAYS; i++) {
- delayMultiply = 1.0 + (i/10.0); // 1.0, 1.1, 1.2...
- this.multiDelays[i].setDelayInSamples( Math.round(this.delayInSamples * delayMultiply) );
- }
- };
- /**
- * Change the master volume.
- *
- * @param {Number} masterVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- */
- Reverb.prototype.setMasterVolume = function (masterVolume){
- this.masterVolume = masterVolume;
- };
- /**
- * Change the reverb signal mix level.
- *
- * @param {Number} mixVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- */
- Reverb.prototype.setMixVolume = function (mixVolume){
- this.mixVolume = mixVolume;
- };
- /**
- * Change all delays feedback volume.
- *
- * @param {Number} delayVolume Float value: 0.0 (silence), 1.0 (normal), >1.0 (amplify)
- */
- Reverb.prototype.setDelayVolume = function (delayVolume){
- this.delayVolume = delayVolume;
-
- var i;
- for (i = 0; i<this.NR_OF_SINGLEDELAYS; i++) {
- this.singleDelays[i].setDelayVolume(this.delayVolume);
- }
-
- for (i = 0; i<this.NR_OF_MULTIDELAYS; i++) {
- this.multiDelays[i].setDelayVolume(this.delayVolume);
- }
- };
- /**
- * Change the Low Pass filter frequency.
- *
- * @param {Number} dampFrequency low pass filter frequency. 0 to 44100 (depending on your maximum sampling frequency)
- */
- Reverb.prototype.setDampFrequency = function (dampFrequency){
- this.dampFrequency = dampFrequency;
-
- this.LOWPASSL.set(dampFrequency, 0);
- this.LOWPASSR.set(dampFrequency, 0);
- };
- /**
- * Process a given interleaved float value Array and copies and adds the reverb signal.
- *
- * @param {Array} samples Array containing Float values or a Float64Array
- *
- * @returns A new Float64Array interleaved buffer.
- */
- Reverb.prototype.process = function (interleavedSamples){
- // NB. Make a copy to put in the output samples to return.
- var outputSamples = new Float64Array(interleavedSamples.length);
-
- // Perform low pass on the input samples to mimick damp
- var leftRightMix = DSP.deinterleave(interleavedSamples);
- this.LOWPASSL.process( leftRightMix[DSP.LEFT] );
- this.LOWPASSR.process( leftRightMix[DSP.RIGHT] );
- var filteredSamples = DSP.interleave(leftRightMix[DSP.LEFT], leftRightMix[DSP.RIGHT]);
- var i;
- // Process MultiDelays in parallel
- for (i = 0; i<this.NR_OF_MULTIDELAYS; i++) {
- // Invert the signal of every even multiDelay
- outputSamples = DSP.mixSampleBuffers(outputSamples, this.multiDelays[i].process(filteredSamples), 2%i === 0, this.NR_OF_MULTIDELAYS);
- }
-
- // Process SingleDelays in series
- var singleDelaySamples = new Float64Array(outputSamples.length);
- for (i = 0; i<this.NR_OF_SINGLEDELAYS; i++) {
- // Invert the signal of every even singleDelay
- singleDelaySamples = DSP.mixSampleBuffers(singleDelaySamples, this.singleDelays[i].process(outputSamples), 2%i === 0, 1);
- }
- // Apply the volume of the reverb signal
- for (i = 0; i<singleDelaySamples.length; i++) {
- singleDelaySamples[i] *= this.mixVolume;
- }
-
- // Mix the original signal with the reverb signal
- outputSamples = DSP.mixSampleBuffers(singleDelaySamples, interleavedSamples, 0, 1);
- // Apply the master volume to the complete signal
- for (i = 0; i<outputSamples.length; i++) {
- outputSamples[i] *= this.masterVolume;
- }
-
- return outputSamples;
- };
- if (typeof module !== 'undefined' && module && module.exports) {
- module.exports = {
- DSP: DSP,
- DFT: DFT,
- FFT: FFT,
- RFFT: RFFT,
- Sampler: Sampler,
- Oscillator: Oscillator,
- ADSR: ADSR,
- IIRFilter: IIRFilter,
- IIRFilter2: IIRFilter2,
- WindowFunction: WindowFunction,
- sinh: sinh,
- Biquad: Biquad,
- GraphicalEq: GraphicalEq,
- MultiDelay: MultiDelay,
- SingleDelay: SingleDelay,
- Reverb: Reverb
- };
- }
|