/*
 *  Copyright (c) 2007 - 2008 by Damien Di Fede <ddf@compartmental.net>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as published
 *   by the Free Software Foundation; either version 2 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * 
 * This Code is taken from the source provided with a tutorial by
 * http://www.badlogicgames.com (as of 16.05.2013).
 * The original source can be found here:
 * http://code.google.com/p/audio-analysis/source/browse/trunk/src/com/badlogic/audio/analysis/DFT.java 
 */
package org.wyrez.audio.analysis;

/**
 * DFT stands for Discrete Fourier Transform and is the most widely used Fourier
 * Transform. You will never want to use this class due to the fact that it is a
 * brute force implementation of the DFT and as such is quite slow. Use an FFT
 * instead. This exists primarily as a way to ensure that other implementations
 * of the DFT are working properly. This implementation expects an even
 * <code>timeSize</code> and will throw and IllegalArgumentException if this is
 * not the case.
 *
 * @author Damien Di Fede
 *
 * @see FourierTransform
 * @see FFT
 * @see <a href="http://www.dspguide.com/ch8.htm">The Discrete Fourier
 * Transform</a>
 *
 */
public class DFT extends FourierTransform {

    /**
     * Constructs a DFT that expects audio buffers of length
     * <code>timeSize</code> that have been recorded with a sample rate of
     * <code>sampleRate</code>. Will throw an IllegalArgumentException if
     * <code>timeSize</code> is not even.
     *
     * @param timeSize the length of the audio buffers you plan to analyze
     * @param sampleRate the sample rate of the audio samples you plan to
     * analyze
     */
    public DFT(int timeSize, float sampleRate) {
        super(timeSize, sampleRate);
        if (timeSize % 2 != 0) {
            throw new IllegalArgumentException("DFT: timeSize must be even.");
        }
        buildTrigTables();
    }

    protected void allocateArrays() {
        spectrum = new float[timeSize / 2 + 1];
        real = new float[timeSize / 2 + 1];
        imag = new float[timeSize / 2 + 1];
    }

    /**
     * Not currently implemented.
     */
    public void scaleBand(int i, float s) {
    }

    /**
     * Not currently implemented.
     */
    public void setBand(int i, float a) {
    }

    public void forward(float[] samples) {
        if (samples.length != timeSize) {
            throw new IllegalArgumentException("DFT.forward: The length of the passed sample buffer must be equal to DFT.timeSize().");
        }
        doWindow(samples);
        int N = samples.length;
        for (int f = 0; f <= N / 2; f++) {
            real[f] = 0.0f;
            imag[f] = 0.0f;
            for (int t = 0; t < N; t++) {
                real[f] += samples[t] * cos(t * f);
                imag[f] += samples[t] * -sin(t * f);
            }
        }
        fillSpectrum();
    }

    public void inverse(float[] buffer) {
        int N = buffer.length;
        real[0] /= N;
        imag[0] = -imag[0] / (N / 2);
        real[N / 2] /= N;
        imag[N / 2] = -imag[0] / (N / 2);
        for (int i = 0; i < N / 2; i++) {
            real[i] /= (N / 2);
            imag[i] = -imag[i] / (N / 2);
        }
        for (int t = 0; t < N; t++) {
            buffer[t] = 0.0f;
            for (int f = 0; f < N / 2; f++) {
                buffer[t] += real[f] * cos(t * f) + imag[f] * sin(t * f);
            }
        }
    }
    // lookup table data and functions
    private float[] sinlookup;
    private float[] coslookup;

    private void buildTrigTables() {
        int N = spectrum.length * timeSize;
        sinlookup = new float[N];
        coslookup = new float[N];
        for (int i = 0; i < N; i++) {
            sinlookup[i] = (float) Math.sin(i * TWO_PI / timeSize);
            coslookup[i] = (float) Math.cos(i * TWO_PI / timeSize);
        }
    }

    private float sin(int i) {
        return sinlookup[i];
    }

    private float cos(int i) {
        return coslookup[i];
    }
}
