UNPKG

corbanbrook-fft

Version:

Real valued split radix FFT with inverse.

425 lines (346 loc) 11.8 kB
/* * RFFT Originally found in DSP.js, IRFFT was on a PR of the repo and needed some fixing. * * RFFT 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 * * @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 separate 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) { this.bufferSize = bufferSize; this.trans = new Float32Array(bufferSize); this.itrans = new Float32Array(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]; }; // don't use a lookup table to do the permute, use this instead // the inverse transform needs to do this in place so we have this this._reverseBinPermuteInPlace = function (buf) { var bufferSize = this.bufferSize, halfSize = bufferSize >>> 1, nm1 = bufferSize - 1, i = 1, r = 0, h, t; do { r += halfSize; t = buf[i]; buf[i] = buf[r]; buf[r] = t; i++; h = halfSize << 1; while (h = h >> 1, !((r ^= h) & h)); if (r >= i) { t = buf[i]; buf[i] = buf[r]; buf[r] = t; t = buf[nm1-i]; buf[nm1-i] = buf[nm1-r]; buf[nm1-r] = t; } i++; } while (i < halfSize); }; } // 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] // // note before using RFFT.trans you need to scale it, however since [0-1] // is frequently not the range you want, or you often want to work with it in // some other way we leave it unscaled for speed (this way we make one fewer // pass, if you're willing to remeber to scale it yourself. RFFT.prototype.forward = function(buffer) { var n = this.bufferSize, 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); 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); } } return this.trans; }; RFFT.prototype.scaleTrans = function(trans) { var i=0,bSi = 1.0/this.bufferSize, x = trans; while(i < x.length) { x[i] *= bSi; i++; } return x; }; // input must have ordering as in output of the forward version, RFFT.prototype.inverse = function(buffer) { var n = this.bufferSize, x = this.itrans, TWO_PI = 2*Math.PI, n2, n4, n8, nn, t1, t2, t3, t4, t5, j, i0, i1, i2, i3, i4, i5, i6, i7, i8, ud, ix, id, st1, cc1, ss1, cc3, ss3, e, a; x.set(buffer); nn = n>>>1; n2 = n<<1; while ( nn >>>= 1 ) { ix = 0; id = n2; n2 >>>= 1; n4 = n2>>>2; n8 = n4>>>1; do // ix { for (i0=ix; i0<n; i0+=id) { i1 = i0; i2 = i1 + n4; i3 = i2 + n4; i4 = i3 + n4; //sumdiff3(x[i1], x[i3], t1);// {a, b, d} <--| {a+b, b, a-b} t1 = x[i1] - x[i3]; x[i1] += x[i3]; x[i2] += x[i2]; x[i4] += x[i4]; //sumdiff3_r(x[i4], t1, x[i3]);// {a,b,d} <--| {a+b, b, b-a} x[i3] = t1 - x[i4]; x[i4] += t1; if ( n4!=1 ) // note: optimise (Note this comment from original C++) { i1 += n8; i2 += n8; i3 += n8; i4 += n8; //sumdiff3(x[i1], x[i2], t1); // {a, b, d} <--| {a+b, b, a-b} t1 = x[i1] - x[i2]; x[i1] += x[i2]; //sumdiff(a, b, &s, &d) {s, d} <--| {a+b, a-b} //sumdiff(x[i4], x[i3], t2, x[i2]); t2 = x[i4] + x[i3]; x[i2] = x[i4] - x[i3]; t2 = -t2 * Math.SQRT2; t1 *= Math.SQRT2; //sumdiff(a, b, &s, &d) {s, d} <--| {a+b, a-b} //sumdiff(t2, t1, x[i3], x[i4]); x[i3] = t2 + t1; x[i4] = t2 - t1; } } ix = (id<<1) - n2; id <<= 2; } while ( ix<n ); e = TWO_PI/n2; for (j=1; j<n8; j++) { a = j*e; ss1 = Math.sin(a); cc1 = Math.cos(a); ss3 = Math.sin(3*a); cc3 = Math.cos(3*a); cc3 = 4*cc1*(cc1*cc1-0.75); ss3 = 4*ss1*(0.75-ss1*ss1); ix = 0; id = n2<<1; do // ix-loop { 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; //sumdiff3(x[i1], x[i6], t1); // {a, b, d} <--| {a+b, b, a-b} t1 = x[i1] - x[i6]; x[i1] += x[i6]; //sumdiff3(x[i5], x[i2], t2); // {a, b, d} <--| {a+b, b, a-b} t2 = x[i5] - x[i2]; x[i5] += x[i2]; //t2 = x[i5] + x[i2]; x[i5] = x[i5] - x[i2]; //sumdiff(a, b, &s, &d) {s, d} <--| {a+b, a-b} //sumdiff(x[i8], x[i3], t3, x[i6]); //sumdiff(x[i4], x[i7], t4, x[i2]); t3 = x[i8] + x[i3]; x[i6] = x[i8] - x[i3]; t4 = x[i4] + x[i7]; x[i2] = x[i4] - x[i7]; //sumdiff3(t1, t4, t5); // {a, b, d} <--| {a+b, b, a-b} t5 = t1 - t4; t1 += t4; //sumdiff3(t2, t3, t4); // {a, b, d} <--| {a+b, b, a-b} t4 = t2 - t3; t2 += t3; //cmult(c, s, x, y, &u, &v) {u,v} <--| {x*c-y*s, x*s+y*c} //cmult(ss1, cc1, t5, t4, x[i7], x[i3]); //cmult(cc3, ss3, t1, t2, x[i4], x[i8]); x[i7] = t5*ss1 - t4*cc1; x[i3] = t5*cc1 + t4*ss1; x[i4] = t1*cc3 - t2*ss3; x[i8] = t1*ss3 + t2*cc3; } ix = (id<<1) - n2; id <<= 2; } while ( ix < n ); } } for (ix=0, id=4; ix<n; id*=4) { for (i0=ix; i0<n; i0+=id) { // sumdiff(&a, &b) {a, b} <--| {a+b, a-b} //sumdiff(x[i0], x[i0+1]); st1 = x[i0] - x[i0+1]; x[i0] += x[i0+1]; x[i0+1] = st1; } ix = 2*(id-1); } this._reverseBinPermuteInPlace(x); return x; }; module.exports = RFFT;