/* For Copyright and License see LICENSE.txt and COPYING.txt in the root directory */ package com.nerdscentral.audio.pitch.algorithm; import com.nerdscentral.data.OffHeapArray; public class OffHeapFFT implements AutoCloseable { private final long n, m; // Lookup tables. Only need to recompute when size of FFT changes. private final OffHeapArray cos; private final OffHeapArray sin; private volatile boolean closed = false; public long size() { return n; } public OffHeapFFT(long n1, boolean isForward) { this.n = n1; this.m = (int) (Math.log(n1) / Math.log(2)); // Make sure n is a power of 2 if (n1 != (1 << m)) throw new RuntimeException(Messages.getString("CacheableFFT.0")); //$NON-NLS-1$ cos = OffHeapArray.doubleArray(n1 >> 1); sin = OffHeapArray.doubleArray(n1 >> 1); double dir = isForward ? -2 * Math.PI : 2 * Math.PI; for (long i = 0; i < n1 >> 1; ++i) { cos.setDouble(i, Math.cos(dir * i / n1)); sin.setDouble(i, Math.sin(dir * i / n1)); } } public void fft(OffHeapArray x, OffHeapArray y) { if (closed) throw new RuntimeException(Messages.getString("OffHeapFFT.0")); //$NON-NLS-1$ long i, j, k, n1, n2, a; double c, s, t1, t2; x.checkBoundsDouble(0, size()); y.checkBoundsDouble(0, size()); // Bit-reverse j = 0; n2 = n >> 1; for (i = 1; i < n - 1; ++i) { n1 = n2; while (j >= n1) { j = j - n1; n1 >>= 1; } j = j + n1; if (i < j) { t1 = x.getDouble(i); x.setDouble(i, x.getDouble(j)); x.setDouble(j, t1); t1 = y.getDouble(i); y.setDouble(i, y.getDouble(j)); y.setDouble(j, t1); } } n1 = 0; n2 = 1; for (i = 0; i < m; ++i) { n1 = n2; n2 <<= 1; a = 0; for (j = 0; j < n1; j++) { c = cos.getDouble(a); s = sin.getDouble(a); a += 1 << (m - i - 1); for (k = j; k < n; k += n2) { long kn1 = k + n1; t1 = c * x.getDouble(kn1) - s * y.getDouble(kn1); t2 = s * x.getDouble(kn1) + c * y.getDouble(kn1); x.setDouble(kn1, x.getDouble(k) - t1); y.setDouble(kn1, y.getDouble(k) - t2); x.setDouble(k, x.getDouble(k) + t1); y.setDouble(k, y.getDouble(k) + t2); } } } } @Override public void close() throws RuntimeException { if (closed) throw new RuntimeException(Messages.getString("OffHeapFFT.1")); //$NON-NLS-1$ closed = true; sin.close(); cos.close(); } @Override public void finalize() throws RuntimeException { close(); } }