processingFinished
method.
+ *
+ * @param audioProcessor
+ * The AudioProcessor to remove.
+ */
+ public void removeAudioProcessor(final AudioProcessor audioProcessor) {
+ audioProcessors.remove(audioProcessor);
+ audioProcessor.processingFinished();
+ LOG.fine("Remove an audioprocessor to the list of processors: " + audioProcessor);
+ }
+
+ public void run() {
+
+ int bytesRead = 0;
+
+ if(bytesToSkip!=0){
+ skipToStart();
+ }
+
+ //Read the first (and in some cases last) audio block.
+ try {
+ //needed to get correct time info when skipping first x seconds
+ audioEvent.setBytesProcessed(bytesProcessed);
+ bytesRead = readNextAudioBlock();
+ } catch (IOException e) {
+ String message="Error while reading audio input stream: " + e.getMessage();
+ LOG.warning(message);
+ throw new Error(message);
+ }
+
+ // As long as the stream has not ended
+ while (bytesRead != 0 && !stopped) {
+
+ //Makes sure the right buffers are processed, they can be changed by audio processors.
+ for (final AudioProcessor processor : audioProcessors) {
+ if(!processor.process(audioEvent)){
+ //skip to the next audio processors if false is returned.
+ break;
+ }
+ }
+
+ if(!stopped){
+ //Update the number of bytes processed;
+ bytesProcessed += bytesRead;
+ audioEvent.setBytesProcessed(bytesProcessed);
+
+ // Read, convert and process consecutive overlapping buffers.
+ // Slide the buffer.
+ try {
+ bytesRead = readNextAudioBlock();
+ audioEvent.setOverlap(floatOverlap);
+ } catch (IOException e) {
+ String message="Error while reading audio input stream: " + e.getMessage();
+ LOG.warning(message);
+ throw new Error(message);
+ }
+ }
+ }
+
+ // Notify all processors that no more data is available.
+ // when stop() is called processingFinished is called explicitly, no need to do this again.
+ // The explicit call is to prevent timing issues.
+ if(!stopped){
+ stop();
+ }
+ }
+
+
+ private void skipToStart() {
+ long skipped = 0L;
+ try{
+ skipped = audioInputStream.skip(bytesToSkip);
+ if(skipped !=bytesToSkip){
+ throw new IOException();
+ }
+ bytesProcessed += bytesToSkip;
+ }catch(IOException e){
+ String message=String.format("Did not skip the expected amount of bytes, %d skipped, %d expected!", skipped,bytesToSkip);
+ LOG.warning(message);
+ throw new Error(message);
+ }
+ }
+
+ /**
+ * Stops dispatching audio data.
+ */
+ public void stop() {
+ stopped = true;
+ for (final AudioProcessor processor : audioProcessors) {
+ processor.processingFinished();
+ }
+ try {
+ audioInputStream.close();
+ } catch (IOException e) {
+ LOG.log(Level.SEVERE, "Closing audio stream error.", e);
+ }
+ }
+
+ /**
+ * Reads the next audio block. It tries to read the number of bytes defined
+ * by the audio buffer size minus the overlap. If the expected number of
+ * bytes could not be read either the end of the stream is reached or
+ * something went wrong.
+ *
+ * The behavior for the first and last buffer is defined by their corresponding the zero pad settings. The method also handles the case if
+ * the first buffer is also the last.
+ *
+ * @return The number of bytes read.
+ * @throws IOException
+ * When something goes wrong while reading the stream. In
+ * particular, an IOException is thrown if the input stream has
+ * been closed.
+ */
+ private int readNextAudioBlock() throws IOException {
+ assert floatOverlap < audioFloatBuffer.length;
+
+ // Is this the first buffer?
+ boolean isFirstBuffer = (bytesProcessed ==0 || bytesProcessed == bytesToSkip);
+
+ final int offsetInBytes;
+
+ final int offsetInSamples;
+
+ final int bytesToRead;
+ //Determine the amount of bytes to read from the stream
+ if(isFirstBuffer && !zeroPadFirstBuffer){
+ //If this is the first buffer and we do not want to zero pad the
+ //first buffer then read a full buffer
+ bytesToRead = audioByteBuffer.length;
+ // With an offset in bytes of zero;
+ offsetInBytes = 0;
+ offsetInSamples=0;
+ }else{
+ //In all other cases read the amount of bytes defined by the step size
+ bytesToRead = byteStepSize;
+ offsetInBytes = byteOverlap;
+ offsetInSamples = floatOverlap;
+ }
+
+ //Shift the audio information using array copy since it is probably faster than manually shifting it.
+ // No need to do this on the first buffer
+ if(!isFirstBuffer && audioFloatBuffer.length == floatOverlap + floatStepSize ){
+ System.arraycopy(audioFloatBuffer,floatStepSize, audioFloatBuffer,0 ,floatOverlap);
+ /*
+ for(int i = floatStepSize ; i < floatStepSize+floatOverlap ; i++){
+ audioFloatBuffer[i-floatStepSize] = audioFloatBuffer[i];
+ }*/
+ }
+
+ // Total amount of bytes read
+ int totalBytesRead = 0;
+
+ // The amount of bytes read from the stream during one iteration.
+ int bytesRead=0;
+
+ // Is the end of the stream reached?
+ boolean endOfStream = false;
+
+ // Always try to read the 'bytesToRead' amount of bytes.
+ // unless the stream is closed (stopped is true) or no bytes could be read during one iteration
+ while(!stopped && !endOfStream && totalBytesRead+ * AudioProcessors are responsible for actual digital signal processing. The + * interface is simple: a process method that works on an AudioEvent object. + * The AudioEvent contains a buffer with some floats and the same information in + * raw bytes. + *
+ *+ * AudioProcessors are meant to be chained e.g. execute an effect and + * then play the sound. The chain of audio processor can be interrupted by returning + * false in the process methods. + *
+ * @author Joren Six + */ +public interface AudioProcessor { + + /** + * Process the audio event. Do the actual signal processing on an + * (optionally) overlapping buffer. + * + * @param audioEvent + * The audio event that contains audio data. + * @return False if the chain needs to stop here, true otherwise. This can + * be used to implement e.g. a silence detector. + */ + boolean process(AudioEvent audioEvent); + + /** + * Notify the AudioProcessor that no more data is available and processing + * has finished. Can be used to deallocate resources or cleanup. + */ + void processingFinished(); +} diff --git a/app/src/main/java/be/tarsos/dsp/BitDepthProcessor.java b/app/src/main/java/be/tarsos/dsp/BitDepthProcessor.java new file mode 100644 index 0000000..ada4c62 --- /dev/null +++ b/app/src/main/java/be/tarsos/dsp/BitDepthProcessor.java @@ -0,0 +1,72 @@ +/* +* _______ _____ _____ _____ +* |__ __| | __ \ / ____| __ \ +* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) | +* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/ +* | | (_| | | \__ \ (_) \__ \ |__| |____) | | +* |_|\__,_|_| |___/\___/|___/_____/|_____/|_| +* +* ------------------------------------------------------------- +* +* TarsosDSP is developed by Joren Six at IPEM, University Ghent +* +* ------------------------------------------------------------- +* +* Info: http://0110.be/tag/TarsosDSP +* Github: https://github.com/JorenSix/TarsosDSP +* Releases: http://0110.be/releases/TarsosDSP/ +* +* TarsosDSP includes modified source code by various authors, +* for credits and info, see README. +* +*/ + +package be.tarsos.dsp; + +/** + * Can be used to show the effect of bit depth modification in real-time. + * It simply transforms every sample to the requested bit depth. + * @author Joren Six + */ +public class BitDepthProcessor implements AudioProcessor { + + int bitDepth = 16; + + /** + * Set a new bit depth + * @param newBitDepth The new bit depth. + */ + public void setBitDepth(int newBitDepth){ + this.bitDepth = newBitDepth; + } + + /** + * The current bit depth + * @return returns the current bit depth. + */ + public int getBitDepth(){ + return this.bitDepth; + } + + + @Override + public boolean process(AudioEvent audioEvent) { + float[] buffer = audioEvent.getFloatBuffer(); + //For e.g. a bith depth of 3, the factor is + // 2^3 - 1 = 7 + float factor = (float) Math.pow(2, bitDepth)/2.0f - 1; + + for(int i = 0 ; i < buffer.length ; i++){ + //the float is scaled to the bith depth + // e.g. if the bit depth is 3 and the value is 0.3: + // ((int)(0.3 * 7)) / 7 = 0.28 + buffer[i]=((int) (buffer[i] * factor))/factor; + } + return true; + } + + @Override + public void processingFinished() { + + } +} diff --git a/app/src/main/java/be/tarsos/dsp/ConstantQ.java b/app/src/main/java/be/tarsos/dsp/ConstantQ.java new file mode 100644 index 0000000..5394c4d --- /dev/null +++ b/app/src/main/java/be/tarsos/dsp/ConstantQ.java @@ -0,0 +1,406 @@ +/* +* _______ _____ _____ _____ +* |__ __| | __ \ / ____| __ \ +* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) | +* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/ +* | | (_| | | \__ \ (_) \__ \ |__| |____) | | +* |_|\__,_|_| |___/\___/|___/_____/|_____/|_| +* +* ------------------------------------------------------------- +* +* TarsosDSP is developed by Joren Six at IPEM, University Ghent +* +* ------------------------------------------------------------- +* +* Info: http://0110.be/tag/TarsosDSP +* Github: https://github.com/JorenSix/TarsosDSP +* Releases: http://0110.be/releases/TarsosDSP/ +* +* TarsosDSP includes modified source code by various authors, +* for credits and info, see README. +* +*/ + +/* +* _______ _____ _____ _____ +* |__ __| | __ \ / ____| __ \ +* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) | +* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/ +* | | (_| | | \__ \ (_) \__ \ |__| |____) | | +* |_|\__,_|_| |___/\___/|___/_____/|_____/|_| +* +* ----------------------------------------------------------- +* +* TarsosDSP is developed by Joren Six at +* The Royal Academy of Fine Arts & Royal Conservatory, +* University College Ghent, +* Hoogpoort 64, 9000 Ghent - Belgium +* +* http://tarsos.0110.be/tag/TarsosDSP +* https://github.com/JorenSix/TarsosDSP +* http://tarsos.0110.be/releases/TarsosDSP/ +* +*/ +/* + * Copyright (c) 2006, Karl Helgason + * + * 2007/1/8 modified by p.j.leonard + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN + * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package be.tarsos.dsp; + +import be.tarsos.dsp.util.fft.FFT; + +/** + * Implementation of the Constant Q Transform.+ * Judith C. Brown, + * Calculation of a constant Q spectral transform, J. Acoust. Soc. Am., + * 89(1): 425-434, 1991. + *
+ *+ * Judith C. Brown and Miller S. Puckette, An efficient algorithm for the calculation of a constant Q transform, J. + * Acoust. Soc. Am., Vol. 92, No. 5, November 1992 + *
+ *+ * Benjamin Blankertz, The Constant Q Transform + *
+ * + * + * @author Joren Six + * @author Karl Helgason + * @author P.J Leonard + */ +public class ConstantQ implements AudioProcessor { + + + /** + * The minimum frequency, in Hertz. The Constant-Q factors are calculated + * starting from this frequency. + */ + private final float minimumFrequency ; + + /** + * The maximum frequency in Hertz. + */ + private final float maximumFreqency; + + /** + * The length of the underlying FFT. + */ + private int fftLength; + + /** + * Lists the start of each frequency bin, in Hertz. + */ + private final float[] frequencies; + + private final float[][] qKernel; + + private final int[][] qKernel_indexes; + + /** + * The array with constant q coefficients. If you for + * example are interested in coefficients between 256 and 1024 Hz + * (2^8 and 2^10 Hz) and you requested 12 bins per octave, you + * will need 12 bins/octave * 2 octaves * 2 entries/bin = 48 + * places in the output buffer. The coefficient needs two entries + * in the output buffer since they are complex numbers. + */ + private final float[] coefficients; + + /** + * The output buffer with constant q magnitudes. If you for example are + * interested in coefficients between 256 and 1024 Hz (2^8 and 2^10 Hz) and + * you requested 12 bins per octave, you will need 12 bins/octave * 2 + * octaves = 24 places in the output buffer. + */ + private final float[] magnitudes; + + /** + * The number of bins per octave. + */ + private final int binsPerOctave; + + /** + * The underlying FFT object. + */ + private final FFT fft; + + + /** + * Create a new ConstantQ instance + * @param sampleRate The audio sample rate + * @param minFreq The minimum frequency to report in Hz + * @param maxFreq The maximum frequency to report in Hz + * @param binsPerOctave The number of bins per octave + */ + public ConstantQ(float sampleRate, float minFreq, float maxFreq,float binsPerOctave) { + this(sampleRate,minFreq,maxFreq,binsPerOctave,0.001f,1.0f); + } + + /** + * Create a new ConstantQ instance + * @param sampleRate The audio sample rate + * @param minFreq The minimum frequency to report in Hz + * @param maxFreq The maximum frequency to report in Hz + * @param binsPerOctave The number of bins per octave + * @param threshold The threshold used in kernel construction. + * @param spread the spread used to calculate the Constant Q + */ + public ConstantQ(float sampleRate, float minFreq, float maxFreq,float binsPerOctave, float threshold,float spread) { + this.minimumFrequency = minFreq; + this.maximumFreqency = maxFreq; + this.binsPerOctave = (int) binsPerOctave; + + // Calculate Constant Q + double q = 1.0 / (Math.pow(2, 1.0 / binsPerOctave) - 1.0) / spread; + + // Calculate number of output bins + int numberOfBins = (int) Math.ceil(binsPerOctave * Math.log(maximumFreqency / minimumFrequency) / Math.log(2)); + + // Initialize the coefficients array (complex number so 2 x number of bins) + coefficients = new float[numberOfBins*2]; + + // Initialize the magnitudes array + magnitudes = new float[numberOfBins]; + + + // Calculate the minimum length of the FFT to support the minimum + // frequency + float calc_fftlen = (float) Math.ceil(q * sampleRate / minimumFrequency); + + // No need to use power of 2 FFT length. + fftLength = (int) calc_fftlen; + + //System.out.println(fftLength); + //The FFT length needs to be a power of two for performance reasons: + fftLength = (int) Math.pow(2, Math.ceil(Math.log(calc_fftlen) / Math.log(2))); + + // Create FFT object + fft = new FFT(fftLength); + qKernel = new float[numberOfBins][]; + qKernel_indexes = new int[numberOfBins][]; + frequencies = new float[numberOfBins]; + + // Calculate Constant Q kernels + float[] temp = new float[fftLength*2]; + float[] ctemp = new float[fftLength*2]; + int[] cindexes = new int[fftLength]; + for (int i = 0; i < numberOfBins; i++) { + float[] sKernel = temp; + // Calculate the frequency of current bin + frequencies[i] = (float) (minimumFrequency * Math.pow(2, i/binsPerOctave )); + + // Calculate length of window + int len = (int)Math.min(Math.ceil( q * sampleRate / frequencies[i]), fftLength); + + for (int j = 0; j < len; j++) { + + double window = -.5*Math.cos(2.*Math.PI*(double)j/(double)len)+.5;// Hanning Window + // double window = -.46*Math.cos(2.*Math.PI*(double)j/(double)len)+.54; // Hamming Window + + window /= len; + + // Calculate kernel + double x = 2*Math.PI * q * (double)j/(double)len; + sKernel[j*2] = (float) (window * Math.cos(x)); + sKernel[j*2+1] = (float) (window * Math.sin(x)); + } + for (int j = len*2; j < fftLength*2; j++) { + sKernel[j] = 0; + } + + // Perform FFT on kernel + fft.complexForwardTransform(sKernel); + + // Remove all zeros from kernel to improve performance + float[] cKernel = ctemp; + + int k = 0; + for (int j = 0, j2 = sKernel.length - 2; j < sKernel.length/2; j+=2,j2-=2) + { + double absval = Math.sqrt(sKernel[j]*sKernel[j] + sKernel[j+1]*sKernel[j+1]); + absval += Math.sqrt(sKernel[j2]*sKernel[j2] + sKernel[j2+1]*sKernel[j2+1]); + if(absval > threshold) + { + cindexes[k] = j; + cKernel[2*k] = sKernel[j] + sKernel[j2]; + cKernel[2*k + 1] = sKernel[j + 1] + sKernel[j2 + 1]; + k++; + } + } + + sKernel = new float[k * 2]; + int[] indexes = new int[k]; + + if (k * 2 >= 0) System.arraycopy(cKernel, 0, sKernel, 0, k * 2); + System.arraycopy(cindexes, 0, indexes, 0, k); + + // Normalize fft output + for (int j = 0; j < sKernel.length; j++) + sKernel[j] /= fftLength; + + // Perform complex conjugate on sKernel + for (int j = 1; j < sKernel.length; j += 2) + sKernel[j] = -sKernel[j]; + + for (int j = 0; j < sKernel.length; j ++) + sKernel[j] = -sKernel[j]; + + qKernel_indexes[i] = indexes; + qKernel[i] = sKernel; + } + } + + /** + * Take an input buffer with audio and calculate the constant Q + * coefficients. + * + * @param inputBuffer + * The input buffer with audio. + * + * + */ + public void calculate(float[] inputBuffer) { + fft.forwardTransform(inputBuffer); + for (int i = 0; i < qKernel.length; i++) { + float[] kernel = qKernel[i]; + int[] indexes = qKernel_indexes[i]; + float t_r = 0; + float t_i = 0; + for (int j = 0, l = 0; j < kernel.length; j += 2, l++) { + int jj = indexes[l]; + float b_r = inputBuffer[jj]; + float b_i = inputBuffer[jj + 1]; + float k_r = kernel[j]; + float k_i = kernel[j + 1]; + // COMPLEX: T += B * K + t_r += b_r * k_r - b_i * k_i; + t_i += b_r * k_i + b_i * k_r; + } + coefficients[i * 2] = t_r; + coefficients[i * 2 + 1] = t_i; + } + } + + /** + * Take an input buffer with audio and calculate the constant Q magnitudes. + * @param inputBuffer The input buffer with audio. + */ + public void calculateMagintudes(float[] inputBuffer) { + calculate(inputBuffer); + for(int i = 0 ; i < magnitudes.length ; i++){ + magnitudes[i] = (float) Math.sqrt(coefficients[i*2] * coefficients[i*2] + coefficients[i*2+1] * coefficients[i*2+1]); + } + } + + + @Override + public boolean process(AudioEvent audioEvent) { + float[] audioBuffer = audioEvent.getFloatBuffer().clone(); + if(audioBuffer.length != getFFTlength()){ + throw new IllegalArgumentException(String.format("The length of the fft (%d) should be the same as the length of the audio buffer (%d)",getFFTlength(),audioBuffer.length)); + } + calculateMagintudes(audioBuffer); + return true; + } + + @Override + public void processingFinished() { + // Do nothing. + } + + //----GETTERS + + /** + * @return The list of starting frequencies for each band. In Hertz. + */ + public float[] getFreqencies() { + return frequencies; + } + + /** + * Returns the Constant Q magnitudes calculated for the previous audio + * buffer. Beware: the array is reused for performance reasons. If your need + * to cache your results, please copy the array. + * @return The output buffer with constant q magnitudes. If you for example are + * interested in coefficients between 256 and 1024 Hz (2^8 and 2^10 Hz) and + * you requested 12 bins per octave, you will need 12 bins/octave * 2 + * octaves = 24 places in the output buffer. + */ + public float[] getMagnitudes() { + return magnitudes; + } + + + /** + * Return the Constant Q coefficients calculated for the previous audio + * buffer. Beware: the array is reused for performance reasons. If your need + * to cache your results, please copy the array. + * + * @return The array with constant q coefficients. If you for example are + * interested in coefficients between 256 and 1024 Hz (2^8 and 2^10 + * Hz) and you requested 12 bins per octave, you will need 12 + * bins/octave * 2 octaves * 2 entries/bin = 48 places in the output + * buffer. The coefficient needs two entries in the output buffer + * since they are complex numbers. + */ + public float[] getCoefficients() { + return coefficients; + } + + /** + * @return The number of coefficients, output bands. + */ + public int getNumberOfOutputBands() { + return frequencies.length; + } + + /** + * @return The required length the FFT. + */ + public int getFFTlength() { + return fftLength; + } + + /** + * @return the number of bins every octave. + */ + public int getBinsPerOctave(){ + return binsPerOctave; + } +} diff --git a/app/src/main/java/be/tarsos/dsp/DetermineDurationProcessor.java b/app/src/main/java/be/tarsos/dsp/DetermineDurationProcessor.java new file mode 100644 index 0000000..d4c9fc9 --- /dev/null +++ b/app/src/main/java/be/tarsos/dsp/DetermineDurationProcessor.java @@ -0,0 +1,51 @@ +/* +* _______ _____ _____ _____ +* |__ __| | __ \ / ____| __ \ +* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) | +* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/ +* | | (_| | | \__ \ (_) \__ \ |__| |____) | | +* |_|\__,_|_| |___/\___/|___/_____/|_____/|_| +* +* ------------------------------------------------------------- +* +* TarsosDSP is developed by Joren Six at IPEM, University Ghent +* +* ------------------------------------------------------------- +* +* Info: http://0110.be/tag/TarsosDSP +* Github: https://github.com/JorenSix/TarsosDSP +* Releases: http://0110.be/releases/TarsosDSP/ +* +* TarsosDSP includes modified source code by various authors, +* for credits and info, see README. +* +*/ + +package be.tarsos.dsp; + +public class DetermineDurationProcessor implements AudioProcessor { + + long durationInSamples; + float sampleRate; + AudioEvent lastEvent; + + @Override + public boolean process(AudioEvent audioEvent) { + lastEvent = audioEvent; + return true; + } + + public double getDurationInSeconds(){ + return durationInSamples/sampleRate; + } + + public double getDurationInSamples(){ + return durationInSamples; + } + + @Override + public void processingFinished() { + sampleRate = lastEvent.getSampleRate(); + durationInSamples = lastEvent.getSamplesProcessed() + lastEvent.getFloatBuffer().length; + } +} diff --git a/app/src/main/java/be/tarsos/dsp/EnvelopeFollower.java b/app/src/main/java/be/tarsos/dsp/EnvelopeFollower.java new file mode 100644 index 0000000..53e5267 --- /dev/null +++ b/app/src/main/java/be/tarsos/dsp/EnvelopeFollower.java @@ -0,0 +1,130 @@ +/* +* _______ _____ _____ _____ +* |__ __| | __ \ / ____| __ \ +* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) | +* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/ +* | | (_| | | \__ \ (_) \__ \ |__| |____) | | +* |_|\__,_|_| |___/\___/|___/_____/|_____/|_| +* +* ------------------------------------------------------------- +* +* TarsosDSP is developed by Joren Six at IPEM, University Ghent +* +* ------------------------------------------------------------- +* +* Info: http://0110.be/tag/TarsosDSP +* Github: https://github.com/JorenSix/TarsosDSP +* Releases: http://0110.be/releases/TarsosDSP/ +* +* TarsosDSP includes modified source code by various authors, +* for credits and info, see README. +* +*/ + + +package be.tarsos.dsp; + +/** + * An envelope follower follows the envelope of a signal. Sometimes the name + * envelope detector is used. From wikipedia: + *An envelope detector + * is an electronic circuit that takes a high-frequency signal as input and + * provides an output which is the envelope of the original signal. The + * capacitor in the circuit stores up charge on the rising edge, and releases it + * slowly through the resistor when the signal falls. The diode in series + * rectifies the incoming signal, allowing current flow only when the positive + * input terminal is at a higher potential than the negative input terminal. + *+ * + * The resulting envelope is stored in the buffer in the processed AudioEvent. The class can be used thusly: + * + *
+ * EnvelopeFollower follower = new EnvelopeFollower(44100); + * + * AudioDispatcher dispatcher = AudioDispatcher.fromFloatArray(sine, 44100, 1024, 0); + * + * + * dispatcher.addAudioProcessor(follower); + * dispatcher.addAudioProcessor(new AudioProcessor() { + * + * public boolean process(AudioEvent audioEvent) { + * //envelope + * float buffer[] = audioEvent.getFloatBuffer(); + * for(int i = 0 ; i < buffer.length ; i++){ + * System.out.println(buffer[i]); + * } + * return true; + * } + * + * public void processingFinished() { + * } + * }); + * dispatcher.run(); + *+ * + * + * @author Joren Six + * + */ +public class EnvelopeFollower implements AudioProcessor { + + /** + * Defines how fast the envelope raises, defined in seconds. + */ + private static final double DEFAULT_ATTACK_TIME = 0.0002;//in seconds + /** + * Defines how fast the envelope goes down, defined in seconds. + */ + private static final double DEFAULT_RELEASE_TIME = 0.0004;//in seconds + + float gainAttack ; + float gainRelease; + float envelopeOut = 0.0f; + + /** + * Create a new envelope follower, with a certain sample rate. + * @param sampleRate The sample rate of the audio signal. + */ + public EnvelopeFollower(double sampleRate){ + this(sampleRate,DEFAULT_ATTACK_TIME,DEFAULT_RELEASE_TIME); + } + + /** + * Create a new envelope follower, with a certain sample rate. + * @param sampleRate The sample rate of the audio signal. + * @param attackTime Defines how fast the envelope raises, defined in seconds. + * @param releaseTime Defines how fast the envelope goes down, defined in seconds. + */ + public EnvelopeFollower(double sampleRate, double attackTime,double releaseTime){ + gainAttack = (float) Math.exp(-1.0/(sampleRate*attackTime)); + gainRelease = (float) Math.exp(-1.0/(sampleRate*releaseTime)); + } + + @Override + public boolean process(AudioEvent audioEvent) { + float[] buffer = audioEvent.getFloatBuffer(); + calculateEnvelope(buffer); + return true; + } + + /** + * Determine the envelope of an audio buffer + * @param buffer The audio buffer. + */ + public void calculateEnvelope(float[] buffer){ + for(int i = 0 ; i < buffer.length ; i++){ + float envelopeIn = Math.abs(buffer[i]); + if(envelopeOut < envelopeIn){ + envelopeOut = envelopeIn + gainAttack * (envelopeOut - envelopeIn); + } else { + envelopeOut = envelopeIn + gainRelease * (envelopeOut - envelopeIn); + } + buffer[i] = envelopeOut; + } + } + + @Override + public void processingFinished() { + + } +} diff --git a/app/src/main/java/be/tarsos/dsp/FadeIn.java b/app/src/main/java/be/tarsos/dsp/FadeIn.java new file mode 100644 index 0000000..1088beb --- /dev/null +++ b/app/src/main/java/be/tarsos/dsp/FadeIn.java @@ -0,0 +1,54 @@ +package be.tarsos.dsp; + +public class FadeIn implements AudioProcessor { + + private final double duration; + private double firstTime=-1; + private double time; + private final GainProcessor gp=new GainProcessor(0.1); + private boolean fadingIn=true; + + /** + * A new fade in processor + * @param d duration of the fade in seconds + */ + public FadeIn(double d) // + { + this.duration=d; + } + + /** + * Stop fade in processing immediately + */ + public void stopFadeIn() + { + this.fadingIn=false; + } + + @Override + public boolean process(AudioEvent audioEvent) + { + // Don't do anything after the end of the Fade In + if(fadingIn) + { + if(firstTime==-1) + firstTime=audioEvent.getTimeStamp(); + + + // Increase the gain according to time since the beginning of the Fade In + time=audioEvent.getTimeStamp()-firstTime; + gp.setGain(time/duration); + gp.process(audioEvent); + if(time > duration){ + fadingIn = false; + } + } + return true; + } + + @Override + public void processingFinished() + { + gp.processingFinished(); + } +} diff --git a/app/src/main/java/be/tarsos/dsp/FadeOut.java b/app/src/main/java/be/tarsos/dsp/FadeOut.java new file mode 100644 index 0000000..954f977 --- /dev/null +++ b/app/src/main/java/be/tarsos/dsp/FadeOut.java @@ -0,0 +1,50 @@ +package be.tarsos.dsp; + +public class FadeOut implements AudioProcessor { + + private final double duration; + private double firstTime=-1; + private double time; + private boolean isFadeOut=false; + private final GainProcessor gp=new GainProcessor(0.9); + + /** + * A new fade out processor + * @param d duration of the fade out in seconds + */ + public FadeOut(double d) // d= + { + this.duration=d; + } + + /** + * Start fade out processing now + */ + public void startFadeOut() + { + this.isFadeOut=true; + } + + @Override + public boolean process(AudioEvent audioEvent) + { + // Don't do anything before the beginning of Fade Out + if(isFadeOut) + { + if(firstTime==-1) + firstTime=audioEvent.getTimeStamp(); + + // Decrease the gain according to time since the beginning of the Fade Out + time=audioEvent.getTimeStamp()-firstTime; + gp.setGain(1-time/duration); + gp.process(audioEvent); + } + return true; + } + + @Override + public void processingFinished() + { + gp.processingFinished(); + } +} \ No newline at end of file diff --git a/app/src/main/java/be/tarsos/dsp/GainProcessor.java b/app/src/main/java/be/tarsos/dsp/GainProcessor.java new file mode 100644 index 0000000..0e62418 --- /dev/null +++ b/app/src/main/java/be/tarsos/dsp/GainProcessor.java @@ -0,0 +1,74 @@ +/* +* _______ _____ _____ _____ +* |__ __| | __ \ / ____| __ \ +* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) | +* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/ +* | | (_| | | \__ \ (_) \__ \ |__| |____) | | +* |_|\__,_|_| |___/\___/|___/_____/|_____/|_| +* +* ------------------------------------------------------------- +* +* TarsosDSP is developed by Joren Six at IPEM, University Ghent +* +* ------------------------------------------------------------- +* +* Info: http://0110.be/tag/TarsosDSP +* Github: https://github.com/JorenSix/TarsosDSP +* Releases: http://0110.be/releases/TarsosDSP/ +* +* TarsosDSP includes modified source code by various authors, +* for credits and info, see README. +* +*/ + + +package be.tarsos.dsp; + +/** + * With the gain processor it is possible to adapt the volume of the sound. With + * a gain of 1, nothing happens. A gain greater than one is a volume increase a + * gain between zero and one, exclusive, is a decrease. If you need to flip the + * sign of the audio samples, you can by providing a gain of -1.0. but I have no + * idea what you could gain by doing that (pathetic pun, I know). + * + * @author Joren Six + */ +public class GainProcessor implements AudioProcessor { + private double gain; + + /** + * Create a new gain processor + * @param newGain the gain + */ + public GainProcessor(double newGain) { + setGain(newGain); + } + + /** + * Set the gain applied to the next buffer. + * @param newGain The new gain. + */ + public void setGain(double newGain) { + this.gain = newGain; + } + + @Override + public boolean process(AudioEvent audioEvent) { + float[] audioFloatBuffer = audioEvent.getFloatBuffer(); + for (int i = audioEvent.getOverlap(); i < audioFloatBuffer.length ; i++) { + float newValue = (float) (audioFloatBuffer[i] * gain); + if(newValue > 1.0f) { + newValue = 1.0f; + } else if(newValue < -1.0f) { + newValue = -1.0f; + } + audioFloatBuffer[i] = newValue; + } + return true; + } + + @Override + public void processingFinished() { + // NOOP + } +} diff --git a/app/src/main/java/be/tarsos/dsp/MultichannelToMono.java b/app/src/main/java/be/tarsos/dsp/MultichannelToMono.java new file mode 100644 index 0000000..b24a2dc --- /dev/null +++ b/app/src/main/java/be/tarsos/dsp/MultichannelToMono.java @@ -0,0 +1,71 @@ +/* +* _______ _____ _____ _____ +* |__ __| | __ \ / ____| __ \ +* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) | +* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/ +* | | (_| | | \__ \ (_) \__ \ |__| |____) | | +* |_|\__,_|_| |___/\___/|___/_____/|_____/|_| +* +* ------------------------------------------------------------- +* +* TarsosDSP is developed by Joren Six at IPEM, University Ghent +* +* ------------------------------------------------------------- +* +* Info: http://0110.be/tag/TarsosDSP +* Github: https://github.com/JorenSix/TarsosDSP +* Releases: http://0110.be/releases/TarsosDSP/ +* +* TarsosDSP includes modified source code by various authors, +* for credits and info, see README. +* +*/ + +package be.tarsos.dsp; + +/** + * Process multi channels audio to mono + */ +public class MultichannelToMono implements AudioProcessor{ + + private final int channels; + private final boolean mean; + + public MultichannelToMono(int numberOfChannels,boolean meanOfchannels){ + channels = numberOfChannels; + mean = meanOfchannels; + } + + @Override + public boolean process(AudioEvent audioEvent) { + float[] buffer = audioEvent.getFloatBuffer(); + float[] newBuffer = new float[buffer.length/channels]; + + if(mean){ + if(channels==2){ + for(int i = 0 ; i < buffer.length ; i = i + channels){ + newBuffer[i/channels]=(buffer[i]+buffer[i+1])/2.0f; + } + }else{ + for(int i = 0 ; i < buffer.length ; i = i + channels){ + double sum = 0; + for(int j = 0; j < channels;j++){ + sum = sum + buffer[i+j]; + } + newBuffer[i/channels]=(float) (sum/channels); + } + } + }else{ + for(int i = 0 ; i < buffer.length ; i = i + channels){ + newBuffer[i/channels]=buffer[i]; + } + } + + audioEvent.setFloatBuffer(newBuffer); + return true; + } + + @Override + public void processingFinished() { + } +} diff --git a/app/src/main/java/be/tarsos/dsp/Oscilloscope.java b/app/src/main/java/be/tarsos/dsp/Oscilloscope.java new file mode 100644 index 0000000..975d9fc --- /dev/null +++ b/app/src/main/java/be/tarsos/dsp/Oscilloscope.java @@ -0,0 +1,96 @@ +/* +* _______ _____ _____ _____ +* |__ __| | __ \ / ____| __ \ +* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) | +* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/ +* | | (_| | | \__ \ (_) \__ \ |__| |____) | | +* |_|\__,_|_| |___/\___/|___/_____/|_____/|_| +* +* ------------------------------------------------------------- +* +* TarsosDSP is developed by Joren Six at IPEM, University Ghent +* +* ------------------------------------------------------------- +* +* Info: http://0110.be/tag/TarsosDSP +* Github: https://github.com/JorenSix/TarsosDSP +* Releases: http://0110.be/releases/TarsosDSP/ +* +* TarsosDSP includes modified source code by various authors, +* for credits and info, see README. +* +*/ + + +package be.tarsos.dsp; + +/** + * The oscilloscope generates a float array with + * array[i] an x coordinate in percentage + * array[i+1] the value of the amplitude in audio buffer + * array[i+2] another x coordinate in percentage + * array[i+3] the next amplitude in the audio buffer + * + * The implementation is based on the one by Dan Ellis found at http://www.ee.columbia.edu/~dpwe/resources/Processing/ + * @author Dan Ellis + * @author Joren Six + * + */ +public class Oscilloscope implements AudioProcessor { + public interface OscilloscopeEventHandler{ + /** + * @param data The data contains a float array with: + * array[i] an x coordinate in percentage + * array[i+1] the value of the amplitude in audio buffer + * array[i+2] another x coordinate in percentage + * array[i+3] the next amplitude in the audio buffer + * @param event An audio Event. + */ + void handleEvent(float[] data, AudioEvent event); + } + float[] dataBuffer; + private final OscilloscopeEventHandler handler; + public Oscilloscope(OscilloscopeEventHandler handler){ + this.handler = handler; + } + + @Override + public boolean process(AudioEvent audioEvent) { + float[] audioBuffer = audioEvent.getFloatBuffer(); + int offset = 0; + float maxdx = 0; + for (int i = 0; i < audioBuffer.length / 4; ++i) { + float dx = audioBuffer[i + 1] - audioBuffer[i]; + if (dx > maxdx) { + offset = i; + maxdx = dx; + } + } + + float tbase = audioBuffer.length / 2; + + + int length = Math.min((int) tbase, audioBuffer.length-offset); + if(dataBuffer == null || dataBuffer.length != length * 4){ + dataBuffer = new float[length * 4]; + } + + int j = 0; + for(int i = 0; i < length - 1; i++){ + float x1 = i / tbase; + float x2 = i / tbase; + dataBuffer[j] = x1; + dataBuffer[j+1] = audioBuffer[i+offset]; + dataBuffer[j+2] = x2; + dataBuffer[j+3] = audioBuffer[i+1+offset]; + j = j + 4; + } + handler.handleEvent(dataBuffer, audioEvent); + return true; + } + + @Override + public void processingFinished() { + } + +} diff --git a/app/src/main/java/be/tarsos/dsp/PitchShifter.java b/app/src/main/java/be/tarsos/dsp/PitchShifter.java new file mode 100644 index 0000000..d415f25 --- /dev/null +++ b/app/src/main/java/be/tarsos/dsp/PitchShifter.java @@ -0,0 +1,182 @@ +package be.tarsos.dsp; + + +import be.tarsos.dsp.util.fft.FFT; + +/** + * This is a translation of code by Stephan M. Bernsee. See the following explanation on this code: + * Pitch shifting using the STFT. + * + * @author Joren Six + * @author Stephan M. Bernsee + */ +public class PitchShifter implements AudioProcessor{ + + private final FFT fft; + private final int size; + private final float[] currentMagnitudes; + private final float[] currentPhase; + private final float[] currentFrequencies; + private final float[] outputAccumulator; + private final float[] summedPhase; + + private final float[] previousPhase; + + private double pitchShiftRatio = 0; + + private final double sampleRate; + + private final long osamp; + + private final double excpt; + + public PitchShifter(double factor, double sampleRate, int size, int overlap){ + + + pitchShiftRatio = factor; + this.size = size; + this.sampleRate = sampleRate; + //this.d = d; + + osamp=size/(size-overlap); + + this.excpt = 2.*Math.PI*(double)(size-overlap)/(double)size; + + fft = new FFT(size); + + currentMagnitudes = new float[size/2]; + currentFrequencies = new float[size/2]; + currentPhase = new float[size/2]; + + previousPhase = new float[size/2]; + summedPhase = new float[size/2]; + outputAccumulator = new float[size*2]; + } + + public void setPitchShiftFactor(float newPitchShiftFactor){ + this.pitchShiftRatio = newPitchShiftFactor; + } + + @Override + public boolean process(AudioEvent audioEvent) { + //see http://downloads.dspdimension.com/smbPitchShift.cpp + + /* ***************** ANALYSIS ******************* */ + float[] fftData = audioEvent.getFloatBuffer().clone(); + + for(int i = 0 ; i
+ * This class implements a spectral peak follower as described in Sethares et + * al. 2009 - Spectral Tools for Dynamic Tonality and Audio Morphing - section + * "Analysis-Resynthessis". It calculates a noise floor and picks spectral peaks + * rising above a calculated noise floor with a certain factor. The noise floor + * is determined using a simple median filter. + *
+ *+ * Parts of the code is modified from the code accompanying + * "Spectral Tools for Dynamic Tonality and Audio Morphing". + *
+ *
+ * To get the spectral peaks from an audio frame, call
+ * An overlap-add technique based on waveform similarity (WSOLA) for high
+ * quality time-scale modification of speech
+ *
+ * A concept of waveform similarity for tackling the problem of time-scale
+ * modification of speech is proposed. It is worked out in the context of
+ * short-time Fourier transform representations. The resulting WSOLA
+ * (waveform-similarity-based synchronized overlap-add) algorithm produces
+ * high-quality speech output, is algorithmically and computationally efficient
+ * and robust, and allows for online processing with arbitrary time-scaling
+ * factors that may be specified in a time-varying fashion and can be chosen
+ * over a wide continuous range of values.
+ *
+ * Inspired by the work soundtouch by Olli Parviainen,
+ * http://www.surina.net/soundtouch, especially the TDStrech.cpp file.
+ *
+ * Adds an echo effect to the signal.
+ *
+ * Adds a flanger effect to a signal. The implementation is done with a delay
+ * buffer and an LFO in the form of a sine wave. It is probably the most
+ * straightforward flanger implementation possible.
+ *
+ * Decode audio files to PCM, mono, 16bits per sample, at any sample rate using
+ * an external program. By default ffmpeg is used. Other
+ * command Line programs that are able to decode audio and pipe binary PCM
+ * samples to STDOUT are possible as well (avconv, mplayer).
+ * To install ffmpeg on Debian:
+ * This adds support for a lot of audio formats and video container formats with
+ * relatively little effort. Depending on the program used also http streams,
+ * rtpm streams, ... are supported as well.
+ *
+ * To see which audio decoders are supported, check
+ *
+ * Every data LineWavelet has an audio format associated with its data stream. The audio format of a source (playback) data LineWavelet indicates
+ * what kind of data the data LineWavelet expects to receive for output. For a target (capture) data LineWavelet, the audio format specifies the kind
+ * of the data that can be read from the LineWavelet.
+ * Sound files also have audio formats, of course.
+ *
+ * The
+ * In addition to the encoding, the audio format includes other properties that further specify the exact
+ * arrangement of the data.
+ * These include the number of channels, sample rate, sample size, byte order, frame rate, and frame size.
+ * Sounds may have different numbers of audio channels: one for mono, two for stereo.
+ * The sample rate measures how many "snapshots" (samples) of the sound pressure are taken per second, per channel.
+ * (If the sound is stereo rather than mono, two samples are actually measured at each instant of time: one for the left channel,
+ * and another for the right channel; however, the sample rate still measures the number per channel, so the rate is the same
+ * regardless of the number of channels. This is the standard use of the term.)
+ * The sample size indicates how many bits are used to store each snapshot; 8 and 16 are typical values.
+ * For 16-bit samples (or any other sample size larger than a byte),
+ * byte order is important; the bytes in each sample are arranged in
+ * either the "little-endian" or "big-endian" style.
+ * For encodings like PCM, a frame consists of the set of samples for all channels at a given
+ * point in time, and so the size of a frame (in bytes) is always equal to the size of a sample (in bytes) times
+ * the number of channels. However, with some other sorts of encodings a frame can contain
+ * a bundle of compressed data for a whole series of samples, as well as additional, non-sample
+ * data. For such encodings, the sample rate and sample size refer to the data after it is decoded into PCM,
+ * and so they are completely different from the frame rate and frame size.
+ *
+ * An The following table lists some common properties which
+ * service providers should use, if applicable:
+ *
+ *
+ * Vendors of service providers (plugins) are encouraged
+ * to seek information about other already established
+ * properties in third party plugins, and follow the same
+ * conventions.
+ *
+ * @author Kara Kytle
+ * @author Florian Bomers
+ * @since 1.3
+ */
+public class TarsosDSPAudioFormat {
+
+ // INSTANCE VARIABLES
+
+
+ /**
+ * The audio encoding technique used by this format.
+ */
+ protected Encoding encoding;
+
+ /**
+ * The number of samples played or recorded per second, for sounds that have this format.
+ */
+ protected float sampleRate;
+
+ /**
+ * The number of bits in each sample of a sound that has this format.
+ */
+ protected int sampleSizeInBits;
+
+ /**
+ * The number of audio channels in this format (1 for mono, 2 for stereo).
+ */
+ protected int channels;
+
+ /**
+ * The number of bytes in each frame of a sound that has this format.
+ */
+ protected int frameSize;
+
+ /**
+ * The number of frames played or recorded per second, for sounds that have this format.
+ */
+ protected float frameRate;
+
+ /**
+ * Indicates whether the audio data is stored in big-endian or little-endian order.
+ */
+ protected boolean bigEndian;
+
+
+ /** The set of properties */
+ private HashMap If the specified property is not defined for a
+ * particular file format, this method returns
+ *
+ * One ubiquitous type of audio encoding is pulse-code modulation (PCM),
+ * which is simply a linear (proportional) representation of the sound
+ * waveform. With PCM, the number stored in each sample is proportional
+ * to the instantaneous amplitude of the sound pressure at that point in
+ * time. The numbers are frequently signed or unsigned integers.
+ * Besides PCM, other encodings include mu-law and a-law, which are nonlinear
+ * mappings of the sound amplitude that are often used for recording speech.
+ *
+ * You can use a predefined encoding by referring to one of the static
+ * objects created by this class, such as PCM_SIGNED or
+ * PCM_UNSIGNED. Service providers can create new encodings, such as
+ * compressed audio formats or floating-point PCM samples, and make
+ * these available through the
+ * The This method will always read an integral number of frames.
+ * If
+ * A non real-time spectral flux onset detection method, as implemented in the
+ * BeatRoot system of Centre for Digital Music, Queen Mary, University of
+ * London.
+ *
+ * This onset detection function does not, NOT work in real-time. It analyzes an
+ * audio-stream and detects onsets during a post processing step.
+ *
+ * Estimates the locations of percussive onsets using a simple method described
+ * in "Drum Source Separation using Percussive Feature Detection and Spectral Modulation"
+ * by Dan Barry, Derry Fitzgerald, Eugene Coyle and Bob Lawlor,
+ * ISSC 2005.
+ *
+ * Implementation based on a VAMP plugin by Chris Cannam at Queen Mary, London:
+ *
+ * A pitch extractor that extracts the Average Magnitude Difference (AMDF) from
+ * an audio buffer. This is a good measure of the Pitch (f0) of a signal.
+ *
+ * AMDF is calculated by the the difference between the waveform summing a
+ * lagged version of itself.
+ *
+ * The main bulk of the code is written by Eder de Souza for the jAudio framework. Adapted for TarsosDSP by
+ * Joren Six.
+ *
+ * The pitch is the main frequency of the waveform (the 'note' being played or
+ * sung). It is expressed as a float in Hz.
+ *
+ * Unlike the human ear, pitch detection is difficult to achieve for computers.
+ * Many algorithms have been designed and experimented, but there is no 'best'
+ * algorithm. They all depend on the context and the tradeoffs acceptable in
+ * terms of speed and latency. The context includes the quality and 'cleanness'
+ * of the audio : obviously polyphonic sounds (multiple instruments playing
+ * different notes at the same time) are extremely difficult to track,
+ * percussive or noisy audio has no pitch, most real-life audio have some noisy
+ * moments, some instruments have a lot of harmonics, etc...
+ *
+ * The dywapitchtrack is based on a custom-tailored algorithm which is of very
+ * high quality: both very accurate (precision < 0.05 semitones), very low
+ * latency (< 23 ms) and very low error rate. It has been thoroughly tested on
+ * human voice.
+ *
+ * It can best be described as a dynamic wavelet algorithm (dywa):
+ *
+ * The heart of the algorithm is a very powerful wavelet algorithm, described in
+ * a paper by Eric Larson and Ross Maddox: Real-Time Time-Domain Pitch Tracking Using Wavelets.
+ *
+ * Implementation of The McLeod Pitch Method (MPM). It is described in the
+ * article A Smarter Way to Find Pitch. According to the article:
+ *
+ * A fast, accurate and robust method for finding the continuous pitch in
+ * monophonic musical sounds. [It uses] a special normalized version of the
+ * Squared Difference Function (SDF) coupled with a peak picking algorithm.
+ *
+ * MPM runs in real time with a standard 44.1 kHz sampling rate. It operates
+ * without using low-pass filtering so it can work on sound with high harmonic
+ * frequencies such as a violin and it can display pitch changes of one cent
+ * reliably. MPM works well without any post processing to correct the pitch.
+ *
+ * For the moment this implementation uses the inefficient way of calculating
+ * the pitch. It uses
+ * Finds the x value corresponding with the peak of a parabola.
+ *
+ * a,b,c are three samples that follow each other. E.g. a is at 511, b at
+ * 512 and c at 513; f(a), f(b) and f(c) are the normalized square
+ * difference values for those samples; x is the peak of the parabola and is
+ * what we are looking for. Because the samples follow each other
+ *
+ * The following ASCII ART shows it a bit more clear, imagine this to be a
+ * bit more curvaceous.
+ *
+ * Implementation based on the GPL'ED code of Tartini This code can be found in the file
+ *
+ * Finds the highest value between each pair of positive zero crossings.
+ * Including the highest value between the last positive zero crossing and
+ * the end (if any). Ignoring the first maximum (which is at zero). In this
+ * diagram the desired values are marked with a +
+ *
+ * For performance reasons the object is reused. Please create a copy of the object
+ * if you want to use it on an other thread.
+ *
+ *
+ * @author Joren Six
+ */
+public class PitchDetectionResult {
+ /**
+ * The pitch in Hertz.
+ */
+ private float pitch;
+
+ private float probability;
+
+ private boolean pitched;
+
+ public PitchDetectionResult(){
+ pitch = -1;
+ probability = -1;
+ pitched = false;
+ }
+
+ /**
+ * A copy constructor. Since PitchDetectionResult objects are reused for performance reasons, creating a copy can be practical.
+ * @param other
+ */
+ public PitchDetectionResult(PitchDetectionResult other){
+ this.pitch = other.pitch;
+ this.probability = other.probability;
+ this.pitched = other.pitched;
+ }
+
+
+ /**
+ * @return The pitch in Hertz.
+ */
+ public float getPitch() {
+ return pitch;
+ }
+
+ public void setPitch(float pitch) {
+ this.pitch = pitch;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Object#clone()
+ */
+ public PitchDetectionResult clone(){
+ return new PitchDetectionResult(this);
+ }
+
+ /**
+ * @return A probability (noisiness, (a)periodicity, salience, voicedness or
+ * clarity measure) for the detected pitch. This is somewhat similar
+ * to the term voiced which is used in speech recognition. This
+ * probability should be calculated together with the pitch. The
+ * exact meaning of the value depends on the detector used.
+ */
+ public float getProbability() {
+ return probability;
+ }
+
+ public void setProbability(float probability) {
+ this.probability = probability;
+ }
+
+ /**
+ * @return Whether the algorithm thinks the block of audio is pitched. Keep
+ * in mind that an algorithm can come up with a best guess for a
+ * pitch even when isPitched() is false.
+ */
+ public boolean isPitched() {
+ return pitched;
+ }
+
+ public void setPitched(boolean pitched) {
+ this.pitched = pitched;
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/pitch/PitchDetector.java b/app/src/main/java/be/tarsos/dsp/pitch/PitchDetector.java
new file mode 100644
index 0000000..9d6975b
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/pitch/PitchDetector.java
@@ -0,0 +1,46 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+package be.tarsos.dsp.pitch;
+
+/**
+ * A pitch detector is capable of analyzing a buffer with audio information
+ * and return a pitch estimation in Hz.
+ *
+ * @author Joren Six
+ */
+public interface PitchDetector {
+ /**
+ * Analyzes a buffer with audio information and estimates a pitch in Hz.
+ * Currently this interface only allows one pitch per buffer.
+ *
+ * @param audioBuffer
+ * The buffer with audio information. The information in the
+ * buffer is not modified so it can be (re)used for e.g. FFT
+ * analysis.
+ * @return An estimation of the pitch in Hz or -1 if no pitch is detected or
+ * present in the buffer.
+ */
+ PitchDetectionResult getPitch(final float[] audioBuffer);
+}
diff --git a/app/src/main/java/be/tarsos/dsp/pitch/PitchProcessor.java b/app/src/main/java/be/tarsos/dsp/pitch/PitchProcessor.java
new file mode 100644
index 0000000..f3a6031
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/pitch/PitchProcessor.java
@@ -0,0 +1,144 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+package be.tarsos.dsp.pitch;
+
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.AudioProcessor;
+
+/**
+ * Is responsible to call a pitch estimation algorithm. It also calculates progress.
+ * The underlying pitch detection algorithm must implement the {@link PitchDetector} interface.
+ * @author Joren Six
+ */
+public class PitchProcessor implements AudioProcessor {
+
+ /**
+ * A list of pitch estimation algorithms.
+ * @author Joren Six
+ */
+ public enum PitchEstimationAlgorithm {
+ /**
+ * See {@link Yin} for the implementation. Or see the YIN article.
+ */
+ YIN,
+ /**
+ * See {@link McLeodPitchMethod}. It is described in the article "A Smarter Way to Find Pitch".
+ */
+ MPM,
+ /**
+ * A YIN implementation with a faster {@link FastYin} for the implementation. Or see the YIN article.
+ */
+ FFT_YIN,
+ /**
+ * An implementation of a dynamic wavelet pitch detection algorithm (See
+ * {@link DynamicWavelet}), described in a paper by Eric Larson and Ross
+ * Maddox 'Real-Time Time-Domain Pitch Tracking Using Wavelets'
+ */
+ DYNAMIC_WAVELET,
+ /**
+ * Returns the frequency of the FFT-bin with most energy.
+ */
+ FFT_PITCH,
+ /**
+ * A pitch extractor that extracts the Average Magnitude Difference
+ * (AMDF) from an audio buffer. This is a good measure of the Pitch (f0)
+ * of a signal.
+ */
+ AMDF;
+
+ /**
+ * Returns a new instance of a pitch detector object based on the provided values.
+ * @param sampleRate The sample rate of the audio buffer.
+ * @param bufferSize The size (in samples) of the audio buffer.
+ * @return A new pitch detector object.
+ */
+ public PitchDetector getDetector(float sampleRate,int bufferSize){
+ PitchDetector detector;
+ if (this == MPM ) {
+ detector = new McLeodPitchMethod(sampleRate, bufferSize);
+ } else if(this == DYNAMIC_WAVELET ) {
+ detector = new DynamicWavelet(sampleRate,bufferSize);
+ } else if(this == FFT_YIN){
+ detector = new FastYin(sampleRate, bufferSize);
+ } else if(this==AMDF){
+ detector = new AMDF(sampleRate, bufferSize);
+ } else {
+ detector = new Yin(sampleRate, bufferSize);
+ }
+ return detector;
+ }
+
+ }
+
+ /**
+ * The underlying pitch detector;
+ */
+ private final PitchDetector detector;
+
+ private final PitchDetectionHandler handler;
+
+ /**
+ * Initialize a new pitch processor.
+ *
+ * @param algorithm
+ * An enum defining the algorithm.
+ * @param sampleRate
+ * The sample rate of the buffer (Hz).
+ * @param bufferSize
+ * The size of the buffer in samples.
+ * @param handler
+ * The handler handles detected pitch.
+ */
+ public PitchProcessor(PitchEstimationAlgorithm algorithm, float sampleRate,
+ int bufferSize,
+ PitchDetectionHandler handler) {
+ detector = algorithm.getDetector(sampleRate, bufferSize);
+ this.handler = handler;
+ }
+
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+ float[] audioFloatBuffer = audioEvent.getFloatBuffer();
+
+ PitchDetectionResult result = detector.getPitch(audioFloatBuffer);
+
+
+ handler.handlePitch(result,audioEvent);
+ return true;
+ }
+
+ @Override
+ public void processingFinished() {
+ }
+
+
+}
diff --git a/app/src/main/java/be/tarsos/dsp/pitch/Yin.java b/app/src/main/java/be/tarsos/dsp/pitch/Yin.java
new file mode 100644
index 0000000..2bdadfd
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/pitch/Yin.java
@@ -0,0 +1,273 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+package be.tarsos.dsp.pitch;
+
+/**
+ * An implementation of the AUBIO_YIN pitch tracking algorithm. See the YIN paper. Implementation based on aubio
+ *
+ * @author Joren Six
+ * @author Paul Brossier
+ */
+public final class Yin implements PitchDetector {
+ /**
+ * The default YIN threshold value. Should be around 0.10~0.15. See YIN
+ * paper for more information.
+ */
+ private static final double DEFAULT_THRESHOLD = 0.20;
+
+ /**
+ * The default size of an audio buffer (in samples).
+ */
+ public static final int DEFAULT_BUFFER_SIZE = 2048;
+
+ /**
+ * The default overlap of two consecutive audio buffers (in samples).
+ */
+ public static final int DEFAULT_OVERLAP = 1536;
+
+ /**
+ * The actual YIN threshold.
+ */
+ private final double threshold;
+
+ /**
+ * The audio sample rate. Most audio has a sample rate of 44.1kHz.
+ */
+ private final float sampleRate;
+
+ /**
+ * The buffer that stores the calculated values. It is exactly half the size
+ * of the input buffer.
+ */
+ private final float[] yinBuffer;
+
+ /**
+ * The result of the pitch detection iteration.
+ */
+ private final PitchDetectionResult result;
+
+ /**
+ * Create a new pitch detector for a stream with the defined sample rate.
+ * Processes the audio in blocks of the defined size.
+ *
+ * @param audioSampleRate
+ * The sample rate of the audio stream. E.g. 44.1 kHz.
+ * @param bufferSize
+ * The size of a buffer. E.g. 1024.
+ */
+ public Yin(final float audioSampleRate, final int bufferSize) {
+ this(audioSampleRate, bufferSize, DEFAULT_THRESHOLD);
+ }
+
+ /**
+ * Create a new pitch detector for a stream with the defined sample rate.
+ * Processes the audio in blocks of the defined size.
+ *
+ * @param audioSampleRate
+ * The sample rate of the audio stream. E.g. 44.1 kHz.
+ * @param bufferSize
+ * The size of a buffer. E.g. 1024.
+ * @param yinThreshold
+ * The parameter that defines which peaks are kept as possible
+ * pitch candidates. See the YIN paper for more details.
+ */
+ public Yin(final float audioSampleRate, final int bufferSize, final double yinThreshold) {
+ this.sampleRate = audioSampleRate;
+ this.threshold = yinThreshold;
+ yinBuffer = new float[bufferSize / 2];
+ result = new PitchDetectionResult();
+ }
+
+ /**
+ * The main flow of the YIN algorithm. Returns a pitch value in Hz or -1 if
+ * no pitch is detected.
+ *
+ * @return a pitch value in Hz or -1 if no pitch is detected.
+ */
+ public PitchDetectionResult getPitch(final float[] audioBuffer) {
+
+ final int tauEstimate;
+ final float pitchInHertz;
+
+ // step 2
+ difference(audioBuffer);
+
+ // step 3
+ cumulativeMeanNormalizedDifference();
+
+ // step 4
+ tauEstimate = absoluteThreshold();
+
+ // step 5
+ if (tauEstimate != -1) {
+ final float betterTau = parabolicInterpolation(tauEstimate);
+
+ // step 6
+ // TODO Implement optimization for the AUBIO_YIN algorithm.
+ // 0.77% => 0.5% error rate,
+ // using the data of the YIN paper
+ // bestLocalEstimate()
+
+ // conversion to Hz
+ pitchInHertz = sampleRate / betterTau;
+ } else{
+ // no pitch found
+ pitchInHertz = -1;
+ }
+
+ result.setPitch(pitchInHertz);
+
+ return result;
+ }
+
+ /**
+ * Implements the difference function as described in step 2 of the YIN
+ * paper.
+ */
+ private void difference(final float[] audioBuffer) {
+ int index, tau;
+ float delta;
+ for (tau = 0; tau < yinBuffer.length; tau++) {
+ yinBuffer[tau] = 0;
+ }
+ for (tau = 1; tau < yinBuffer.length; tau++) {
+ for (index = 0; index < yinBuffer.length; index++) {
+ delta = audioBuffer[index] - audioBuffer[index + tau];
+ yinBuffer[tau] += delta * delta;
+ }
+ }
+ }
+
+ /**
+ * The cumulative mean normalized difference function as described in step 3
+ * of the YIN paper.
+ * Windowing
+ *
+ * Windowing is the process of shaping the audio samples before transforming
+ * them to the frequency domain. The Fourier Transform assumes the sample buffer
+ * is is a repetitive signal, if a sample buffer is not truly periodic within
+ * the measured interval sharp discontinuities may arise that can introduce
+ * spectral leakage. Spectral leakage is the speading of signal energy across
+ * multiple FFT bins. This "spreading" can drown out narrow band signals and
+ * hinder detection.
+ *
+ * A windowing
+ * function attempts to reduce spectral leakage by attenuating the measured
+ * sample buffer at its end points to eliminate discontinuities. If you call the
+ *
+ *
+ * Default two step Lifting Scheme inverse wavelet transform
+ *
+ * inverseTrans is passed the result of an ordered wavelet transform,
+ * consisting of an average and a set of wavelet coefficients. The inverse
+ * transform is calculated in-place and the result is returned in the
+ * argument array.
+ *
+ * HaarWavelet (flat LineWavelet) wavelet.
+ *
+ * As with all Lifting scheme wavelet transform functions, the first stage of a
+ * transform step is the split stage. The split step moves the even element to
+ * the first half of an N element region and the odd elements to the second half
+ * of the N element region.
+ *
+ * The Lifting Scheme version of the HaarWavelet transform uses a wavelet
+ * function (predict stage) that "predicts" that an odd element will have the
+ * same value as it preceeding even element. Stated another way, the odd element
+ * is "predicted" to be on a flat (zero slope LineWavelet) shared with the even
+ * point. The difference between this "prediction" and the actual odd value
+ * replaces the odd element.
+ *
+ * The wavelet scaling function (a.k.a. smoothing function) used in the update
+ * stage calculates the average between an even and an odd element.
+ *
+ * The merge stage at the end of the inverse transform interleaves odd and even
+ * elements from the two halves of the array (e.g., ordering them
+ * even0, odd0, even1, odd1, ...)
+ *
+ * You may use this source code without limitation and without fee as long as
+ * you include:
+ *
+ * This software is provided "as is", without any warrenty or claim as to its
+ * usefulness. Anyone who uses this source code uses it at their own risk. Nor
+ * is any support provided by Ian Kaplan and Bear Products International.
+ *
+ * Please send any bug fixes or suggested source changes to:
+ *
+ *
+ * Update step of the HaarWavelet wavelet transform.
+ *
+ * The wavelet transform calculates a set of detail or difference
+ * coefficients in the predict step. These are stored in the upper half of
+ * the array. The update step calculates an average from the even-odd
+ * element pairs. The averages will replace the even elements in the lower
+ * half of the array.
+ *
+ * The HaarWavelet wavelet calculation used in the Lifting Scheme is
+ *
+ * Note that the Lifting Scheme uses an in-place algorithm. The odd elements
+ * have been replaced by the detail coefficients in the predict step. With a
+ * little algebra we can substitute the coefficient calculation into the
+ * average calculation, which gives us
+ *
+ * HaarWavelet transform extended with a polynomial interpolation step
+ *
+ * This wavelet transform extends the HaarWavelet transform with a polynomial
+ * wavelet function.
+ *
+ * The polynomial wavelet uses 4-point polynomial interpolation to "predict" an
+ * odd point from four even point values.
+ *
+ * This class extends the HaarWavelet transform with an interpolation stage
+ * which follows the predict and update stages of the HaarWavelet transform. The
+ * predict value is calculated from the even points, which in this case are the
+ * smoothed values calculated by the scaling function (e.g., the averages of the
+ * even and odd values).
+ *
+ * The predict value is subtracted from the current odd value, which is the
+ * result of the HaarWavelet wavelet function (e.g., the difference between the
+ * odd value and the even value). This tends to result in large odd values after
+ * the interpolation stage, which is a weakness in this algorithm.
+ *
+ * This algorithm was suggested by Wim Sweldens' tutorial Building Your Own
+ * Wavelets at Home.
+ *
+ * You may use this source code without limitation and without fee as long as
+ * you include:
+ *
+ * This software is provided "as is", without any warrenty or claim as to its
+ * usefulness. Anyone who uses this source code uses it at their own risk. Nor
+ * is any support provided by Ian Kaplan and Bear Products International.
+ *
+ * Please send any bug fixes or suggested source changes to:
+ *
+ *
+ * Copy four points or N (which ever is less) data points from
+ * vec into d These points are the "known" points used in the
+ * polynomial interpolation.
+ *
+ * Predict an odd point from the even points, using 4-point polynomial
+ * interpolation.
+ *
+ * The four points used in the polynomial interpolation are the even points.
+ * We pretend that these four points are located at the x-coordinates
+ * 0,1,2,3. The first odd point interpolated will be located between the
+ * first and second even point, at 0.5. The next N-3 points are located at
+ * 1.5 (in the middle of the four points). The last two points are located
+ * at 2.5 and 3.5. For complete documentation see
+ *
+ * The difference between the predicted (interpolated) value and the actual
+ * odd value replaces the odd value in the forward transform.
+ *
+ * As the recursive steps proceed, N will eventually be 4 and then 2. When N
+ * = 4, linear interpolation is used. When N = 2, HaarWavelet interpolation
+ * is used (the prediction for the odd value is that it is equal to the even
+ * value).
+ *
+ * HaarWavelet transform extened with polynomial interpolation forward
+ * transform.
+ *
+ * This version of the forwardTrans function overrides the function in the
+ * LiftingSchemeBaseWavelet base class. This function introduces an extra
+ * polynomial interpolation stage at the end of the transform.
+ *
+ * HaarWavelet transform extened with polynomial interpolation inverse
+ * transform.
+ *
+ * This version of the inverseTrans function overrides the function in the
+ * LiftingSchemeBaseWavelet base class. This function introduces an inverse
+ * polynomial interpolation stage at the start of the inverse transform.
+ *
+ * class LiftingSchemeBaseWavelet: base class for simple Lifting Scheme wavelets
+ * using split, predict, update or update, predict, merge steps.
+ *
+ * Simple lifting scheme wavelets consist of three steps, a split/merge step,
+ * predict step and an update step:
+ *
+ * The split step divides the elements in an array so that the even elements are
+ * in the first half and the odd elements are in the second half.
+ *
+ * The merge step is the inverse of the split step. It takes two regions of an
+ * array, an odd region and an even region and merges them into a new region
+ * where an even element alternates with an odd element.
+ *
+ * The predict step calculates the difference between an odd element and its
+ * predicted value based on the even elements. The difference between the
+ * predicted value and the actual value replaces the odd element.
+ *
+ * The predict step operates on the odd elements. The update step operates on
+ * the even element, replacing them with a difference between the predict value
+ * and the actual odd element. The update step replaces each even element with
+ * an average. The result of the update step becomes the input to the next
+ * recursive step in the wavelet calculation.
+ *
+ * The split and merge methods are shared by all Lifting Scheme wavelet
+ * algorithms. This base class provides the transform and inverse transform
+ * methods (forwardTrans and inverseTrans). The predict and update methods are
+ * abstract and are defined for a particular Lifting Scheme wavelet sub-class.
+ *
+ * References:
+ *
+ * You may use this source code without limitation and without fee as long as
+ * you include:
+ *
+ * This software is provided "as is", without any warrenty or claim as to its
+ * usefulness. Anyone who uses this source code uses it at their own risk. Nor
+ * is any support provided by Ian Kaplan and Bear Products International.
+ *
+ * Please send any bug fixes or suggested source changes to:
+ *
+ *
+ * Simple wavelet Lifting Scheme forward transform
+ *
+ * forwardTrans is passed an array of doubles. The array size must be a
+ * power of two. Lifting Scheme wavelet transforms are calculated in-place
+ * and the result is returned in the argument array.
+ *
+ * The result of forwardTrans is a set of wavelet coefficients ordered by
+ * increasing frequency and an approximate average of the input data set in
+ * vec[0]. The coefficient bands follow this element in powers of two (e.g.,
+ * 1, 2, 4, 8...).
+ *
+ * Default two step Lifting Scheme inverse wavelet transform
+ *
+ * inverseTrans is passed the result of an ordered wavelet transform,
+ * consisting of an average and a set of wavelet coefficients. The inverse
+ * transform is calculated in-place and the result is returned in the
+ * argument array.
+ *
+ * Line (with slope) wavelet
+ *
+ * The wavelet Lifting Scheme "LineWavelet" wavelet approximates the data set
+ * using a LineWavelet with with slope (in contrast to the HaarWavelet wavelet
+ * where a LineWavelet has zero slope is used to approximate the data).
+ *
+ * The predict stage of the LineWavelet wavelet "predicts" that an odd point
+ * will lie midway between its two neighboring even points. That is, that the
+ * odd point will lie on a LineWavelet between the two adjacent even points. The
+ * difference between this "prediction" and the actual odd value replaces the
+ * odd element.
+ *
+ * The update stage calculates the average of the odd and even element pairs,
+ * although the method is indirect, since the predict phase has over written the
+ * odd value.
+ *
+ * You may use this source code without limitation and without fee as long as
+ * you include:
+ *
+ * This software is provided "as is", without any warrenty or claim as to its
+ * usefulness. Anyone who uses this source code uses it at their own risk. Nor
+ * is any support provided by Ian Kaplan and Bear Products International.
+ *
+ * Please send any bug fixes or suggested source changes to:
+ *
+ *
+ * Calculate an extra "even" value for the LineWavelet wavelet algorithm at
+ * the end of the data series. Here we pretend that the last two values in
+ * the data series are at the x-axis coordinates 0 and 1, respectively. We
+ * then need to calculate the y-axis value at the x-axis coordinate 2. This
+ * point lies on a LineWavelet running through the points at 0 and 1.
+ *
+ * Given two points, x 1 , y 1 and x 2 ,
+ * y 2 , where
+ *
+ * calculate the point on the LineWavelet at x 3 , y 3 ,
+ * where
+ *
+ * The "two-point equation" for a LineWavelet given x 1 ,
+ * y 1 and x 2 , y 2 is
+ *
+ * Solving for y
+ *
+ * Since x 1 = 0 and x 2 = 1
+ *
+ * or
+ *
+ * We're calculating the value at x 3 = 2, so
+ *
+ * or
+ *
+ * Predict phase of LineWavelet Lifting Scheme wavelet
+ *
+ * The predict step attempts to "predict" the value of an odd element from
+ * the even elements. The difference between the prediction and the actual
+ * element is stored as a wavelet coefficient.
+ *
+ * The "predict" step takes place after the split step. The split step will
+ * move the odd elements (b j ) to the second half of the array,
+ * leaving the even elements (a i ) in the first half
+ *
+ * The predict step of the LineWavelet wavelet "predicts" that the odd
+ * element will be on a LineWavelet between two even elements.
+ *
+ * Note that when we get to the end of the data series the odd element is
+ * the last element in the data series (remember, wavelet algorithms work on
+ * data series with 2n elements). Here we "predict" that the odd
+ * element will be on a LineWavelet that runs through the last two even
+ * elements. This can be calculated by assuming that the last two even
+ * elements are located at x-axis coordinates 0 and 1, respectively. The odd
+ * element will be at 2. The new_y() function is called to do this
+ * simple calculation.
+ *
+ * The predict phase works on the odd elements in the second half of the
+ * array. The update phase works on the even elements in the first half of
+ * the array. The update phase attempts to preserve the average. After the
+ * update phase is completed the average of the even elements should be
+ * approximately the same as the average of the input data set from the
+ * previous iteration. The result of the update phase becomes the input for
+ * the next iteration.
+ *
+ * In a HaarWavelet wavelet the average that replaces the even element is
+ * calculated as the average of the even element and its associated odd
+ * element (e.g., its odd neighbor before the split). This is not possible
+ * in the LineWavelet wavelet since the odd element has been replaced by the
+ * difference between the odd element and the mid-point of its two even
+ * neighbors. As a result, the odd element cannot be recovered.
+ *
+ * The value that is added to the even element to preserve the average is
+ * calculated by the equation shown below. This equation is given in Wim
+ * Sweldens' journal articles and his tutorial (Building Your Own
+ * Wavelets at Home) and in Ripples in Mathematics. A somewhat
+ * more complete derivation of this equation is provided in Ripples in
+ * Mathematics by A. Jensen and A. la Cour-Harbo, Springer, 2001.
+ *
+ * The equation used to calculate the average is shown below for a given
+ * iteratin i. Note that the predict phase has already completed, so
+ * the odd values belong to iteration i+1.
+ *
+ * There is an edge problem here, when i = 0 and k = N/2 (e.g., there is no
+ * k-1 element). We assume that the odd i+1,k-1 is the same as
+ * odd k . So for the first element this becomes
+ *
+ *
+ * or
+ *
+ * The polynomial interpolation algorithm assumes that the known points are
+ * located at x-coordinates 0, 1,.. N-1. An interpolated point is calculated
+ * at x, using N coefficients. The polynomial coefficients for
+ * the point x can be calculated staticly, using the Lagrange
+ * method.
+ *
+ * For a given N-point polynomial interpolation, fill the coefficient table,
+ * for points 0.5 ... (N-0.5).
+ *
+ * PolynomialWavelets constructor
+ *
+ * Build the 4-point and 2-point polynomial coefficient tables.
+ *
+ * For the polynomial interpolation point x-coordinate x,
+ * return the associated polynomial interpolation coefficients.
+ *
+ * Given four points at the x,y coordinates {0,d0},
+ * {1,d1}, {2,d2}, {3,d3} return the
+ * y-coordinate value for the polynomial interpolated point at
+ * x.
+ *
+ * Polynomial wavelets
+ *
+ * This wavelet transform uses a polynomial interpolation wavelet (e.g., the
+ * function used to calculate the differences). A HaarWavelet scaling function
+ * (the calculation of the average for the even points) is used.
+ *
+ * This wavelet transform uses a two stage version of the lifting scheme. In the
+ * "classic" two stage Lifting Scheme wavelet the predict stage preceeds the
+ * update stage. Also, the algorithm is absolutely symetric, with only the
+ * operators (usually addition and subtraction) interchanged.
+ *
+ * The problem with the classic Lifting Scheme transform is that it can be
+ * difficult to determine how to calculate the smoothing (scaling) function in
+ * the update phase once the predict stage has altered the odd values. This
+ * version of the wavelet transform calculates the update stage first and then
+ * calculates the predict stage from the modified update values. In this case
+ * the predict stage uses 4-point polynomial interpolation using even values
+ * that result from the update stage.
+ *
+ * In this version of the wavelet transform the update stage is no longer
+ * perfectly symetric, since the forward and inverse transform equations differ
+ * by more than an addition or subtraction operator. However, this version of
+ * the transform produces a better result than the HaarWavelet transform
+ * extended with a polynomial interpolation stage.
+ *
+ * This algorithm was suggested to me from my reading of Wim Sweldens' tutorial
+ * Building Your Own Wavelets at Home.
+ *
+ * You may use this source code without limitation and without fee as long as
+ * you include:
+ *
+ * This software is provided "as is", without any warrenty or claim as to its
+ * usefulness. Anyone who uses this source code uses it at their own risk. Nor
+ * is any support provided by Ian Kaplan and Bear Products International.
+ *
+ * Please send any bug fixes or suggested source changes to:
+ *
+ *
+ * Copy four points or N (which ever is less) data points from
+ * vec into d These points are the "known" points used in the
+ * polynomial interpolation.
+ *
+ * The update stage calculates the forward and inverse HaarWavelet scaling
+ * functions. The forward HaarWavelet scaling function is simply the average
+ * of the even and odd elements. The inverse function is found by simple
+ * algebraic manipulation, solving for the even element given the average
+ * and the odd element.
+ *
+ * In this version of the wavelet transform the update stage preceeds the
+ * predict stage in the forward transform. In the inverse transform the
+ * predict stage preceeds the update stage, reversing the calculation on the
+ * odd elements.
+ *
+ * Predict an odd point from the even points, using 4-point polynomial
+ * interpolation.
+ *
+ * The four points used in the polynomial interpolation are the even points.
+ * We pretend that these four points are located at the x-coordinates
+ * 0,1,2,3. The first odd point interpolated will be located between the
+ * first and second even point, at 0.5. The next N-3 points are located at
+ * 1.5 (in the middle of the four points). The last two points are located
+ * at 2.5 and 3.5. For complete documentation see
+ *
+ * The difference between the predicted (interpolated) value and the actual
+ * odd value replaces the odd value in the forward transform.
+ *
+ * As the recursive steps proceed, N will eventually be 4 and then 2. When N
+ * = 4, linear interpolation is used. When N = 2, HaarWavelet interpolation
+ * is used (the prediction for the odd value is that it is equal to the even
+ * value).
+ *
+ * Polynomial wavelet lifting scheme transform.
+ *
+ * This version of the forwardTrans function overrides the function in the
+ * LiftingSchemeBaseWavelet base class. This function introduces an extra
+ * polynomial interpolation stage at the end of the transform.
+ *
+ * Polynomial wavelet lifting Scheme inverse transform.
+ *
+ * This version of the inverseTrans function overrides the function in the
+ * LiftingSchemeBaseWavelet base class. This function introduces an inverse
+ * polynomial interpolation stage at the start of the inverse transform.
+ * getPeakList
+ *
+AudioDispatcher dispatcher = new AudioDispatcher(stream, fftsize, overlap);
+dispatcher.addAudioProcessor(spectralPeakFollower);
+dispatcher.addAudioProcessor(new AudioProcessor() {
+
+ public void processingFinished() {
+ }
+
+ public boolean process(AudioEvent audioEvent) {
+ float[] noiseFloor = SpectralPeakProcessor.calculateNoiseFloor(spectralPeakFollower.getMagnitudes(), medianFilterLength, noiseFloorFactor);
+ List localMaxima = SpectralPeakProcessor.findLocalMaxima(spectralPeakFollower.getMagnitudes(), noiseFloor);
+ List list = SpectralPeakProcessor.findPeaks(spectralPeakFollower.getMagnitudes(), spectralPeakFollower.getFrequencyEstimates(), localMaxima, numberOfPeaks);
+ // do something with the list...
+ return true;
+ }
+});
+dispatcher.run();
+
+ *
+ * @author Joren Six
+ * @author William A. Sethares
+ * @author Andrew J. Milne
+ * @author Stefan Tiedje
+ * @author Anthony Prechtl
+ * @author James Plamondon
+ *
+ */
+public class SpectralPeakProcessor implements AudioProcessor {
+
+ /**
+ * The sample rate of the signal.
+ */
+ private final int sampleRate;
+
+ /**
+ * Cached calculations for the frequency calculation
+ */
+ private final double dt;
+ private final double cbin;
+ private final double inv_2pi;
+ private final double inv_deltat;
+ private final double inv_2pideltat;
+
+ /**
+ * The fft object used to calculate phase and magnitudes.
+ */
+ private final FFT fft;
+
+ /**
+ * The pahse info of the current frame.
+ */
+ private final float[] currentPhaseOffsets;
+
+ /**
+ * The magnitudes in the current frame.
+ */
+ private final float[] magnitudes;
+
+ /**
+ * Detailed frequency estimates for each bin, using phase info
+ */
+ private final float[] frequencyEstimates;
+
+ /**
+ * The phase information of the previous frame, or null.
+ */
+ private float[] previousPhaseOffsets;
+
+
+
+ public SpectralPeakProcessor(int bufferSize, int overlap, int sampleRate) {
+ fft = new FFT(bufferSize, new HammingWindow());
+
+ magnitudes = new float[bufferSize / 2];
+ currentPhaseOffsets = new float[bufferSize / 2];
+ frequencyEstimates = new float[bufferSize / 2];
+
+ dt = (bufferSize - overlap) / (double) sampleRate;
+ cbin = (double) (dt * sampleRate / (double) bufferSize);
+
+ inv_2pi = (double) (1.0 / (2.0 * Math.PI));
+ inv_deltat = (double) (1.0 / dt);
+ inv_2pideltat = (double) (inv_deltat * inv_2pi);
+
+ this.sampleRate = sampleRate;
+
+ }
+
+ private void calculateFFT(float[] audio) {
+ // Clone to prevent overwriting audio data
+ float[] fftData = audio.clone();
+ // Extract the power and phase data
+ fft.powerPhaseFFT(fftData, magnitudes, currentPhaseOffsets);
+ }
+
+ private void normalizeMagintudes(){
+ float maxMagnitude = (float) -1e6;
+ for(int i = 0;istop
seconds.
+ */
+ public void beatTrack(EventList el, double stop) {
+ ListIteratorcalcCoeff()
function. When filling the coefficient
+ * arrays, be aware that b[0]
corresponds to
+ * b1
.
+ *
+ * @author Damien Di Fede
+ * @author Joren Six
+ *
+ */
+public abstract class IIRFilter implements AudioProcessor {
+
+ /** The b coefficients. */
+ protected float[] b;
+
+ /** The a coefficients. */
+ protected float[] a;
+
+ /**
+ * The input values to the left of the output value currently being
+ * calculated.
+ */
+ protected float[] in;
+
+ /** The previous output values. */
+ protected float[] out;
+
+ private float frequency;
+
+ private final float sampleRate;
+
+
+ /**
+ * Constructs an IIRFilter with the given cutoff frequency that will be used
+ * to filter audio recorded at sampleRate
.
+ *
+ * @param freq
+ * the cutoff frequency
+ * @param sampleRate
+ * the sample rate of audio to be filtered
+ */
+ public IIRFilter(float freq, float sampleRate) {
+ this.sampleRate = sampleRate;
+ this.frequency = freq;
+ calcCoeff();
+ in = new float[a.length];
+ out = new float[b.length];
+ }
+
+ public void setFrequency(float freq){
+ this.frequency = freq;
+ calcCoeff();
+ }
+
+ /**
+ * Returns the cutoff frequency (in Hz).
+ *
+ * @return the current cutoff frequency (in Hz).
+ */
+ protected final float getFrequency() {
+ return frequency;
+ }
+
+ protected final float getSampleRate(){
+ return sampleRate;
+ }
+
+ /**
+ * Calculates the coefficients of the filter using the current cutoff
+ * frequency. To make your own IIRFilters, you must extend IIRFilter and
+ * implement this function. The frequency is expressed as a fraction of the
+ * sample rate. When filling the coefficient arrays, be aware that
+ * b[0]
corresponds to the coefficient
+ * b1
.
+ *
+ */
+ protected abstract void calcCoeff() ;
+
+
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+ float[] audioFloatBuffer = audioEvent.getFloatBuffer();
+
+ for (int i = audioEvent.getOverlap(); i < audioFloatBuffer.length; i++) {
+ //shift the in array
+ System.arraycopy(in, 0, in, 1, in.length - 1);
+ in[0] = audioFloatBuffer[i];
+
+ //calculate y based on a and b coefficients
+ //and in and out.
+ float y = 0;
+ for(int j = 0 ; j < a.length ; j++){
+ y += a[j] * in[j];
+ }
+ for(int j = 0 ; j < b.length ; j++){
+ y += b[j] * out[j];
+ }
+ //shift the out array
+ System.arraycopy(out, 0, out, 1, out.length - 1);
+ out[0] = y;
+
+ audioFloatBuffer[i] = y;
+ }
+ return true;
+ }
+
+
+ @Override
+ public void processingFinished() {
+
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/filters/LowPassFS.java b/app/src/main/java/be/tarsos/dsp/filters/LowPassFS.java
new file mode 100644
index 0000000..be4f83f
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/filters/LowPassFS.java
@@ -0,0 +1,64 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+/*
+ * Copyright (c) 2007 - 2008 by Damien Di Fede apt-get install ffmpeg
.
+ * ffmpeg -decoders | grep -E "^A" | sort
+avconv version 9.8, Copyright (c) 2000-2013 the Libav developers
+ built on Aug 26 2013 09:52:20 with gcc 4.4.3 (Ubuntu 4.4.3-4ubuntu5.1)
+A... 8svx_exp 8SVX exponential
+A... 8svx_fib 8SVX fibonacci
+A... aac AAC (Advanced Audio Coding)
+A... aac_latm AAC LATM (Advanced Audio Coding LATM syntax)
+A... ac3 ATSC A/52A (AC-3)
+A... adpcm_4xm ADPCM 4X Movie
+...
+
+ *
+ * @author Joren Six
+ */
+public class PipeDecoder {
+
+ private final static Logger LOG = Logger.getLogger(PipeDecoder.class.getName());
+ private final String pipeEnvironment;
+ private final String pipeArgument;
+ private final String pipeCommand;
+ private final int pipeBuffer;
+
+ private boolean printErrorstream = false;
+
+ private String decoderBinaryAbsolutePath;
+
+ public PipeDecoder(){
+ pipeBuffer = 10000;
+
+ //Use sensible defaults depending on the platform
+ if(System.getProperty("os.name").indexOf("indows") > 0 ){
+ pipeEnvironment = "cmd.exe";
+ pipeArgument = "/C";
+ }else if(new File("/bin/bash").exists()){
+ pipeEnvironment = "/bin/bash";
+ pipeArgument = "-c";
+ }else if (new File("/system/bin/sh").exists()){
+ //probably we are on android here
+ pipeEnvironment = "/system/bin/sh";
+ pipeArgument = "-c";
+ }else{
+ LOG.severe("Coud not find a command line environment (cmd.exe or /bin/bash)");
+ throw new Error("Decoding via a pipe will not work: Coud not find a command line environment (cmd.exe or /bin/bash)");
+ }
+
+ String path = System.getenv("PATH");
+ String arguments = " -ss %input_seeking% %number_of_seconds% -i \"%resource%\" -vn -ar %sample_rate% -ac %channels% -sample_fmt s16 -f s16le pipe:1";
+ if(isAvailable("ffmpeg")){
+ LOG.info("found ffmpeg on the path (" + path + "). Will use ffmpeg for decoding media files.");
+ pipeCommand = "ffmpeg" + arguments;
+ } else {
+ if(isAndroid()) {
+ String tempDirectory = System.getProperty("java.io.tmpdir");
+ printErrorstream=true;
+ File f = new File(tempDirectory, "ffmpeg");
+ if (f.exists() && f.length() > 1000000 && f.canExecute()) {
+ decoderBinaryAbsolutePath = f.getAbsolutePath();
+ } else {
+ LOG.severe("Could not find an ffmpeg binary for your Android system. Did you forget calling: 'new AndroidFFMPEGLocator(this);' ?");
+ LOG.severe("Tried to unpack a statically compiled ffmpeg binary for your architecture to: " + f.getAbsolutePath());
+ }
+ }else{
+ LOG.warning("Dit not find ffmpeg or avconv on your path(" + path + "), will try to download it automatically.");
+ FFMPEGDownloader downloader = new FFMPEGDownloader();
+ decoderBinaryAbsolutePath = downloader.ffmpegBinary();
+ if(decoderBinaryAbsolutePath==null){
+ LOG.severe("Could not download an ffmpeg binary automatically for your system.");
+ }
+ }
+ if(decoderBinaryAbsolutePath == null){
+ pipeCommand = "false";
+ throw new Error("Decoding via a pipe will not work: Could not find an ffmpeg binary for your system");
+ }else{
+ pipeCommand = '"' + decoderBinaryAbsolutePath + '"' + arguments;
+ }
+ }
+ }
+
+ private boolean isAvailable(String command){
+ try{
+ Runtime.getRuntime().exec(command + " -version");
+ return true;
+ }catch (Exception e){
+ return false;
+ }
+ }
+
+ public PipeDecoder(String pipeEnvironment,String pipeArgument,String pipeCommand,String pipeLogFile,int pipeBuffer){
+ this.pipeEnvironment = pipeEnvironment;
+ this.pipeArgument = pipeArgument;
+ this.pipeCommand = pipeCommand;
+ this.pipeBuffer = pipeBuffer;
+ }
+
+
+ public InputStream getDecodedStream(final String resource,final int targetSampleRate,final double timeOffset, double numberOfSeconds) {
+
+ try {
+ String command = pipeCommand;
+ command = command.replace("%input_seeking%",String.valueOf(timeOffset));
+ //defines the number of seconds to process
+ // -t 10.000 e.g. specifies to process ten seconds
+ // from the specified time offset (which is often zero).
+ if(numberOfSeconds>0){
+ command = command.replace("%number_of_seconds%","-t " + numberOfSeconds);
+ } else {
+ command = command.replace("%number_of_seconds%","");
+ }
+ command = command.replace("%resource%", resource);
+ command = command.replace("%sample_rate%", String.valueOf(targetSampleRate));
+ command = command.replace("%channels%","1");
+
+ ProcessBuilder pb;
+ pb= new ProcessBuilder(pipeEnvironment, pipeArgument , command);
+
+ LOG.info("Starting piped decoding process for " + resource);
+ LOG.info(" with command: " + command);
+ final Process process = pb.start();
+
+ final InputStream stdOut = new BufferedInputStream(process.getInputStream(), pipeBuffer){
+ @Override
+ public void close() throws IOException{
+ super.close();
+ // try to destroy the ffmpeg command after close
+ process.destroy();
+ }
+ };
+
+ if(printErrorstream) {
+ //print to log if requested
+ new ErrorStreamGobbler(process.getErrorStream(),LOG).start();
+ }else{
+ //makes sure the error stream is handled
+ //fix by SalomonBrys
+ //see https://github.com/JorenSix/TarsosDSP/pull/212
+ new ErrorStreamIgnorer(process.getErrorStream()).start();
+ }
+
+ new Thread(new Runnable(){
+ @Override
+ public void run() {
+ try {
+ process.waitFor();
+ LOG.info("Finished piped decoding process");
+ } catch (InterruptedException e) {
+ LOG.severe("Interrupted while waiting for decoding sub process exit.");
+ e.printStackTrace();
+ }
+ }},"Decoding Pipe").start();
+ return stdOut;
+ } catch (IOException e) {
+ LOG.warning("IO exception while decoding audio via sub process." + e.getMessage() );
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ public double getDuration(final String resource) {
+ double duration = -1;
+ try {
+ //use " for windows compatibility!
+ String command = "ffmpeg -i \"%resource%\"";
+
+ command = command.replace("%resource%", resource);
+
+ ProcessBuilder pb;
+ pb = new ProcessBuilder(pipeEnvironment, pipeArgument , command);
+
+ LOG.info("Starting duration command for " + resource);
+ LOG.fine(" with command: " + command);
+ final Process process = pb.start();
+
+ final InputStream stdOut = new BufferedInputStream(process.getInputStream(), pipeBuffer){
+ @Override
+ public void close() throws IOException{
+ super.close();
+ // try to destroy the ffmpeg command after close
+ process.destroy();
+ }
+ };
+
+ ErrorStreamStringGlobber essg = new ErrorStreamStringGlobber(process.getErrorStream());
+ essg.start();
+
+ new Thread(new Runnable(){
+ @Override
+ public void run() {
+ try {
+ process.waitFor();
+ LOG.info("Finished piped decoding process");
+ } catch (InterruptedException e) {
+ LOG.severe("Interrupted while waiting for decoding sub process exit.");
+ e.printStackTrace();
+ }
+ }},"Decoding Pipe").run();
+
+ String stdError = essg.getErrorStreamAsString();
+ Pattern regex = Pattern.compile(".*\\s.*Duration:\\s+(\\d\\d):(\\d\\d):(\\d\\d)\\.(\\d\\d), .*", Pattern.DOTALL | Pattern.MULTILINE);
+ Matcher regexMatcher = regex.matcher(stdError);
+ if (regexMatcher.find()) {
+ duration = Integer.valueOf(regexMatcher.group(1)) * 3600+
+ Integer.valueOf(regexMatcher.group(2)) * 60+
+ Integer.valueOf(regexMatcher.group(3)) +
+ Double.valueOf("." + regexMatcher.group(4) );
+ }
+ } catch (IOException e) {
+ LOG.warning("IO exception while decoding audio via sub process." + e.getMessage() );
+ e.printStackTrace();
+ }
+ return duration;
+ }
+
+ public void printBinaryInfo(){
+ try {
+ Process p = Runtime.getRuntime().exec(decoderBinaryAbsolutePath);
+ BufferedReader input = new BufferedReader(new InputStreamReader(p.getErrorStream()));
+ String line = null;
+ while ((line = input.readLine()) != null) {
+ System.out.println(line);
+ }
+ input.close();
+ //int exitVal =
+ p.waitFor();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Constructs the target audio format. The audio format is one channel
+ * signed PCM of a given sample rate.
+ *
+ * @param targetSampleRate
+ * The sample rate to convert to.
+ * @return The audio format after conversion.
+ */
+ public static TarsosDSPAudioFormat getTargetAudioFormat(int targetSampleRate) {
+ TarsosDSPAudioFormat audioFormat = new TarsosDSPAudioFormat(TarsosDSPAudioFormat.Encoding.PCM_SIGNED,
+ targetSampleRate,
+ 2 * 8,
+ 1,
+ 2,
+ targetSampleRate,
+ ByteOrder.BIG_ENDIAN.equals(ByteOrder.nativeOrder()));
+ return audioFormat;
+ }
+
+
+ private boolean isAndroid(){
+ try {
+ // This class is only available on android
+ Class.forName("android.app.Activity");
+ System.out.println("Running on Android!");
+ return true;
+ } catch(ClassNotFoundException e) {
+ //the class is not found when running JVM
+ return false;
+ }
+ }
+
+ private static class ErrorStreamIgnorer extends Thread {
+ private final InputStream is;
+
+ private ErrorStreamIgnorer(InputStream is) {
+ this.is = is;
+ }
+
+ @Override
+ public void run() {
+ try {
+ InputStreamReader isr = new InputStreamReader(is);
+ BufferedReader br = new BufferedReader(isr);
+ String line = null;
+ while ((line = br.readLine()) != null) {}
+ }
+ catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+ }
+
+ private class ErrorStreamGobbler extends Thread {
+ private final InputStream is;
+ private final Logger logger;
+
+ private ErrorStreamGobbler(InputStream is, Logger logger) {
+ this.is = is;
+ this.logger = logger;
+ }
+
+ @Override
+ public void run() {
+ try {
+ InputStreamReader isr = new InputStreamReader(is);
+ BufferedReader br = new BufferedReader(isr);
+ String line = null;
+ while ((line = br.readLine()) != null) {
+ logger.info(line);
+ }
+ }
+ catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+ }
+
+ private class ErrorStreamStringGlobber extends Thread {
+ private final InputStream is;
+ private final StringBuilder sb;
+
+ private ErrorStreamStringGlobber(InputStream is) {
+ this.is = is;
+ this.sb = new StringBuilder();
+ }
+
+ @Override
+ public void run() {
+ try {
+ InputStreamReader isr = new InputStreamReader(is);
+ BufferedReader br = new BufferedReader(isr);
+ String line = null;
+ while ((line = br.readLine()) != null) {
+ sb.append(line);
+ }
+ }
+ catch (IOException ioe) {
+ ioe.printStackTrace();
+ }
+ }
+
+ public String getErrorStreamAsString(){
+ return sb.toString();
+ }
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/io/PipedAudioStream.java b/app/src/main/java/be/tarsos/dsp/io/PipedAudioStream.java
new file mode 100644
index 0000000..fae6012
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/io/PipedAudioStream.java
@@ -0,0 +1,94 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.io;
+
+import java.io.InputStream;
+
+import be.tarsos.dsp.util.AudioResourceUtils;
+
+
+/**
+ * An audio file can be used to convert and read from. It uses libAV to convert
+ * about any audio format to a one channel PCM stream of a chosen sample rate. There is
+ * support for movie files as well, the first audio channel is then used as input.
+ * The resource is either a local file or a type of stream supported by libAV (e.g. HTTP streams);
+ *
+ * For a list of audio decoders the following command is practical:
+ *
+avconv -decoders | grep -E "^A" | sort
+
+
+A... 8svx_exp 8SVX exponential
+A... 8svx_fib 8SVX fibonacci
+A... aac AAC (Advanced Audio Coding)
+A... aac_latm AAC LATM (Advanced Audio Coding LATM syntax)
+...
+ *
+ */
+public class PipedAudioStream {
+
+ //private final static Logger LOG = Logger.getLogger(PipedAudioStream.class.getName());
+
+ private final String resource;
+ private static PipeDecoder pipeDecoder = new PipeDecoder();
+
+ public static void setDecoder(PipeDecoder decoder){
+ pipeDecoder = decoder;
+ }
+
+ private final PipeDecoder decoder;
+ public PipedAudioStream(String resource){
+ this.resource = AudioResourceUtils.sanitizeResource(resource);
+ decoder = pipeDecoder;
+ }
+
+ /**
+ * Return a one channel, signed PCM stream of audio of a defined sample rate.
+ * @param targetSampleRate The target sample stream.
+ * @param startTimeOffset The start time offset.
+ * @return An audio stream which can be used to read samples from.
+ */
+ public TarsosDSPAudioInputStream getMonoStream(int targetSampleRate,double startTimeOffset){
+ return getMonoStream(targetSampleRate, startTimeOffset,-1);
+ }
+
+ private TarsosDSPAudioFormat getTargetFormat(int targetSampleRate){
+ return new TarsosDSPAudioFormat(targetSampleRate, 16, 1, true, false);
+ }
+
+
+ /**
+ * Return a one channel, signed PCM stream of audio of a defined sample rate.
+ * @param targetSampleRate The target sample stream.
+ * @param startTimeOffset The start time offset.
+ * @param numberOfSeconds the number of seconds to pipe. If negative the stream is processed until end of stream.
+ * @return An audio stream which can be used to read samples from.
+ */
+ public TarsosDSPAudioInputStream getMonoStream(int targetSampleRate, double startTimeOffset,
+ double numberOfSeconds) {
+ InputStream stream = null;
+ stream = decoder.getDecodedStream(resource, targetSampleRate,startTimeOffset,numberOfSeconds);
+ return new UniversalAudioInputStream(stream, getTargetFormat(targetSampleRate));
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/io/TarsosDSPAudioFloatConverter.java b/app/src/main/java/be/tarsos/dsp/io/TarsosDSPAudioFloatConverter.java
new file mode 100644
index 0000000..b39f53d
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/io/TarsosDSPAudioFloatConverter.java
@@ -0,0 +1,1082 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+package be.tarsos.dsp.io;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.DoubleBuffer;
+import java.nio.FloatBuffer;
+
+import be.tarsos.dsp.io.TarsosDSPAudioFormat.Encoding;
+
+
+/**
+ * This class is used to convert between 8,16,24,32,32+ bit signed/unsigned
+ * big/litle endian fixed/floating point byte buffers and float buffers.
+ *
+ * @author Karl Helgason
+ */
+public abstract class TarsosDSPAudioFloatConverter {
+
+ public static final Encoding PCM_FLOAT = new Encoding("PCM_FLOAT");
+
+ /***************************************************************************
+ *
+ * LSB Filter, used filter least significant byte in samples arrays.
+ *
+ * Is used filter out data in lsb byte when SampleSizeInBits is not
+ * dividable by 8.
+ *
+ **************************************************************************/
+
+ private static class AudioFloatLSBFilter extends TarsosDSPAudioFloatConverter {
+
+ private final TarsosDSPAudioFloatConverter converter;
+
+ final private int offset;
+
+ final private int stepsize;
+
+ final private byte mask;
+
+ private byte[] mask_buffer;
+
+ public AudioFloatLSBFilter(TarsosDSPAudioFloatConverter converter,
+ TarsosDSPAudioFormat format) {
+ int bits = format.getSampleSizeInBits();
+ boolean bigEndian = format.isBigEndian();
+ this.converter = converter;
+ stepsize = (bits + 7) / 8;
+ offset = bigEndian ? (stepsize - 1) : 0;
+ int lsb_bits = bits % 8;
+ if (lsb_bits == 0)
+ mask = (byte) 0x00;
+ else if (lsb_bits == 1)
+ mask = (byte) 0x80;
+ else if (lsb_bits == 2)
+ mask = (byte) 0xC0;
+ else if (lsb_bits == 3)
+ mask = (byte) 0xE0;
+ else if (lsb_bits == 4)
+ mask = (byte) 0xF0;
+ else if (lsb_bits == 5)
+ mask = (byte) 0xF8;
+ else if (lsb_bits == 6)
+ mask = (byte) 0xFC;
+ else if (lsb_bits == 7)
+ mask = (byte) 0xFE;
+ else
+ mask = (byte) 0xFF;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ byte[] ret = converter.toByteArray(in_buff, in_offset, in_len,
+ out_buff, out_offset);
+
+ int out_offset_end = in_len * stepsize;
+ for (int i = out_offset + offset; i < out_offset_end; i += stepsize) {
+ out_buff[i] = (byte) (out_buff[i] & mask);
+ }
+
+ return ret;
+ }
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ if (mask_buffer == null || mask_buffer.length < in_buff.length)
+ mask_buffer = new byte[in_buff.length];
+ System.arraycopy(in_buff, 0, mask_buffer, 0, in_buff.length);
+ int in_offset_end = out_len * stepsize;
+ for (int i = in_offset + offset; i < in_offset_end; i += stepsize) {
+ mask_buffer[i] = (byte) (mask_buffer[i] & mask);
+ }
+ float[] ret = converter.toFloatArray(mask_buffer, in_offset,
+ out_buff, out_offset, out_len);
+ return ret;
+ }
+
+ }
+
+ /***************************************************************************
+ *
+ * 64 bit float, little/big-endian
+ *
+ **************************************************************************/
+
+ // PCM 64 bit float, little-endian
+ private static class AudioFloatConversion64L extends TarsosDSPAudioFloatConverter {
+ ByteBuffer bytebuffer = null;
+
+ DoubleBuffer floatbuffer = null;
+
+ double[] double_buff = null;
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int in_len = out_len * 8;
+ if (bytebuffer == null || bytebuffer.capacity() < in_len) {
+ bytebuffer = ByteBuffer.allocate(in_len).order(
+ ByteOrder.LITTLE_ENDIAN);
+ floatbuffer = bytebuffer.asDoubleBuffer();
+ }
+ bytebuffer.position(0);
+ floatbuffer.position(0);
+ bytebuffer.put(in_buff, in_offset, in_len);
+ if (double_buff == null
+ || double_buff.length < out_len + out_offset)
+ double_buff = new double[out_len + out_offset];
+ floatbuffer.get(double_buff, out_offset, out_len);
+ int out_offset_end = out_offset + out_len;
+ for (int i = out_offset; i < out_offset_end; i++) {
+ out_buff[i] = (float) double_buff[i];
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int out_len = in_len * 8;
+ if (bytebuffer == null || bytebuffer.capacity() < out_len) {
+ bytebuffer = ByteBuffer.allocate(out_len).order(
+ ByteOrder.LITTLE_ENDIAN);
+ floatbuffer = bytebuffer.asDoubleBuffer();
+ }
+ floatbuffer.position(0);
+ bytebuffer.position(0);
+ if (double_buff == null || double_buff.length < in_offset + in_len)
+ double_buff = new double[in_offset + in_len];
+ int in_offset_end = in_offset + in_len;
+ for (int i = in_offset; i < in_offset_end; i++) {
+ double_buff[i] = in_buff[i];
+ }
+ floatbuffer.put(double_buff, in_offset, in_len);
+ bytebuffer.get(out_buff, out_offset, out_len);
+ return out_buff;
+ }
+ }
+
+ // PCM 64 bit float, big-endian
+ private static class AudioFloatConversion64B extends TarsosDSPAudioFloatConverter {
+ ByteBuffer bytebuffer = null;
+
+ DoubleBuffer floatbuffer = null;
+
+ double[] double_buff = null;
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int in_len = out_len * 8;
+ if (bytebuffer == null || bytebuffer.capacity() < in_len) {
+ bytebuffer = ByteBuffer.allocate(in_len).order(
+ ByteOrder.BIG_ENDIAN);
+ floatbuffer = bytebuffer.asDoubleBuffer();
+ }
+ bytebuffer.position(0);
+ floatbuffer.position(0);
+ bytebuffer.put(in_buff, in_offset, in_len);
+ if (double_buff == null
+ || double_buff.length < out_len + out_offset)
+ double_buff = new double[out_len + out_offset];
+ floatbuffer.get(double_buff, out_offset, out_len);
+ int out_offset_end = out_offset + out_len;
+ for (int i = out_offset; i < out_offset_end; i++) {
+ out_buff[i] = (float) double_buff[i];
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int out_len = in_len * 8;
+ if (bytebuffer == null || bytebuffer.capacity() < out_len) {
+ bytebuffer = ByteBuffer.allocate(out_len).order(
+ ByteOrder.BIG_ENDIAN);
+ floatbuffer = bytebuffer.asDoubleBuffer();
+ }
+ floatbuffer.position(0);
+ bytebuffer.position(0);
+ if (double_buff == null || double_buff.length < in_offset + in_len)
+ double_buff = new double[in_offset + in_len];
+ int in_offset_end = in_offset + in_len;
+ for (int i = in_offset; i < in_offset_end; i++) {
+ double_buff[i] = in_buff[i];
+ }
+ floatbuffer.put(double_buff, in_offset, in_len);
+ bytebuffer.get(out_buff, out_offset, out_len);
+ return out_buff;
+ }
+ }
+
+ /***************************************************************************
+ *
+ * 32 bit float, little/big-endian
+ *
+ **************************************************************************/
+
+ // PCM 32 bit float, little-endian
+ private static class AudioFloatConversion32L extends TarsosDSPAudioFloatConverter {
+ ByteBuffer bytebuffer = null;
+
+ FloatBuffer floatbuffer = null;
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int in_len = out_len * 4;
+ if (bytebuffer == null || bytebuffer.capacity() < in_len) {
+ bytebuffer = ByteBuffer.allocate(in_len).order(
+ ByteOrder.LITTLE_ENDIAN);
+ floatbuffer = bytebuffer.asFloatBuffer();
+ }
+ bytebuffer.position(0);
+ floatbuffer.position(0);
+ bytebuffer.put(in_buff, in_offset, in_len);
+ floatbuffer.get(out_buff, out_offset, out_len);
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int out_len = in_len * 4;
+ if (bytebuffer == null || bytebuffer.capacity() < out_len) {
+ bytebuffer = ByteBuffer.allocate(out_len).order(
+ ByteOrder.LITTLE_ENDIAN);
+ floatbuffer = bytebuffer.asFloatBuffer();
+ }
+ floatbuffer.position(0);
+ bytebuffer.position(0);
+ floatbuffer.put(in_buff, in_offset, in_len);
+ bytebuffer.get(out_buff, out_offset, out_len);
+ return out_buff;
+ }
+ }
+
+ // PCM 32 bit float, big-endian
+ private static class AudioFloatConversion32B extends TarsosDSPAudioFloatConverter {
+ ByteBuffer bytebuffer = null;
+
+ FloatBuffer floatbuffer = null;
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int in_len = out_len * 4;
+ if (bytebuffer == null || bytebuffer.capacity() < in_len) {
+ bytebuffer = ByteBuffer.allocate(in_len).order(
+ ByteOrder.BIG_ENDIAN);
+ floatbuffer = bytebuffer.asFloatBuffer();
+ }
+ bytebuffer.position(0);
+ floatbuffer.position(0);
+ bytebuffer.put(in_buff, in_offset, in_len);
+ floatbuffer.get(out_buff, out_offset, out_len);
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int out_len = in_len * 4;
+ if (bytebuffer == null || bytebuffer.capacity() < out_len) {
+ bytebuffer = ByteBuffer.allocate(out_len).order(
+ ByteOrder.BIG_ENDIAN);
+ floatbuffer = bytebuffer.asFloatBuffer();
+ }
+ floatbuffer.position(0);
+ bytebuffer.position(0);
+ floatbuffer.put(in_buff, in_offset, in_len);
+ bytebuffer.get(out_buff, out_offset, out_len);
+ return out_buff;
+ }
+ }
+
+ /***************************************************************************
+ *
+ * 8 bit signed/unsigned
+ *
+ **************************************************************************/
+
+ // PCM 8 bit, signed
+ private static class AudioFloatConversion8S extends TarsosDSPAudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++)
+ out_buff[ox++] = in_buff[ix++] * (1.0f / 127.0f);
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++)
+ out_buff[ox++] = (byte) (in_buff[ix++] * 127.0f);
+ return out_buff;
+ }
+ }
+
+ // PCM 8 bit, unsigned
+ private static class AudioFloatConversion8U extends TarsosDSPAudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++)
+ out_buff[ox++] = ((in_buff[ix++] & 0xFF) - 127)
+ * (1.0f / 127.0f);
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++)
+ out_buff[ox++] = (byte) (127 + in_buff[ix++] * 127.0f);
+ return out_buff;
+ }
+ }
+
+ /***************************************************************************
+ *
+ * 16 bit signed/unsigned, little/big-endian
+ *
+ **************************************************************************/
+
+ // PCM 16 bit, signed, little-endian
+ private static class AudioFloatConversion16SL extends TarsosDSPAudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int len = out_offset + out_len;
+ for (int ox = out_offset; ox < len; ox++) {
+ out_buff[ox] = ((short) ((in_buff[ix++] & 0xFF) |
+ (in_buff[ix++] << 8))) * (1.0f / 32767.0f);
+ }
+
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ox = out_offset;
+ int len = in_offset + in_len;
+ for (int ix = in_offset; ix < len; ix++) {
+ int x = (int) (in_buff[ix] * 32767.0);
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 16 bit, signed, big-endian
+ private static class AudioFloatConversion16SB extends TarsosDSPAudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ out_buff[ox++] = ((short) ((in_buff[ix++] << 8) |
+ (in_buff[ix++] & 0xFF))) * (1.0f / 32767.0f);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * 32767.0);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 16 bit, unsigned, little-endian
+ private static class AudioFloatConversion16UL extends TarsosDSPAudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8);
+ out_buff[ox++] = (x - 32767) * (1.0f / 32767.0f);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = 32767 + (int) (in_buff[ix++] * 32767.0);
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 16 bit, unsigned, big-endian
+ private static class AudioFloatConversion16UB extends TarsosDSPAudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+ out_buff[ox++] = (x - 32767) * (1.0f / 32767.0f);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = 32767 + (int) (in_buff[ix++] * 32767.0);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ }
+ return out_buff;
+ }
+ }
+
+ /***************************************************************************
+ *
+ * 24 bit signed/unsigned, little/big-endian
+ *
+ **************************************************************************/
+
+ // PCM 24 bit, signed, little-endian
+ private static class AudioFloatConversion24SL extends TarsosDSPAudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8)
+ | ((in_buff[ix++] & 0xFF) << 16);
+ if (x > 0x7FFFFF)
+ x -= 0x1000000;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFF);
+ if (x < 0)
+ x += 0x1000000;
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) (x >>> 16);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 24 bit, signed, big-endian
+ private static class AudioFloatConversion24SB extends TarsosDSPAudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = ((in_buff[ix++] & 0xFF) << 16)
+ | ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+ if (x > 0x7FFFFF)
+ x -= 0x1000000;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFF);
+ if (x < 0)
+ x += 0x1000000;
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 24 bit, unsigned, little-endian
+ private static class AudioFloatConversion24UL extends TarsosDSPAudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8)
+ | ((in_buff[ix++] & 0xFF) << 16);
+ x -= 0x7FFFFF;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFF);
+ x += 0x7FFFFF;
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) (x >>> 16);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 24 bit, unsigned, big-endian
+ private static class AudioFloatConversion24UB extends TarsosDSPAudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = ((in_buff[ix++] & 0xFF) << 16)
+ | ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+ x -= 0x7FFFFF;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFF);
+ x += 0x7FFFFF;
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ }
+ return out_buff;
+ }
+ }
+
+ /***************************************************************************
+ *
+ * 32 bit signed/unsigned, little/big-endian
+ *
+ **************************************************************************/
+
+ // PCM 32 bit, signed, little-endian
+ private static class AudioFloatConversion32SL extends TarsosDSPAudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8) |
+ ((in_buff[ix++] & 0xFF) << 16) |
+ ((in_buff[ix++] & 0xFF) << 24);
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 24);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 32 bit, signed, big-endian
+ private static class AudioFloatConversion32SB extends TarsosDSPAudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = ((in_buff[ix++] & 0xFF) << 24) |
+ ((in_buff[ix++] & 0xFF) << 16) |
+ ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+ out_buff[ox++] = (byte) (x >>> 24);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 32 bit, unsigned, little-endian
+ private static class AudioFloatConversion32UL extends TarsosDSPAudioFloatConverter {
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8) |
+ ((in_buff[ix++] & 0xFF) << 16) |
+ ((in_buff[ix++] & 0xFF) << 24);
+ x -= 0x7FFFFFFF;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+ x += 0x7FFFFFFF;
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 24);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 32 bit, unsigned, big-endian
+ private static class AudioFloatConversion32UB extends TarsosDSPAudioFloatConverter {
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = ((in_buff[ix++] & 0xFF) << 24) |
+ ((in_buff[ix++] & 0xFF) << 16) |
+ ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+ x -= 0x7FFFFFFF;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+ x += 0x7FFFFFFF;
+ out_buff[ox++] = (byte) (x >>> 24);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ }
+ return out_buff;
+ }
+ }
+
+ /***************************************************************************
+ *
+ * 32+ bit signed/unsigned, little/big-endian
+ *
+ **************************************************************************/
+
+ // PCM 32+ bit, signed, little-endian
+ private static class AudioFloatConversion32xSL extends TarsosDSPAudioFloatConverter {
+
+ final int xbytes;
+
+ public AudioFloatConversion32xSL(int xbytes) {
+ this.xbytes = xbytes;
+ }
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ ix += xbytes;
+ int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8)
+ | ((in_buff[ix++] & 0xFF) << 16)
+ | ((in_buff[ix++] & 0xFF) << 24);
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+ for (int j = 0; j < xbytes; j++) {
+ out_buff[ox++] = 0;
+ }
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 24);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 32+ bit, signed, big-endian
+ private static class AudioFloatConversion32xSB extends TarsosDSPAudioFloatConverter {
+
+ final int xbytes;
+
+ public AudioFloatConversion32xSB(int xbytes) {
+ this.xbytes = xbytes;
+ }
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = ((in_buff[ix++] & 0xFF) << 24)
+ | ((in_buff[ix++] & 0xFF) << 16)
+ | ((in_buff[ix++] & 0xFF) << 8)
+ | (in_buff[ix++] & 0xFF);
+ ix += xbytes;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+ out_buff[ox++] = (byte) (x >>> 24);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ for (int j = 0; j < xbytes; j++) {
+ out_buff[ox++] = 0;
+ }
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 32+ bit, unsigned, little-endian
+ private static class AudioFloatConversion32xUL extends TarsosDSPAudioFloatConverter {
+
+ final int xbytes;
+
+ public AudioFloatConversion32xUL(int xbytes) {
+ this.xbytes = xbytes;
+ }
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ ix += xbytes;
+ int x = (in_buff[ix++] & 0xFF) | ((in_buff[ix++] & 0xFF) << 8)
+ | ((in_buff[ix++] & 0xFF) << 16)
+ | ((in_buff[ix++] & 0xFF) << 24);
+ x -= 0x7FFFFFFF;
+ out_buff[ox++] = x * (1.0f / (float)0x7FFFFFFF);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * (float)0x7FFFFFFF);
+ x += 0x7FFFFFFF;
+ for (int j = 0; j < xbytes; j++) {
+ out_buff[ox++] = 0;
+ }
+ out_buff[ox++] = (byte) x;
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 24);
+ }
+ return out_buff;
+ }
+ }
+
+ // PCM 32+ bit, unsigned, big-endian
+ private static class AudioFloatConversion32xUB extends TarsosDSPAudioFloatConverter {
+
+ final int xbytes;
+
+ public AudioFloatConversion32xUB(int xbytes) {
+ this.xbytes = xbytes;
+ }
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < out_len; i++) {
+ int x = ((in_buff[ix++] & 0xFF) << 24) |
+ ((in_buff[ix++] & 0xFF) << 16) |
+ ((in_buff[ix++] & 0xFF) << 8) | (in_buff[ix++] & 0xFF);
+ ix += xbytes;
+ x -= 2147483647;
+ out_buff[ox++] = x * (1.0f / 2147483647.0f);
+ }
+ return out_buff;
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff, int out_offset) {
+ int ix = in_offset;
+ int ox = out_offset;
+ for (int i = 0; i < in_len; i++) {
+ int x = (int) (in_buff[ix++] * 2147483647.0);
+ x += 2147483647;
+ out_buff[ox++] = (byte) (x >>> 24);
+ out_buff[ox++] = (byte) (x >>> 16);
+ out_buff[ox++] = (byte) (x >>> 8);
+ out_buff[ox++] = (byte) x;
+ for (int j = 0; j < xbytes; j++) {
+ out_buff[ox++] = 0;
+ }
+ }
+ return out_buff;
+ }
+ }
+
+ public static TarsosDSPAudioFloatConverter getConverter(TarsosDSPAudioFormat format) {
+ TarsosDSPAudioFloatConverter conv = null;
+ if (format.getFrameSize() == 0)
+ return null;
+ if (format.getFrameSize() !=
+ ((format.getSampleSizeInBits() + 7) / 8) * format.getChannels()) {
+ return null;
+ }
+ if (format.getEncoding().equals(Encoding.PCM_SIGNED)) {
+ if (format.isBigEndian()) {
+ if (format.getSampleSizeInBits() <= 8) {
+ conv = new AudioFloatConversion8S();
+ } else if (format.getSampleSizeInBits() > 8 &&
+ format.getSampleSizeInBits() <= 16) {
+ conv = new AudioFloatConversion16SB();
+ } else if (format.getSampleSizeInBits() > 16 &&
+ format.getSampleSizeInBits() <= 24) {
+ conv = new AudioFloatConversion24SB();
+ } else if (format.getSampleSizeInBits() > 24 &&
+ format.getSampleSizeInBits() <= 32) {
+ conv = new AudioFloatConversion32SB();
+ } else if (format.getSampleSizeInBits() > 32) {
+ conv = new AudioFloatConversion32xSB(((format
+ .getSampleSizeInBits() + 7) / 8) - 4);
+ }
+ } else {
+ if (format.getSampleSizeInBits() <= 8) {
+ conv = new AudioFloatConversion8S();
+ } else if (format.getSampleSizeInBits() > 8 &&
+ format.getSampleSizeInBits() <= 16) {
+ conv = new AudioFloatConversion16SL();
+ } else if (format.getSampleSizeInBits() > 16 &&
+ format.getSampleSizeInBits() <= 24) {
+ conv = new AudioFloatConversion24SL();
+ } else if (format.getSampleSizeInBits() > 24 &&
+ format.getSampleSizeInBits() <= 32) {
+ conv = new AudioFloatConversion32SL();
+ } else if (format.getSampleSizeInBits() > 32) {
+ conv = new AudioFloatConversion32xSL(((format
+ .getSampleSizeInBits() + 7) / 8) - 4);
+ }
+ }
+ } else if (format.getEncoding().equals(Encoding.PCM_UNSIGNED)) {
+ if (format.isBigEndian()) {
+ if (format.getSampleSizeInBits() <= 8) {
+ conv = new AudioFloatConversion8U();
+ } else if (format.getSampleSizeInBits() > 8 &&
+ format.getSampleSizeInBits() <= 16) {
+ conv = new AudioFloatConversion16UB();
+ } else if (format.getSampleSizeInBits() > 16 &&
+ format.getSampleSizeInBits() <= 24) {
+ conv = new AudioFloatConversion24UB();
+ } else if (format.getSampleSizeInBits() > 24 &&
+ format.getSampleSizeInBits() <= 32) {
+ conv = new AudioFloatConversion32UB();
+ } else if (format.getSampleSizeInBits() > 32) {
+ conv = new AudioFloatConversion32xUB(((
+ format.getSampleSizeInBits() + 7) / 8) - 4);
+ }
+ } else {
+ if (format.getSampleSizeInBits() <= 8) {
+ conv = new AudioFloatConversion8U();
+ } else if (format.getSampleSizeInBits() > 8 &&
+ format.getSampleSizeInBits() <= 16) {
+ conv = new AudioFloatConversion16UL();
+ } else if (format.getSampleSizeInBits() > 16 &&
+ format.getSampleSizeInBits() <= 24) {
+ conv = new AudioFloatConversion24UL();
+ } else if (format.getSampleSizeInBits() > 24 &&
+ format.getSampleSizeInBits() <= 32) {
+ conv = new AudioFloatConversion32UL();
+ } else if (format.getSampleSizeInBits() > 32) {
+ conv = new AudioFloatConversion32xUL(((
+ format.getSampleSizeInBits() + 7) / 8) - 4);
+ }
+ }
+ } else if (format.getEncoding().equals(PCM_FLOAT)) {
+ if (format.getSampleSizeInBits() == 32) {
+ if (format.isBigEndian())
+ conv = new AudioFloatConversion32B();
+ else
+ conv = new AudioFloatConversion32L();
+ } else if (format.getSampleSizeInBits() == 64) {
+ if (format.isBigEndian())
+ conv = new AudioFloatConversion64B();
+ else
+ conv = new AudioFloatConversion64L();
+ }
+
+ }
+
+ if ((format.getEncoding().equals(Encoding.PCM_SIGNED) ||
+ format.getEncoding().equals(Encoding.PCM_UNSIGNED)) &&
+ (format.getSampleSizeInBits() % 8 != 0)) {
+ conv = new AudioFloatLSBFilter(conv, format);
+ }
+
+ if (conv != null)
+ conv.format = format;
+ return conv;
+ }
+
+ private TarsosDSPAudioFormat format;
+
+ public TarsosDSPAudioFormat getFormat() {
+ return format;
+ }
+
+ public abstract float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_offset, int out_len);
+
+ public float[] toFloatArray(byte[] in_buff, float[] out_buff,
+ int out_offset, int out_len) {
+ return toFloatArray(in_buff, 0, out_buff, out_offset, out_len);
+ }
+
+ public float[] toFloatArray(byte[] in_buff, int in_offset,
+ float[] out_buff, int out_len) {
+ return toFloatArray(in_buff, in_offset, out_buff, 0, out_len);
+ }
+
+ public float[] toFloatArray(byte[] in_buff, float[] out_buff, int out_len) {
+ return toFloatArray(in_buff, 0, out_buff, 0, out_len);
+ }
+
+ public float[] toFloatArray(byte[] in_buff, float[] out_buff) {
+ return toFloatArray(in_buff, 0, out_buff, 0, out_buff.length);
+ }
+
+ public abstract byte[] toByteArray(float[] in_buff, int in_offset,
+ int in_len, byte[] out_buff, int out_offset);
+
+ public byte[] toByteArray(float[] in_buff, int in_len, byte[] out_buff,
+ int out_offset) {
+ return toByteArray(in_buff, 0, in_len, out_buff, out_offset);
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_offset, int in_len,
+ byte[] out_buff) {
+ return toByteArray(in_buff, in_offset, in_len, out_buff, 0);
+ }
+
+ public byte[] toByteArray(float[] in_buff, int in_len, byte[] out_buff) {
+ return toByteArray(in_buff, 0, in_len, out_buff, 0);
+ }
+
+ public byte[] toByteArray(float[] in_buff, byte[] out_buff) {
+ return toByteArray(in_buff, 0, in_buff.length, out_buff, 0);
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/io/TarsosDSPAudioFormat.java b/app/src/main/java/be/tarsos/dsp/io/TarsosDSPAudioFormat.java
new file mode 100644
index 0000000..a1de2e9
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/io/TarsosDSPAudioFormat.java
@@ -0,0 +1,648 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.io;
+
+/*
+ * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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 General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * AudioFormat
is the class that specifies a particular arrangement of data in a sound stream.
+ * By examing the information stored in the audio format, you can discover how to interpret the bits in the
+ * binary sound data.
+ * AudioFormat
class accommodates a number of common sound-file encoding techniques, including
+ * pulse-code modulation (PCM), mu-law encoding, and a-law encoding. These encoding techniques are predefined,
+ * but service providers can create new encoding types.
+ * The encoding that a specific format uses is named by its encoding
field.
+ *AudioFormat
object can include a set of
+ * properties. A property is a pair of key and value: the key
+ * is of type String
, the associated property
+ * value is an arbitrary object. Properties specify
+ * additional format specifications, like the bit rate for
+ * compressed formats. Properties are mainly used as a means
+ * to transport additional information of the audio format
+ * to and from the service providers. Therefore, properties
+ * are ignored in the AudioFormat method.
+ *
+ *
+ *
+ *
+ *
+ *
+ * Property key
+ * Value type
+ * Description
+ *
+ *
+ * "bitrate"
+ * {@link java.lang.Integer Integer}
+ * average bit rate in bits per second
+ *
+ *
+ * "vbr"
+ * {@link java.lang.Boolean Boolean}
+ *
+ * true
, if the file is encoded in variable bit
+ * rate (VBR)
+ *
+ * "quality"
+ * {@link java.lang.Integer Integer}
+ * encoding/conversion quality, 1..100
+ * AudioFormat
with the given parameters.
+ * The encoding specifies the convention used to represent the data.
+ * The other parameters are further explained in the
+ * @param encoding the audio encoding technique
+ * @param sampleRate the number of samples per second
+ * @param sampleSizeInBits the number of bits in each sample
+ * @param channels the number of channels (1 for mono, 2 for stereo, and so on)
+ * @param frameSize the number of bytes in each frame
+ * @param frameRate the number of frames per second
+ * @param bigEndian indicates whether the data for a single sample
+ * is stored in big-endian byte order (false
+ * means little-endian)
+ */
+ public TarsosDSPAudioFormat(Encoding encoding, float sampleRate, int sampleSizeInBits,
+ int channels, int frameSize, float frameRate, boolean bigEndian) {
+
+ this.encoding = encoding;
+ this.sampleRate = sampleRate;
+ this.sampleSizeInBits = sampleSizeInBits;
+ this.channels = channels;
+ this.frameSize = frameSize;
+ this.frameRate = frameRate;
+ this.bigEndian = bigEndian;
+ this.properties = null;
+ }
+
+
+ /**
+ * Constructs an AudioFormat
with the given parameters.
+ * The encoding specifies the convention used to represent the data.
+ * The other parameters are further explained in the
+ * @param encoding the audio encoding technique
+ * @param sampleRate the number of samples per second
+ * @param sampleSizeInBits the number of bits in each sample
+ * @param channels the number of channels (1 for mono, 2 for
+ * stereo, and so on)
+ * @param frameSize the number of bytes in each frame
+ * @param frameRate the number of frames per second
+ * @param bigEndian indicates whether the data for a single sample
+ * is stored in big-endian byte order
+ * (false
means little-endian)
+ * @param properties a Map<String,Object>
object
+ * containing format properties
+ *
+ * @since 1.5
+ */
+ public TarsosDSPAudioFormat(Encoding encoding, float sampleRate,
+ int sampleSizeInBits, int channels,
+ int frameSize, float frameRate,
+ boolean bigEndian, MapAudioFormat
with a linear PCM encoding and
+ * the given parameters. The frame size is set to the number of bytes
+ * required to contain one sample from each channel, and the frame rate
+ * is set to the sample rate.
+ *
+ * @param sampleRate the number of samples per second
+ * @param sampleSizeInBits the number of bits in each sample
+ * @param channels the number of channels (1 for mono, 2 for stereo, and so on)
+ * @param signed indicates whether the data is signed or unsigned
+ * @param bigEndian indicates whether the data for a single sample
+ * is stored in big-endian byte order (false
+ * means little-endian)
+ */
+ public TarsosDSPAudioFormat(float sampleRate, int sampleSizeInBits,
+ int channels, boolean signed, boolean bigEndian) {
+
+ this((signed ? Encoding.PCM_SIGNED : Encoding.PCM_UNSIGNED),
+ sampleRate,
+ sampleSizeInBits,
+ channels,
+ (channels == NOT_SPECIFIED || sampleSizeInBits == NOT_SPECIFIED)?
+ NOT_SPECIFIED:
+ ((sampleSizeInBits + 7) / 8) * channels,
+ sampleRate,
+ bigEndian);
+ }
+
+ /**
+ * Obtains the type of encoding for sounds in this format.
+ *
+ * @return the encoding type
+ * @see Encoding#PCM_SIGNED
+ * @see Encoding#PCM_UNSIGNED
+ * @see Encoding#ULAW
+ * @see Encoding#ALAW
+ */
+ public Encoding getEncoding() {
+
+ return encoding;
+ }
+
+ /**
+ * Obtains the sample rate.
+ * For compressed formats, the return value is the sample rate of the uncompressed
+ * audio data.
+ * When this AudioFormat is used for queries capabilities , a sample rate of
+ * AudioSystem.NOT_SPECIFIED
means that any sample rate is
+ * acceptable. AudioSystem.NOT_SPECIFIED
is also returned when
+ * the sample rate is not defined for this audio format.
+ * @return the number of samples per second,
+ * or AudioSystem.NOT_SPECIFIED
+ *
+ * @see #getFrameRate()
+ */
+ public float getSampleRate() {
+
+ return sampleRate;
+ }
+
+ /**
+ * Obtains the size of a sample.
+ * For compressed formats, the return value is the sample size of the
+ * uncompressed audio data.
+ * When this AudioFormat is used for queries or capabilities , a sample size of
+ * AudioSystem.NOT_SPECIFIED
means that any sample size is
+ * acceptable. AudioSystem.NOT_SPECIFIED
is also returned when
+ * the sample size is not defined for this audio format.
+ * @return the number of bits in each sample,
+ * or AudioSystem.NOT_SPECIFIED
+ *
+ * @see #getFrameSize()
+ */
+ public int getSampleSizeInBits() {
+
+ return sampleSizeInBits;
+ }
+
+ /**
+ * Obtains the number of channels.
+ * When this AudioFormat is used for queries or capabilities , a return value of
+ * AudioSystem.NOT_SPECIFIED
means that any (positive) number of channels is
+ * acceptable.
+ * @return The number of channels (1 for mono, 2 for stereo, etc.),
+ * or AudioSystem.NOT_SPECIFIED
+ *
+ */
+ public int getChannels() {
+
+ return channels;
+ }
+
+ /**
+ * Obtains the frame size in bytes.
+ * When this AudioFormat is used for queries or capabilities, a frame size of
+ * AudioSystem.NOT_SPECIFIED
means that any frame size is
+ * acceptable. AudioSystem.NOT_SPECIFIED
is also returned when
+ * the frame size is not defined for this audio format.
+ * @return the number of bytes per frame,
+ * or AudioSystem.NOT_SPECIFIED
+ *
+ * @see #getSampleSizeInBits()
+ */
+ public int getFrameSize() {
+
+ return frameSize;
+ }
+
+ /**
+ * Obtains the frame rate in frames per second.
+ * When this AudioFormat is used for queries or capabilities , a frame rate of
+ * AudioSystem.NOT_SPECIFIED
means that any frame rate is
+ * acceptable. AudioSystem.NOT_SPECIFIED
is also returned when
+ * the frame rate is not defined for this audio format.
+ * @return the number of frames per second,
+ * or AudioSystem.NOT_SPECIFIED
+ *
+ * @see #getSampleRate()
+ */
+ public float getFrameRate() {
+
+ return frameRate;
+ }
+
+
+ /**
+ * Indicates whether the audio data is stored in big-endian or little-endian
+ * byte order. If the sample size is not more than one byte, the return value is
+ * irrelevant.
+ * @return true
if the data is stored in big-endian byte order,
+ * false
if little-endian
+ */
+ public boolean isBigEndian() {
+
+ return bigEndian;
+ }
+
+
+ /**
+ * Obtain an unmodifiable map of properties.
+ * The concept of properties is further explained in
+ * the.
+ *
+ * @return a Map<String,Object>
object containing
+ * all properties. If no properties are recognized, an empty map is
+ * returned.
+ *
+ * @see #getProperty(String)
+ * @since 1.5
+ */
+ @SuppressWarnings("unchecked")
+ public Mapnull
.
+ *
+ * @param key the key of the desired property
+ * @return the value of the property with the specified key,
+ * or null
if the property does not exist.
+ *
+ * @see #properties()
+ * @since 1.5
+ */
+ public Object getProperty(String key) {
+ if (properties == null) {
+ return null;
+ }
+ return properties.get(key);
+ }
+
+
+ /**
+ * Indicates whether this format matches the one specified. To match,
+ * two formats must have the same encoding, the same number of channels,
+ * and the same number of bits per sample and bytes per frame.
+ * The two formats must also have the same sample rate,
+ * unless the specified format has the sample rate value AudioSystem.NOT_SPECIFIED
,
+ * which any sample rate will match. The frame rates must
+ * similarly be equal, unless the specified format has the frame rate
+ * value AudioSystem.NOT_SPECIFIED
. The byte order (big-endian or little-endian)
+ * must match if the sample size is greater than one byte.
+ *
+ * @param format format to test for match
+ * @return true
if this format matches the one specified,
+ * false
otherwise.
+ */
+ /*
+ * $$kk: 04.20.99: i changed the semantics of this.
+ */
+ public boolean matches(TarsosDSPAudioFormat format) {
+
+ return format.getEncoding().equals(getEncoding()) &&
+ ((format.getSampleRate() == (float) NOT_SPECIFIED) || (format.getSampleRate() == getSampleRate())) &&
+ (format.getSampleSizeInBits() == getSampleSizeInBits()) &&
+ (format.getChannels() == getChannels() &&
+ (format.getFrameSize() == getFrameSize()) &&
+ ((format.getFrameRate() == (float) NOT_SPECIFIED) || (format.getFrameRate() == getFrameRate())) &&
+ ((format.getSampleSizeInBits() <= 8) || (format.isBigEndian() == isBigEndian())));
+ }
+
+
+ /**
+ * Returns a string that describes the format, such as:
+ * "PCM SIGNED 22050 Hz 16 bit mono big-endian". The contents of the string
+ * may vary between implementations of Java Sound.
+ *
+ * @return a string that describes the format parameters
+ */
+ public String toString() {
+ String sEncoding = "";
+ if (getEncoding() != null) {
+ sEncoding = getEncoding().toString() + " ";
+ }
+
+ String sSampleRate;
+ if (getSampleRate() == (float) NOT_SPECIFIED) {
+ sSampleRate = "unknown sample rate, ";
+ } else {
+ sSampleRate = getSampleRate() + " Hz, ";
+ }
+
+ String sSampleSizeInBits;
+ if (getSampleSizeInBits() == (float) NOT_SPECIFIED) {
+ sSampleSizeInBits = "unknown bits per sample, ";
+ } else {
+ sSampleSizeInBits = getSampleSizeInBits() + " bit, ";
+ }
+
+ String sChannels;
+ if (getChannels() == 1) {
+ sChannels = "mono, ";
+ } else
+ if (getChannels() == 2) {
+ sChannels = "stereo, ";
+ } else {
+ if (getChannels() == NOT_SPECIFIED) {
+ sChannels = " unknown number of channels, ";
+ } else {
+ sChannels = getChannels()+" channels, ";
+ }
+ }
+
+ String sFrameSize;
+ if (getFrameSize() == (float) NOT_SPECIFIED) {
+ sFrameSize = "unknown frame size, ";
+ } else {
+ sFrameSize = getFrameSize()+ " bytes/frame, ";
+ }
+
+ String sFrameRate = "";
+ if (Math.abs(getSampleRate() - getFrameRate()) > 0.00001) {
+ if (getFrameRate() == (float) NOT_SPECIFIED) {
+ sFrameRate = "unknown frame rate, ";
+ } else {
+ sFrameRate = getFrameRate() + " frames/second, ";
+ }
+ }
+
+ String sEndian = "";
+ if ((getEncoding().equals(Encoding.PCM_SIGNED)
+ || getEncoding().equals(Encoding.PCM_UNSIGNED))
+ && ((getSampleSizeInBits() > 8)
+ || (getSampleSizeInBits() == NOT_SPECIFIED))) {
+ if (isBigEndian()) {
+ sEndian = "big-endian";
+ } else {
+ sEndian = "little-endian";
+ }
+ }
+
+ return sEncoding
+ + sSampleRate
+ + sSampleSizeInBits
+ + sChannels
+ + sFrameSize
+ + sFrameRate
+ + sEndian;
+
+ }
+
+ /**
+ * The Encoding
class names the specific type of data representation
+ * used for an audio stream. The encoding includes aspects of the
+ * sound format other than the number of channels, sample rate, sample size,
+ * frame rate, frame size, and byte order.
+ * AudioSystem
class.
+ * Encoding
class is static, so that all
+ * AudioFormat
objects that have the same encoding will refer
+ * to the same object (rather than different instances of the same class).
+ * This allows matches to be made by checking that two format's encodings
+ * are equal.
+ *
+ * @author Kara Kytle
+ * @since 1.3
+ */
+ public static class Encoding {
+
+
+ // ENCODING DEFINES
+
+ /**
+ * Specifies signed, linear PCM data.
+ */
+ public static final Encoding PCM_SIGNED = new Encoding("PCM_SIGNED");
+
+ /**
+ * Specifies unsigned, linear PCM data.
+ */
+ public static final Encoding PCM_UNSIGNED = new Encoding("PCM_UNSIGNED");
+
+ /**
+ * Specifies u-law encoded data.
+ */
+ public static final Encoding ULAW = new Encoding("ULAW");
+
+ /**
+ * Specifies a-law encoded data.
+ */
+ public static final Encoding ALAW = new Encoding("ALAW");
+
+
+ // INSTANCE VARIABLES
+
+ /**
+ * Encoding name.
+ */
+ private final String name;
+
+
+ // CONSTRUCTOR
+
+ /**
+ * Constructs a new encoding.
+ * @param name the name of the new type of encoding
+ */
+ public Encoding(String name) {
+ this.name = name;
+ }
+
+
+ // METHODS
+
+ /**
+ * Finalizes the equals method
+ */
+ public final boolean equals(Object obj) {
+ if (toString() == null) {
+ return (obj != null) && (obj.toString() == null);
+ }
+ if (obj instanceof Encoding) {
+ return toString().equals(obj.toString());
+ }
+ return false;
+ }
+
+ /**
+ * Finalizes the hashCode method
+ */
+ public final int hashCode() {
+ if (toString() == null) {
+ return 0;
+ }
+ return toString().hashCode();
+ }
+
+ /**
+ * Provides the String
representation of the encoding. This String
is
+ * the same name that was passed to the constructor. For the predefined encodings, the name
+ * is similar to the encoding's variable (field) name. For example, PCM_SIGNED.toString()
returns
+ * the name "pcm_signed".
+ *
+ * @return the encoding name
+ */
+ public final String toString() {
+ return name;
+ }
+
+ } // class Encoding
+}
diff --git a/app/src/main/java/be/tarsos/dsp/io/TarsosDSPAudioInputStream.java b/app/src/main/java/be/tarsos/dsp/io/TarsosDSPAudioInputStream.java
new file mode 100644
index 0000000..70491d2
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/io/TarsosDSPAudioInputStream.java
@@ -0,0 +1,76 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.io;
+
+import java.io.IOException;
+
+/**
+ * Decouples the audio input stream
+ * @author Joren Six
+ */
+public interface TarsosDSPAudioInputStream {
+
+ /**
+ * Skip a number of bytes before reading the remaining bytes.
+ * @param bytesToSkip The number of bytes to skip.
+ * @return The number of bytes skipped.
+ * @throws IOException If the underlying if an input or output error occurs
+ * #see read
+ */
+ long skip(long bytesToSkip) throws IOException;
+
+ /**
+ * Reads up to a specified maximum number of bytes of data from the audio
+ * stream, putting them into the given byte array.
+ * len
does not specify an integral number
+ * of frames, a maximum of len - (len % frameSize)
+ *
bytes will be read.
+ *
+ * @param b the buffer into which the data is read
+ * @param off the offset, from the beginning of array b
, at which
+ * the data will be written
+ * @param len the maximum number of bytes to read
+ * @return the total number of bytes read into the buffer, or -1 if there
+ * is no more data because the end of the stream has been reached
+ * @throws IOException if an input or output error occurs
+ * @see #skip
+ */
+ int read(byte[] b, int off, int len) throws IOException ;
+
+ /**
+ * Closes this audio input stream and releases any system resources associated
+ * with the stream.
+ * @throws IOException if an input or output error occurs
+ */
+ void close() throws IOException;
+
+ /**
+ *
+ * @return The format of the underlying audio
+ */
+ TarsosDSPAudioFormat getFormat();
+
+ long getFrameLength();
+}
diff --git a/app/src/main/java/be/tarsos/dsp/io/UniversalAudioInputStream.java b/app/src/main/java/be/tarsos/dsp/io/UniversalAudioInputStream.java
new file mode 100644
index 0000000..daffa58
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/io/UniversalAudioInputStream.java
@@ -0,0 +1,72 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.io;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class UniversalAudioInputStream implements TarsosDSPAudioInputStream {
+
+ private final InputStream underlyingStream;
+ private final TarsosDSPAudioFormat format;
+
+ public UniversalAudioInputStream(InputStream underlyingInputStream, TarsosDSPAudioFormat format){
+ this.underlyingStream = underlyingInputStream;
+ this.format = format;
+ }
+
+ @Override
+ public long skip(long bytesToSkip) throws IOException {
+ //the skip probably
+ int bytesSkipped = 0;
+ for(int i = 0 ; i < bytesToSkip ; i++){
+ int theByte = underlyingStream.read();
+ if(theByte!=-1){
+ bytesSkipped++;
+ }
+ }
+ return bytesSkipped;
+ }
+
+ @Override
+ public int read(byte[] b, int off, int len) throws IOException {
+ return underlyingStream.read(b, off, len);
+ }
+
+ @Override
+ public void close() throws IOException {
+ underlyingStream.close();
+ }
+
+ @Override
+ public TarsosDSPAudioFormat getFormat() {
+ return format;
+ }
+
+ @Override
+ public long getFrameLength() {
+ return -1;
+ }
+
+}
diff --git a/app/src/main/java/be/tarsos/dsp/io/package-info.java b/app/src/main/java/be/tarsos/dsp/io/package-info.java
new file mode 100644
index 0000000..84113c5
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/io/package-info.java
@@ -0,0 +1,28 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+/**
+ * Contains an abstraction of audio I/O. This is needed to support both the Java runtime and Dalvik/Android.
+ */
+package be.tarsos.dsp.io;
diff --git a/app/src/main/java/be/tarsos/dsp/mfcc/DCT.java b/app/src/main/java/be/tarsos/dsp/mfcc/DCT.java
new file mode 100644
index 0000000..9c301b9
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/mfcc/DCT.java
@@ -0,0 +1,155 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.mfcc;
+
+import java.io.*;
+import java.util.StringTokenizer;
+
+public class DCT {
+
+ int[][] f;
+ int[][] g;
+ int[][] inv;
+
+ static public void main(String[] args) {
+
+ int[][] fm = new int[8][8];
+
+ if ( args.length != 1 ) {
+ System.out.println("usage: java DCT
+ * calls: none
+ * called by: featureExtraction
+ * @param frame Input frame signal
+ * @return Magnitude Spectrum array
+ */
+ public float[] magnitudeSpectrum(float[] frame){
+ float[] magSpectrum = new float[frame.length];
+
+ // calculate FFT for current frame
+
+ fft.forwardTransform(frame);
+
+ // calculate magnitude spectrum
+ for (int k = 0; k < frame.length/2; k++){
+ magSpectrum[frame.length/2+k] = fft.modulus(frame, frame.length/2-1-k);
+ magSpectrum[frame.length/2-1-k] = magSpectrum[frame.length/2+k];
+ }
+
+ return magSpectrum;
+ }
+
+ /**
+ * calculates the FFT bin indices
calls: none
called by:
+ * featureExtraction
+ *
+ */
+
+ public final void calculateFilterBanks() {
+ centerFrequencies = new int[amountOfMelFilters + 2];
+
+ centerFrequencies[0] = Math.round(lowerFilterFreq / sampleRate * samplesPerFrame);
+ centerFrequencies[centerFrequencies.length - 1] = (int) (samplesPerFrame / 2);
+
+ double[] mel = new double[2];
+ mel[0] = freqToMel(lowerFilterFreq);
+ mel[1] = freqToMel(upperFilterFreq);
+
+ float factor = (float)((mel[1] - mel[0]) / (amountOfMelFilters + 1));
+ //Calculates te centerfrequencies.
+ for (int i = 1; i <= amountOfMelFilters; i++) {
+ float fc = (inverseMel(mel[0] + factor * i) / sampleRate) * samplesPerFrame;
+ centerFrequencies[i] = Math.round(fc);
+ }
+
+ }
+
+
+ /**
+ * the output of mel filtering is subjected to a logarithm function (natural logarithm)
+ * calls: none
+ * called by: featureExtraction
+ * @param fbank Output of mel filtering
+ * @return Natural log of the output of mel filtering
+ */
+ public float[] nonLinearTransformation(float[] fbank){
+ float[] f = new float[fbank.length];
+ final float FLOOR = -50;
+
+ for (int i = 0; i < fbank.length; i++){
+ f[i] = (float) Math.log(fbank[i]);
+
+ // check if ln() returns a value less than the floor
+ if (f[i] < FLOOR) f[i] = FLOOR;
+ }
+
+ return f;
+ }
+
+ /**
+ * Calculate the output of the mel filter
calls: none called by:
+ * featureExtraction
+ * @param bin The bins.
+ * @param centerFrequencies The frequency centers.
+ * @return Output of mel filter.
+ */
+ public float[] melFilter(float[] bin, int[] centerFrequencies) {
+ float[] temp = new float[amountOfMelFilters + 2];
+
+ for (int k = 1; k <= amountOfMelFilters; k++) {
+ float num1 = 0, num2 = 0;
+
+ float den = (centerFrequencies[k] - centerFrequencies[k - 1] + 1);
+
+ for (int i = centerFrequencies[k - 1]; i <= centerFrequencies[k]; i++) {
+ num1 += bin[i] * (i - centerFrequencies[k - 1] + 1);
+ }
+ num1 /= den;
+
+ den = (centerFrequencies[k + 1] - centerFrequencies[k] + 1);
+
+ for (int i = centerFrequencies[k] + 1; i <= centerFrequencies[k + 1]; i++) {
+ num2 += bin[i] * (1 - ((i - centerFrequencies[k]) / den));
+ }
+
+ temp[k] = num1 + num2;
+ }
+
+ float[] fbank = new float[amountOfMelFilters];
+
+ System.arraycopy(temp, 1, fbank, 0, amountOfMelFilters);
+
+ return fbank;
+ }
+
+
+ /**
+ * Cepstral coefficients are calculated from the output of the Non-linear Transformation method
+ * calls: none
+ * called by: featureExtraction
+ * @param f Output of the Non-linear Transformation method
+ * @return Cepstral Coefficients
+ */
+ public float[] cepCoefficients(float[] f){
+ float[] cepc = new float[amountOfCepstrumCoef];
+
+ for (int i = 0; i < cepc.length; i++){
+ for (int j = 0; j < f.length; j++){
+ cepc[i] += f[j] * Math.cos(Math.PI * i / f.length * (j + 0.5));
+ }
+ }
+
+ return cepc;
+ }
+
+// /**
+// * calculates center frequency
+// * calls: none
+// * called by: featureExtraction
+// * @param i Index of mel filters
+// * @return Center Frequency
+// */
+// private static float centerFreq(int i,float samplingRate){
+// double mel[] = new double[2];
+// mel[0] = freqToMel(lowerFilterFreq);
+// mel[1] = freqToMel(samplingRate / 2);
+//
+// // take inverse mel of:
+// double temp = mel[0] + ((mel[1] - mel[0]) / (amountOfMelFilters + 1)) * i;
+// return inverseMel(temp);
+// }
+
+ /**
+ * convert frequency to mel-frequency
+ * calls: none
+ * called by: featureExtraction
+ * @param freq Frequency
+ * @return Mel-Frequency
+ */
+ protected static float freqToMel(float freq){
+ return (float) (2595 * log10(1 + freq / 700));
+ }
+
+ /**
+ * calculates the inverse of Mel Frequency
+ * calls: none
+ * called by: featureExtraction
+ */
+ private static float inverseMel(double x) {
+ return (float) (700 * (Math.pow(10, x / 2595) - 1));
+ }
+
+ /**
+ * calculates logarithm with base 10
+ * calls: none
+ * called by: featureExtraction
+ * @param value Number to take the log of
+ * @return base 10 logarithm of the input values
+ */
+ protected static float log10(float value){
+ return (float) (Math.log(value) / Math.log(10));
+ }
+
+ public float[] getMFCC() {
+ return mfcc.clone();
+ }
+
+ public int[] getCenterFrequencies() {
+ return centerFrequencies;
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/mfcc/package-info.java b/app/src/main/java/be/tarsos/dsp/mfcc/package-info.java
new file mode 100644
index 0000000..7246def
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/mfcc/package-info.java
@@ -0,0 +1,28 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+/**
+ * Contains an MFCC implementation.
+ */
+package be.tarsos.dsp.mfcc;
diff --git a/app/src/main/java/be/tarsos/dsp/onsets/BeatRootSpectralFluxOnsetDetector.java b/app/src/main/java/be/tarsos/dsp/onsets/BeatRootSpectralFluxOnsetDetector.java
new file mode 100644
index 0000000..0cd96cb
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/onsets/BeatRootSpectralFluxOnsetDetector.java
@@ -0,0 +1,279 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.onsets;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+import be.tarsos.dsp.AudioDispatcher;
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.AudioProcessor;
+import be.tarsos.dsp.beatroot.Peaks;
+import be.tarsos.dsp.util.fft.FFT;
+import be.tarsos.dsp.util.fft.ScaledHammingWindow;
+
+/**
+ * freqMap
. Note that the length of
+ * the array is greater, because its size is not known at creation time. */
+ private int freqMapSize;
+
+ /** The magnitude spectrum of the most recent frame.
+ * Used for calculating the spectral flux. */
+ private float[] prevFrame;
+
+ /** The magnitude spectrum of the current frame. */
+ private final double[] newFrame;
+
+ /** The magnitude spectra of all frames, used for plotting the spectrogram. */
+ private final double[][] frames;
+
+ /** The RMS energy of all frames. */
+ private final double[] energy;
+
+ /** Spacing of audio frames in samples (see hopTime
) */
+ protected int hopSize;
+
+ /** The size of an FFT frame in samples (see fftTime
) */
+ protected int fftSize;
+
+ /** Total number of audio frames if known, or -1 for live or compressed input. */
+ private final int totalFrames;
+
+ /** RMS frame energy below this value results in the frame being set to zero,
+ * so that normalization does not have undesired side-effects. */
+ public static double silenceThreshold = 0.0004;
+
+ /** For dynamic range compression, this value is added to the log magnitude
+ * in each frequency bin and any remaining negative values are then set to zero.
+ */
+ public static double rangeThreshold = 10;
+
+ /** Determines method of normalization. Values can be:
+ *
+ */
+ public static int normaliseMode = 2;
+
+ /** Ratio between rate of sampling the signal energy (for the amplitude envelope) and the hop size */
+ public static int energyOversampleFactor = 2;
+
+ private OnsetHandler handler;
+
+ private final double hopTime;
+
+ private final FFT fft;
+
+ /**
+ * Create anew onset detector
+ * @param d the dispatcher
+ * @param fftSize The size of the fft
+ * @param hopSize the hop size of audio blocks.
+ */
+ public BeatRootSpectralFluxOnsetDetector(AudioDispatcher d,int fftSize, int hopSize){
+
+ this.hopSize = hopSize;
+ this.hopTime = hopSize/d.getFormat().getSampleRate();
+ this.fftSize = fftSize;
+
+ System.err.println("Please use the ComplexOnset detector: BeatRootSpectralFluxOnsetDetector does currenlty not support streaming");
+ //no overlap
+ //FIXME:
+ int durationInFrames = -1000;
+ totalFrames = (int)(durationInFrames / hopSize) + 4;
+ energy = new double[totalFrames*energyOversampleFactor];
+ spectralFlux = new double[totalFrames];
+
+ reBuffer = new float[fftSize/2];
+ imBuffer = new float[fftSize/2];
+ prevFrame = new float[fftSize/2];
+
+ makeFreqMap(fftSize, d.getFormat().getSampleRate());
+
+ newFrame = new double[freqMapSize];
+ frames = new double[totalFrames][freqMapSize];
+ handler = new PrintOnsetHandler();
+ fft = new FFT(fftSize,new ScaledHammingWindow());
+ }
+
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+ frameRMS = audioEvent.getRMS()/2.0;
+
+ float[] audioBuffer = audioEvent.getFloatBuffer().clone();
+
+ Arrays.fill(imBuffer, 0);
+ fft.powerPhaseFFTBeatRootOnset(audioBuffer, reBuffer, imBuffer);
+ Arrays.fill(newFrame, 0);
+
+ double flux = 0;
+ for (int i = 0; i < fftSize/2; i++) {
+ if (reBuffer[i] > prevFrame[i])
+ flux += reBuffer[i] - prevFrame[i];
+ newFrame[freqMap[i]] += reBuffer[i];
+ }
+ spectralFlux[frameCount] = flux;
+ if (freqMapSize >= 0) System.arraycopy(newFrame, 0, frames[frameCount], 0, freqMapSize);
+
+ int sz = (fftSize - hopSize) / energyOversampleFactor;
+ int index = hopSize;
+ for (int j = 0; j < energyOversampleFactor; j++) {
+ double newEnergy = 0;
+ for (int i = 0; i < sz; i++) {
+ newEnergy += audioBuffer[index] * audioBuffer[index];
+ if (++index == fftSize)
+ index = 0;
+ }
+ energy[frameCount * energyOversampleFactor + j] =
+ newEnergy / sz <= 1e-6? 0: Math.log(newEnergy / sz) + 13.816;
+ }
+ double decay = frameCount >= 200? 0.99:
+ (frameCount < 100? 0: (frameCount - 100) / 100.0);
+ if (ltAverage == 0)
+ ltAverage = frameRMS;
+ else
+ ltAverage = ltAverage * decay + frameRMS * (1.0 - decay);
+ if (frameRMS <= silenceThreshold)
+ for (int i = 0; i < freqMapSize; i++)
+ frames[frameCount][i] = 0;
+ else {
+ if (normaliseMode == 1)
+ for (int i = 0; i < freqMapSize; i++)
+ frames[frameCount][i] /= frameRMS;
+ else if (normaliseMode == 2)
+ for (int i = 0; i < freqMapSize; i++)
+ frames[frameCount][i] /= ltAverage;
+ for (int i = 0; i < freqMapSize; i++) {
+ frames[frameCount][i] = Math.log(frames[frameCount][i]) + rangeThreshold;
+ if (frames[frameCount][i] < 0)
+ frames[frameCount][i] = 0;
+ }
+ }
+
+ float[] tmp = prevFrame;
+ prevFrame = reBuffer;
+ reBuffer = tmp;
+ frameCount++;
+ return true;
+ }
+
+ /**
+ * Creates a map of FFT frequency bins to comparison bins.
+ * Where the spacing of FFT bins is less than 0.5 semitones, the mapping is
+ * one to one. Where the spacing is greater than 0.5 semitones, the FFT
+ * energy is mapped into semitone-wide bins. No scaling is performed; that
+ * is the energy is summed into the comparison bins. See also
+ * processFrame()
+ */
+ protected void makeFreqMap(int fftSize, float sampleRate) {
+ freqMap = new int[fftSize/2+1];
+ double binWidth = sampleRate / fftSize;
+ int crossoverBin = (int)(2 / (Math.pow(2, 1/12.0) - 1));
+ int crossoverMidi = (int)Math.round(Math.log(crossoverBin*binWidth/440)/
+ Math.log(2) * 12 + 69);
+ // freq = 440 * Math.pow(2, (midi-69)/12.0) / binWidth;
+ int i = 0;
+ while (i <= crossoverBin)
+ freqMap[i++] = i;
+ while (i <= fftSize/2) {
+ double midi = Math.log(i*binWidth/440) / Math.log(2) * 12 + 69;
+ if (midi > 127)
+ midi = 127;
+ freqMap[i++] = crossoverBin + (int)Math.round(midi) - crossoverMidi;
+ }
+ freqMapSize = freqMap[i-1] + 1;
+ } // makeFreqMap()
+
+
+ private void findOnsets(double p1, double p2){
+ LinkedList
+ * Centre for Digital Music, Queen Mary, University of London.
+ * Copyright 2006 Chris Cannam.
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+ * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Except as contained in this notice, the names of the Centre for
+ * Digital Music; Queen Mary, University of London; and Chris Cannam
+ * shall not be used in advertising or otherwise to promote the sale,
+ * use or other dealings in this Software without prior written
+ * authorization.
+ *
+ *
+ *
+ *
+ * @author Joren Six
+ * @author Chris Cannam
+ * @see "Drum Source Separation using Percussive Feature Detection and Spectral Modulation"
+ * @see VAMP plugin example
+ */
+public class PercussionOnsetDetector implements AudioProcessor, OnsetDetector {
+
+ public static final double DEFAULT_THRESHOLD = 8;
+
+ public static final double DEFAULT_SENSITIVITY = 20;
+
+ private final FFT fft;
+
+ private final float[] priorMagnitudes;
+ private final float[] currentMagnitudes;
+
+ private float dfMinus1, dfMinus2;
+
+ private OnsetHandler handler;
+
+ private final float sampleRate;//samples per second (Hz)
+ private long processedSamples;//in samples
+
+ /**
+ * Sensitivity of peak detector applied to broadband detection function (%).
+ * In [0-100].
+ */
+ private final double sensitivity;
+
+ /**
+ * Energy rise within a frequency bin necessary to count toward broadband
+ * total (dB). In [0-20].
+ *
+ */
+ private final double threshold;
+
+ /**
+ * Create a new percussion onset detector. With a default sensitivity and threshold.
+ *
+ * @param sampleRate
+ * The sample rate in Hz (used to calculate timestamps)
+ * @param bufferSize
+ * The size of the buffer in samples.
+ * @param bufferOverlap
+ * The overlap of buffers in samples.
+ * @param handler
+ * An interface implementor to handle percussion onset events.
+ */
+ public PercussionOnsetDetector(float sampleRate, int bufferSize,
+ int bufferOverlap, OnsetHandler handler) {
+ this(sampleRate, bufferSize, handler,
+ DEFAULT_SENSITIVITY, DEFAULT_THRESHOLD);
+ }
+
+ /**
+ * Create a new percussion onset detector.
+ *
+ * @param sampleRate
+ * The sample rate in Hz (used to calculate timestamps)
+ * @param bufferSize
+ * The size of the buffer in samples.
+ * @param handler
+ * An interface implementor to handle percussion onset events.
+ * @param sensitivity
+ * Sensitivity of the peak detector applied to broadband
+ * detection function (%). In [0-100].
+ * @param threshold
+ * Energy rise within a frequency bin necessary to count toward
+ * broadband total (dB). In [0-20].
+ */
+ public PercussionOnsetDetector(float sampleRate, int bufferSize, OnsetHandler handler, double sensitivity, double threshold) {
+ fft = new FFT(bufferSize / 2);
+ this.threshold = threshold;
+ this.sensitivity = sensitivity;
+ priorMagnitudes = new float[bufferSize / 2];
+ currentMagnitudes = new float[bufferSize / 2];
+ this.handler = handler;
+ this.sampleRate = sampleRate;
+
+ }
+
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+ float[] audioFloatBuffer = audioEvent.getFloatBuffer();
+ this.processedSamples += audioFloatBuffer.length;
+ this.processedSamples -= audioEvent.getOverlap();
+
+ fft.forwardTransform(audioFloatBuffer);
+ fft.modulus(audioFloatBuffer, currentMagnitudes);
+ int binsOverThreshold = 0;
+ for (int i = 0; i < currentMagnitudes.length; i++) {
+ if (priorMagnitudes[i] > 0.f) {
+ double diff = 10 * Math.log10(currentMagnitudes[i]
+ / priorMagnitudes[i]);
+ if (diff >= threshold) {
+ binsOverThreshold++;
+ }
+ }
+ priorMagnitudes[i] = currentMagnitudes[i];
+ }
+
+ if (dfMinus2 < dfMinus1
+ && dfMinus1 >= binsOverThreshold
+ && dfMinus1 > ((100 - sensitivity) * audioFloatBuffer.length) / 200) {
+ float timeStamp = processedSamples / sampleRate;
+ handler.handleOnset(timeStamp,-1);
+ }
+
+ dfMinus2 = dfMinus1;
+ dfMinus1 = binsOverThreshold;
+
+ return true;
+ }
+
+ @Override
+ public void processingFinished() {
+ }
+
+ @Override
+ public void setHandler(OnsetHandler handler) {
+ this.handler = handler;
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/onsets/PrintOnsetHandler.java b/app/src/main/java/be/tarsos/dsp/onsets/PrintOnsetHandler.java
new file mode 100644
index 0000000..165d99b
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/onsets/PrintOnsetHandler.java
@@ -0,0 +1,31 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.onsets;
+
+public class PrintOnsetHandler implements OnsetHandler{
+ @Override
+ public void handleOnset(double time, double salience) {
+ System.out.printf("%.4f;%.4f%n", time,salience);
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/onsets/package-info.java b/app/src/main/java/be/tarsos/dsp/onsets/package-info.java
new file mode 100644
index 0000000..736ca4a
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/onsets/package-info.java
@@ -0,0 +1,28 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+/**
+ * Contains various onset detection methods.
+ */
+package be.tarsos.dsp.onsets;
diff --git a/app/src/main/java/be/tarsos/dsp/package-info.java b/app/src/main/java/be/tarsos/dsp/package-info.java
new file mode 100644
index 0000000..36310ca
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/package-info.java
@@ -0,0 +1,28 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+/**
+ * Contains classes to handle sampled sound.
+ */
+package be.tarsos.dsp;
diff --git a/app/src/main/java/be/tarsos/dsp/pitch/AMDF.java b/app/src/main/java/be/tarsos/dsp/pitch/AMDF.java
new file mode 100644
index 0000000..5bfeea6
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/pitch/AMDF.java
@@ -0,0 +1,172 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.pitch;
+
+/**
+ *
+ *
+ * yinBuffer[0] == yinBuffer[1] = 1
+ *
+ */
+ private void cumulativeMeanNormalizedDifference() {
+ int tau;
+ yinBuffer[0] = 1;
+ float runningSum = 0;
+ for (tau = 1; tau < yinBuffer.length; tau++) {
+ runningSum += yinBuffer[tau];
+ yinBuffer[tau] *= tau / runningSum;
+ }
+ }
+
+ /**
+ * Implements step 4 of the AUBIO_YIN paper.
+ */
+ private int absoluteThreshold() {
+ // Uses another loop construct
+ // than the AUBIO implementation
+ int tau;
+ // first two positions in yinBuffer are always 1
+ // So start at the third (index 2)
+ for (tau = 2; tau < yinBuffer.length; tau++) {
+ if (yinBuffer[tau] < threshold) {
+ while (tau + 1 < yinBuffer.length && yinBuffer[tau + 1] < yinBuffer[tau]) {
+ tau++;
+ }
+ // found tau, exit loop and return
+ // store the probability
+ // From the YIN paper: The threshold determines the list of
+ // candidates admitted to the set, and can be interpreted as the
+ // proportion of aperiodic power tolerated
+ // within a periodic signal.
+ //
+ // Since we want the periodicity and and not aperiodicity:
+ // periodicity = 1 - aperiodicity
+ result.setProbability(1 - yinBuffer[tau]);
+ break;
+ }
+ }
+
+
+ // if no pitch found, tau => -1
+ if (tau == yinBuffer.length || yinBuffer[tau] >= threshold || result.getProbability() > 1.0) {
+ tau = -1;
+ result.setProbability(0);
+ result.setPitched(false);
+ } else {
+ result.setPitched(true);
+ }
+
+ return tau;
+ }
+
+ /**
+ * Implements step 5 of the AUBIO_YIN paper. It refines the estimated tau
+ * value using parabolic interpolation. This is needed to detect higher
+ * frequencies more precisely. See http://fizyka.umk.pl/nrbook/c10-2.pdf and
+ * for more background
+ * http://fedc.wiwi.hu-berlin.de/xplore/tutorials/xegbohtmlnode62.html
+ *
+ * @param tauEstimate
+ * The estimated tau value.
+ * @return A better, more precise tau value.
+ */
+ private float parabolicInterpolation(final int tauEstimate) {
+ final float betterTau;
+ final int x0;
+ final int x2;
+
+ if (tauEstimate < 1) {
+ x0 = tauEstimate;
+ } else {
+ x0 = tauEstimate - 1;
+ }
+ if (tauEstimate + 1 < yinBuffer.length) {
+ x2 = tauEstimate + 1;
+ } else {
+ x2 = tauEstimate;
+ }
+ if (x0 == tauEstimate) {
+ if (yinBuffer[tauEstimate] <= yinBuffer[x2]) {
+ betterTau = tauEstimate;
+ } else {
+ betterTau = x2;
+ }
+ } else if (x2 == tauEstimate) {
+ if (yinBuffer[tauEstimate] <= yinBuffer[x0]) {
+ betterTau = tauEstimate;
+ } else {
+ betterTau = x0;
+ }
+ } else {
+ float s0, s1, s2;
+ s0 = yinBuffer[x0];
+ s1 = yinBuffer[tauEstimate];
+ s2 = yinBuffer[x2];
+ // fixed AUBIO implementation, thanks to Karl Helgason:
+ // (2.0f * s1 - s2 - s0) was incorrectly multiplied with -1
+ betterTau = tauEstimate + (s2 - s0) / (2 * (2 * s1 - s2 - s0));
+ }
+ return betterTau;
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/pitch/GeneralizedGoertzel.java b/app/src/main/java/be/tarsos/dsp/pitch/GeneralizedGoertzel.java
new file mode 100644
index 0000000..166da70
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/pitch/GeneralizedGoertzel.java
@@ -0,0 +1,113 @@
+package be.tarsos.dsp.pitch;
+
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.AudioProcessor;
+import be.tarsos.dsp.pitch.Goertzel.FrequenciesDetectedHandler;
+import be.tarsos.dsp.util.Complex;
+import be.tarsos.dsp.util.fft.HammingWindow;
+import be.tarsos.dsp.util.fft.WindowFunction;
+
+/**
+ * See "Goertzel algorithm generalized to non-integer multiples of fundamental frequency" by Petr Sysel and Pavel Rajmic
+ *
+ *
+ * @author Joren Six
+ *
+ */
+public class GeneralizedGoertzel implements AudioProcessor{
+
+ /**
+ * A list of frequencies to detect.
+ */
+ private final double[] frequenciesToDetect;
+
+ private final double[] indvec;
+
+ /**
+ * Cached cosine calculations for each frequency to detect.
+ */
+ private final double[] precalculatedCosines;
+ /**
+ * Cached wnk calculations for each frequency to detect.
+ */
+ private final double[] precalculatedWnk;
+ /**
+ * A calculated power for each frequency to detect. This array is reused for
+ * performance reasons.
+ */
+ private final double[] calculatedPowers;
+ private final Complex[] calculatedComplex;
+
+ private final FrequenciesDetectedHandler handler;
+
+
+ /**
+ * Create a new Generalized Goertzel processor.
+ * @param audioSampleRate The sample rate of the audio in Hz.
+ * @param bufferSize the size of the buffer.
+ * @param frequencies The list of frequencies to detect (in Hz).
+ * @param handler The handler used to handle the detected frequencies.
+ */
+ public GeneralizedGoertzel(final float audioSampleRate, final int bufferSize,
+ double[] frequencies, FrequenciesDetectedHandler handler){
+ frequenciesToDetect = frequencies;
+
+ indvec = new double[frequenciesToDetect.length];
+ for (int j = 0; j < frequenciesToDetect.length; j++) {
+ indvec[j] = frequenciesToDetect[j]/(audioSampleRate/(float)bufferSize);
+ }
+
+
+ precalculatedCosines = new double[frequencies.length];
+ precalculatedWnk = new double[frequencies.length];
+ this.handler = handler;
+
+ calculatedPowers = new double[frequencies.length];
+ calculatedComplex = new Complex[frequencies.length];
+
+ for (int i = 0; i < frequenciesToDetect.length; i++) {
+ precalculatedCosines[i] = 2 * Math.cos(2 * Math.PI
+ * frequenciesToDetect[i] / audioSampleRate);
+ precalculatedWnk[i] = Math.exp(-2 * Math.PI
+ * frequenciesToDetect[i] / audioSampleRate);
+ }
+
+ }
+
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+
+ float[] x = audioEvent.getFloatBuffer();
+ WindowFunction f = new HammingWindow();
+ f.apply(x);
+ for (int j = 0; j < frequenciesToDetect.length; j++) {
+ double pik_term = 2 * Math.PI * indvec[j]/(float) audioEvent.getBufferSize();
+ double cos_pik_term2 = Math.cos(pik_term) * 2;
+ Complex cc = new Complex(0,-1*pik_term).exp();
+ double s0=0;
+ double s1=0;
+ double s2=0;
+
+ for(int i = 0 ; i < audioEvent.getBufferSize() ; i++ ){
+ s0 = x[i]+cos_pik_term2*s1-s2;
+ s2=s1;
+ s1=s0;
+ }
+ s0 = cos_pik_term2 * s1 - s2;
+ calculatedComplex[j] = cc.times(new Complex(-s1,0)).plus(new Complex(s0,0));
+ calculatedPowers[j] = calculatedComplex[j].mod();
+ }
+
+ handler.handleDetectedFrequencies(audioEvent.getTimeStamp(),frequenciesToDetect.clone(), calculatedPowers.clone(),
+ frequenciesToDetect.clone(), calculatedPowers.clone());
+
+ return true;
+ }
+
+
+ @Override
+ public void processingFinished() {
+
+ }
+
+}
diff --git a/app/src/main/java/be/tarsos/dsp/pitch/Goertzel.java b/app/src/main/java/be/tarsos/dsp/pitch/Goertzel.java
new file mode 100644
index 0000000..cdcc49b
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/pitch/Goertzel.java
@@ -0,0 +1,159 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+package be.tarsos.dsp.pitch;
+
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.AudioProcessor;
+
+/**
+ * Contains an implementation of the Goertzel algorithm. It can be used to
+ * detect if one or more predefined frequencies are present in a signal. E.g. to
+ * do DTMF decoding.
+ *
+ * @author Joren Six
+ */
+public class Goertzel implements AudioProcessor {
+
+ /**
+ * If the power in dB is higher than this threshold, the frequency is
+ * present in the signal.
+ */
+ private static final double POWER_THRESHOLD = 35;// in dB
+
+ /**
+ * A list of frequencies to detect.
+ */
+ private final double[] frequenciesToDetect;
+ /**
+ * Cached cosine calculations for each frequency to detect.
+ */
+ private final double[] precalculatedCosines;
+ /**
+ * Cached wnk calculations for each frequency to detect.
+ */
+ private final double[] precalculatedWnk;
+ /**
+ * A calculated power for each frequency to detect. This array is reused for
+ * performance reasons.
+ */
+ private final double[] calculatedPowers;
+
+ private final FrequenciesDetectedHandler handler;
+
+ /**
+ * Create a new Generalized Goertzel processor.
+ * @param audioSampleRate The sample rate of the audio in Hz.
+ * @param bufferSize the size of the buffer.
+ * @param frequencies The list of frequencies to detect (in Hz).
+ * @param handler The handler used to handle the detected frequencies.
+ */
+ public Goertzel(final float audioSampleRate, final int bufferSize,
+ double[] frequencies, FrequenciesDetectedHandler handler) {
+
+ frequenciesToDetect = frequencies;
+ precalculatedCosines = new double[frequencies.length];
+ precalculatedWnk = new double[frequencies.length];
+ this.handler = handler;
+
+ calculatedPowers = new double[frequencies.length];
+
+ for (int i = 0; i < frequenciesToDetect.length; i++) {
+ precalculatedCosines[i] = 2 * Math.cos(2 * Math.PI
+ * frequenciesToDetect[i] / audioSampleRate);
+ precalculatedWnk[i] = Math.exp(-2 * Math.PI
+ * frequenciesToDetect[i] / audioSampleRate);
+ }
+ }
+
+ /**
+ * An interface used to react on detected frequencies.
+ *
+ * @author Joren Six
+ */
+ public interface FrequenciesDetectedHandler {
+ /**
+ * React on detected frequencies.
+ *
+ * @param timestamp
+ * A timestamp in seconds
+ * @param frequencies
+ * A list of detected frequencies.
+ * @param powers
+ * A list of powers of the detected frequencies.
+ * @param allFrequencies
+ * A list of all frequencies that were checked.
+ * @param allPowers
+ * A list of powers of all frequencies that were checked.
+ */
+ void handleDetectedFrequencies(final double timestamp,final double[] frequencies,
+ final double[] powers, final double[] allFrequencies,
+ final double[] allPowers);
+ }
+
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+ float[] audioFloatBuffer = audioEvent.getFloatBuffer();
+ double skn0, skn1, skn2;
+ int numberOfDetectedFrequencies = 0;
+ for (int j = 0; j < frequenciesToDetect.length; j++) {
+ skn0 = skn1 = skn2 = 0;
+ for (int i = 0; i < audioFloatBuffer.length; i++) {
+ skn2 = skn1;
+ skn1 = skn0;
+ skn0 = precalculatedCosines[j] * skn1 - skn2
+ + audioFloatBuffer[i];
+ }
+ double wnk = precalculatedWnk[j];
+ calculatedPowers[j] = 20 * Math.log10(Math.abs(skn0 - wnk * skn1));
+ if (calculatedPowers[j] > POWER_THRESHOLD) {
+ numberOfDetectedFrequencies++;
+ }
+ }
+
+ if (numberOfDetectedFrequencies > 0) {
+ double[] frequencies = new double[numberOfDetectedFrequencies];
+ double[] powers = new double[numberOfDetectedFrequencies];
+ int index = 0;
+ for (int j = 0; j < frequenciesToDetect.length; j++) {
+ if (calculatedPowers[j] > POWER_THRESHOLD) {
+ frequencies[index] = frequenciesToDetect[j];
+ powers[index] = calculatedPowers[j];
+ index++;
+ }
+ }
+ handler.handleDetectedFrequencies(audioEvent.getTimeStamp(),frequencies, powers,
+ frequenciesToDetect.clone(), calculatedPowers.clone());
+ }
+
+ return true;
+ }
+
+
+
+ @Override
+ public void processingFinished() {
+ }
+
+}
diff --git a/app/src/main/java/be/tarsos/dsp/pitch/McLeodPitchMethod.java b/app/src/main/java/be/tarsos/dsp/pitch/McLeodPitchMethod.java
new file mode 100644
index 0000000..ddc6205
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/pitch/McLeodPitchMethod.java
@@ -0,0 +1,397 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+/**
+ */
+package be.tarsos.dsp.pitch;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ *
+ * O(Ww)
with W the window size in samples and w
+ * the desired number of ACF coefficients. The implementation can be optimized
+ * to O((W+w)log(W+w))
by using an Fast Fourier Transform
+ * to calculate the Auto-Correlation Function. But I am still afraid of the
+ * dark magic of the FFT and clinging to the familiar, friendly, laggard time
+ * domain.
+ * b - a = 1
the formula for parabolic interpolation
+ * can be simplified a lot.
+ *
+ * nsdf(x)
+ * ^
+ * |
+ * f(x) |------ ^
+ * f(b) | / |\
+ * f(a) | / | \
+ * | / | \
+ * | / | \
+ * f(c) | / | \
+ * |_____________________> x
+ * a x b c
+ *
+ *
+ * @param tau
+ * The delay tau, b value in the drawing is the tau value.
+ */
+ private void parabolicInterpolation(final int tau) {
+ final float nsdfa = nsdf[tau - 1];
+ final float nsdfb = nsdf[tau];
+ final float nsdfc = nsdf[tau + 1];
+ final float bValue = tau;
+ final float bottom = nsdfc + nsdfa - 2 * nsdfb;
+ if (bottom == 0.0) {
+ turningPointX = bValue;
+ turningPointY = nsdfb;
+ } else {
+ final float delta = nsdfa - nsdfc;
+ turningPointX = bValue + delta / (2 * bottom);
+ turningPointY = nsdfb - delta * delta / (8 * bottom);
+ }
+ }
+
+ /**
+ * general/mytransforms.cpp
.
+ *
+ * f(x)
+ * ^
+ * |
+ * 1| +
+ * | \ + /\ + /\
+ * 0| _\____/\____/__\/\__/\____/_______> x
+ * | \ / \ / \/ \ /
+ * -1| \/ \/ \/
+ * |
+ *
+ *
+ * @param nsdf
+ * The array to look for maximum values in. It should contain
+ * values between -1 and 1
+ * @author Phillip McLeod
+ */
+ private void peakPicking() {
+
+ int pos = 0;
+ int curMaxPos = 0;
+
+ // find the first negative zero crossing
+ while (pos < (nsdf.length - 1) / 3 && nsdf[pos] > 0) {
+ pos++;
+ }
+
+ // loop over all the values below zero
+ while (pos < nsdf.length - 1 && nsdf[pos] <= 0.0) {
+ pos++;
+ }
+
+ // can happen if output[0] is NAN
+ if (pos == 0) {
+ pos = 1;
+ }
+
+ while (pos < nsdf.length - 1) {
+ assert nsdf[pos] >= 0;
+ if (nsdf[pos] > nsdf[pos - 1] && nsdf[pos] >= nsdf[pos + 1]) {
+ if (curMaxPos == 0) {
+ // the first max (between zero crossings)
+ curMaxPos = pos;
+ } else if (nsdf[pos] > nsdf[curMaxPos]) {
+ // a higher max (between the zero crossings)
+ curMaxPos = pos;
+ }
+ }
+ pos++;
+ // a negative zero crossing
+ if (pos < nsdf.length - 1 && nsdf[pos] <= 0) {
+ // if there was a maximum add it to the list of maxima
+ if (curMaxPos > 0) {
+ maxPositions.add(curMaxPos);
+ curMaxPos = 0; // clear the maximum position, so we start
+ // looking for a new ones
+ }
+ while (pos < nsdf.length - 1 && nsdf[pos] <= 0.0f) {
+ pos++; // loop over all the values below zero
+ }
+ }
+ }
+ if (curMaxPos > 0) { // if there was a maximum in the last part
+ maxPositions.add(curMaxPos); // add it to the vector of maxima
+ }
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/pitch/PitchDetectionHandler.java b/app/src/main/java/be/tarsos/dsp/pitch/PitchDetectionHandler.java
new file mode 100644
index 0000000..87f0826
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/pitch/PitchDetectionHandler.java
@@ -0,0 +1,41 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.pitch;
+
+import be.tarsos.dsp.AudioEvent;
+
+/**
+ * An interface to handle detected pitch.
+ *
+ * @author Joren Six
+ */
+public interface PitchDetectionHandler {
+ /**
+ * Handle a detected pitch.
+ * @param pitchDetectionResult
+ * @param audioEvent
+ *
+ */
+ void handlePitch(PitchDetectionResult pitchDetectionResult,AudioEvent audioEvent);
+}
diff --git a/app/src/main/java/be/tarsos/dsp/pitch/PitchDetectionResult.java b/app/src/main/java/be/tarsos/dsp/pitch/PitchDetectionResult.java
new file mode 100644
index 0000000..6978407
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/pitch/PitchDetectionResult.java
@@ -0,0 +1,127 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.pitch;
+
+
+/**
+ * A class with information about the result of a pitch detection on a block of
+ * audio.
+ *
+ * It contains:
+ *
+ *
+ *
+ *
+ * The separate pitched or unpitched boolean can coexist with a defined pitch.
+ * E.g. if the algorithm detects 220Hz in a noisy signal it may respond with
+ * 220Hz "unpitched".
+ *
+ *
+ *
+ * yinBuffer[0] == yinBuffer[1] = 1
+ *
+ */
+ private void cumulativeMeanNormalizedDifference() {
+ int tau;
+ yinBuffer[0] = 1;
+ float runningSum = 0;
+ for (tau = 1; tau < yinBuffer.length; tau++) {
+ runningSum += yinBuffer[tau];
+ yinBuffer[tau] *= tau / runningSum;
+ }
+ }
+
+ /**
+ * Implements step 4 of the AUBIO_YIN paper.
+ */
+ private int absoluteThreshold() {
+ // Uses another loop construct
+ // than the AUBIO implementation
+ int tau;
+ // first two positions in yinBuffer are always 1
+ // So start at the third (index 2)
+ for (tau = 2; tau < yinBuffer.length; tau++) {
+ if (yinBuffer[tau] < threshold) {
+ while (tau + 1 < yinBuffer.length && yinBuffer[tau + 1] < yinBuffer[tau]) {
+ tau++;
+ }
+ // found tau, exit loop and return
+ // store the probability
+ // From the YIN paper: The threshold determines the list of
+ // candidates admitted to the set, and can be interpreted as the
+ // proportion of aperiodic power tolerated
+ // within a periodic signal.
+ //
+ // Since we want the periodicity and and not aperiodicity:
+ // periodicity = 1 - aperiodicity
+ result.setProbability(1 - yinBuffer[tau]);
+ break;
+ }
+ }
+
+
+ // if no pitch found, tau => -1
+ if (tau == yinBuffer.length || yinBuffer[tau] >= threshold) {
+ tau = -1;
+ result.setProbability(0);
+ result.setPitched(false);
+ } else {
+ result.setPitched(true);
+ }
+
+ return tau;
+ }
+
+ /**
+ * Implements step 5 of the AUBIO_YIN paper. It refines the estimated tau
+ * value using parabolic interpolation. This is needed to detect higher
+ * frequencies more precisely. See http://fizyka.umk.pl/nrbook/c10-2.pdf and
+ * for more background
+ * http://fedc.wiwi.hu-berlin.de/xplore/tutorials/xegbohtmlnode62.html
+ *
+ * @param tauEstimate
+ * The estimated tau value.
+ * @return A better, more precise tau value.
+ */
+ private float parabolicInterpolation(final int tauEstimate) {
+ final float betterTau;
+ final int x0;
+ final int x2;
+
+ if (tauEstimate < 1) {
+ x0 = tauEstimate;
+ } else {
+ x0 = tauEstimate - 1;
+ }
+ if (tauEstimate + 1 < yinBuffer.length) {
+ x2 = tauEstimate + 1;
+ } else {
+ x2 = tauEstimate;
+ }
+ if (x0 == tauEstimate) {
+ if (yinBuffer[tauEstimate] <= yinBuffer[x2]) {
+ betterTau = tauEstimate;
+ } else {
+ betterTau = x2;
+ }
+ } else if (x2 == tauEstimate) {
+ if (yinBuffer[tauEstimate] <= yinBuffer[x0]) {
+ betterTau = tauEstimate;
+ } else {
+ betterTau = x0;
+ }
+ } else {
+ float s0, s1, s2;
+ s0 = yinBuffer[x0];
+ s1 = yinBuffer[tauEstimate];
+ s2 = yinBuffer[x2];
+ // fixed AUBIO implementation, thanks to Karl Helgason:
+ // (2.0f * s1 - s2 - s0) was incorrectly multiplied with -1
+ betterTau = tauEstimate + (s2 - s0) / (2 * (2 * s1 - s2 - s0));
+ }
+ return betterTau;
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/pitch/package-info.java b/app/src/main/java/be/tarsos/dsp/pitch/package-info.java
new file mode 100644
index 0000000..c93e39d
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/pitch/package-info.java
@@ -0,0 +1,28 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+/**
+ * Signal processing methods for pitch estimation.
+ */
+package be.tarsos.dsp.pitch;
diff --git a/app/src/main/java/be/tarsos/dsp/resample/FilterKit.java b/app/src/main/java/be/tarsos/dsp/resample/FilterKit.java
new file mode 100644
index 0000000..b06403a
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/resample/FilterKit.java
@@ -0,0 +1,263 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+/******************************************************************************
+ *
+ * libresample4j
+ * Copyright (c) 2009 Laszlo Systems, Inc. All Rights Reserved.
+ *
+ * libresample4j is a Java port of Dominic Mazzoni's libresample 0.1.3,
+ * which is in turn based on Julius Smith's Resample 1.7 library.
+ * http://www-ccrma.stanford.edu/~jos/resample/
+ *
+ * License: LGPL -- see the file LICENSE.txt for more information
+ *
+ *****************************************************************************/
+package be.tarsos.dsp.resample;
+
+/**
+ * This file provides Kaiser-windowed low-pass filter support,
+ * including a function to create the filter coefficients, and
+ * two functions to apply the filter at a particular point.
+ *
+ *
+ * reference: "Digital Filters, 2nd edition"
+ * R.W. Hamming, pp. 178-179
+ *
+ * Izero() computes the 0th order modified bessel function of the first kind.
+ * (Needed to compute Kaiser window).
+ *
+ * LpFilter() computes the coeffs of a Kaiser-windowed low pass filter with
+ * the following characteristics:
+ *
+ * c[] = array in which to store computed coeffs
+ * frq = roll-off frequency of filter
+ * N = Half the window length in number of coeffs
+ * Beta = parameter of Kaiser window
+ * Num = number of coeffs before 1/frq
+ *
+ * Beta trades the rejection of the lowpass filter against the transition
+ * width from passband to stopband. Larger Beta means a slower
+ * transition and greater stopband rejection. See Rabiner and Gold
+ * (Theory and Application of DSP) under Kaiser windows for more about
+ * Beta. The following table from Rabiner and Gold gives some feel
+ * for the effect of Beta:
+ *
+ * All ripples in dB, width of transition band = D*N where N = window length
+ *
+ * BETA D PB RIP SB RIP
+ * 2.120 1.50 +-0.27 -30
+ * 3.384 2.23 0.0864 -40
+ * 4.538 2.93 0.0274 -50
+ * 5.658 3.62 0.00868 -60
+ * 6.764 4.32 0.00275 -70
+ * 7.865 5.0 0.000868 -80
+ * 8.960 5.7 0.000275 -90
+ * 10.056 6.4 0.000087 -100
+ *
+ */
+class FilterKit {
+
+ // Max error acceptable in Izero
+ private static final double IzeroEPSILON = 1E-21;
+
+ private static double Izero(double x) {
+ double sum, u, halfx, temp;
+ int n;
+
+ sum = u = n = 1;
+ halfx = x / 2.0;
+ do {
+ temp = halfx / (double) n;
+ n += 1;
+ temp *= temp;
+ u *= temp;
+ sum += u;
+ } while (u >= IzeroEPSILON * sum);
+ return (sum);
+ }
+
+ public static void lrsLpFilter(double[] c, int N, double frq, double Beta, int Num) {
+ double IBeta, temp, temp1, inm1;
+ int i;
+
+ // Calculate ideal lowpass filter impulse response coefficients:
+ c[0] = 2.0 * frq;
+ for (i = 1; i < N; i++) {
+ temp = Math.PI * (double) i / (double) Num;
+ c[i] = Math.sin(2.0 * temp * frq) / temp; // Analog sinc function,
+ // cutoff = frq
+ }
+
+ /*
+ * Calculate and Apply Kaiser window to ideal lowpass filter. Note: last
+ * window value is IBeta which is NOT zero. You're supposed to really
+ * truncate the window here, not ramp it to zero. This helps reduce the
+ * first sidelobe.
+ */
+ IBeta = 1.0 / Izero(Beta);
+ inm1 = 1.0 / ((double) (N - 1));
+ for (i = 1; i < N; i++) {
+ temp = (double) i * inm1;
+ temp1 = 1.0 - temp * temp;
+ temp1 = (temp1 < 0 ? 0 : temp1); /*
+ * make sure it's not negative
+ * since we're taking the square
+ * root - this happens on Pentium
+ * 4's due to tiny roundoff errors
+ */
+ c[i] *= Izero(Beta * Math.sqrt(temp1)) * IBeta;
+ }
+ }
+
+ /**
+ *
+ * @param Imp impulse response
+ * @param ImpD impulse response deltas
+ * @param Nwing length of one wing of filter
+ * @param Interp Interpolate coefs using deltas?
+ * @param Xp_array Current sample array
+ * @param Xp_index Current sample index
+ * @param Ph Phase
+ * @param Inc increment (1 for right wing or -1 for left)
+ * @return v.
+ */
+ public static float lrsFilterUp(float[] Imp, float[] ImpD, int Nwing, boolean Interp, float[] Xp_array, int Xp_index, double Ph,
+ int Inc) {
+ double a = 0;
+ float v, t;
+
+ Ph *= Resampler.Npc; // Npc is number of values per 1/delta in impulse
+ // response
+
+ v = 0.0f; // The output value
+
+ float[] Hp_array = Imp;
+ int Hp_index = (int) Ph;
+
+ int End_index = Nwing;
+
+ float[] Hdp_array = ImpD;
+ int Hdp_index = (int) Ph;
+
+ if (Interp) {
+ // Hdp = &ImpD[(int)Ph];
+ a = Ph - Math.floor(Ph); /* fractional part of Phase */
+ }
+
+ if (Inc == 1) // If doing right wing...
+ { // ...drop extra coeff, so when Ph is
+ End_index--; // 0.5, we don't do too many mult's
+ if (Ph == 0) // If the phase is zero...
+ { // ...then we've already skipped the
+ Hp_index += Resampler.Npc; // first sample, so we must also
+ Hdp_index += Resampler.Npc; // skip ahead in Imp[] and ImpD[]
+ }
+ }
+
+ if (Interp)
+ while (Hp_index < End_index) {
+ t = Hp_array[Hp_index]; /* Get filter coeff */
+ t += Hdp_array[Hdp_index] * a; /* t is now interp'd filter coeff */
+ Hdp_index += Resampler.Npc; /* Filter coeff differences step */
+ t *= Xp_array[Xp_index]; /* Mult coeff by input sample */
+ v += t; /* The filter output */
+ Hp_index += Resampler.Npc; /* Filter coeff step */
+ Xp_index += Inc; /* Input signal step. NO CHECK ON BOUNDS */
+ }
+ else
+ while (Hp_index < End_index) {
+ t = Hp_array[Hp_index]; /* Get filter coeff */
+ t *= Xp_array[Xp_index]; /* Mult coeff by input sample */
+ v += t; /* The filter output */
+ Hp_index += Resampler.Npc; /* Filter coeff step */
+ Xp_index += Inc; /* Input signal step. NO CHECK ON BOUNDS */
+ }
+
+ return v;
+ }
+
+ /**
+ *
+ * @param Imp impulse response
+ * @param ImpD impulse response deltas
+ * @param Nwing length of one wing of filter
+ * @param Interp Interpolate coefs using deltas?
+ * @param Xp_array Current sample array
+ * @param Xp_index Current sample index
+ * @param Ph Phase
+ * @param Inc increment (1 for right wing or -1 for left)
+ * @param dhb filter sampling period
+ * @return v.
+ */
+ public static float lrsFilterUD(float[] Imp, float[] ImpD, int Nwing, boolean Interp, float[] Xp_array, int Xp_index, double Ph,
+ int Inc, double dhb) {
+ float a;
+ float v, t;
+ double Ho;
+
+ v = 0.0f; // The output value
+ Ho = Ph * dhb;
+
+ int End_index = Nwing;
+
+ if (Inc == 1) // If doing right wing...
+ { // ...drop extra coeff, so when Ph is
+ End_index--; // 0.5, we don't do too many mult's
+ if (Ph == 0) // If the phase is zero...
+ Ho += dhb; // ...then we've already skipped the
+ } // first sample, so we must also
+ // skip ahead in Imp[] and ImpD[]
+
+ float[] Hp_array = Imp;
+ int Hp_index;
+
+ if (Interp) {
+ float[] Hdp_array = ImpD;
+ int Hdp_index;
+
+ while ((Hp_index = (int) Ho) < End_index) {
+ t = Hp_array[Hp_index]; // Get IR sample
+ Hdp_index = (int) Ho; // get interp bits from diff table
+ a = (float) (Ho - Math.floor(Ho)); // a is logically between 0
+ // and 1
+ t += Hdp_array[Hdp_index] * a; // t is now interp'd filter coeff
+ t *= Xp_array[Xp_index]; // Mult coeff by input sample
+ v += t; // The filter output
+ Ho += dhb; // IR step
+ Xp_index += Inc; // Input signal step. NO CHECK ON BOUNDS
+ }
+ } else {
+ while ((Hp_index = (int) Ho) < End_index) {
+ t = Hp_array[Hp_index]; // Get IR sample
+ t *= Xp_array[Xp_index]; // Mult coeff by input sample
+ v += t; // The filter output
+ Ho += dhb; // IR step
+ Xp_index += Inc; // Input signal step. NO CHECK ON BOUNDS
+ }
+ }
+
+ return v;
+ }
+
+}
diff --git a/app/src/main/java/be/tarsos/dsp/resample/RateTransposer.java b/app/src/main/java/be/tarsos/dsp/resample/RateTransposer.java
new file mode 100644
index 0000000..e25aaec
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/resample/RateTransposer.java
@@ -0,0 +1,85 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.resample;
+
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.AudioProcessor;
+
+
+/**
+ * Sample rate transposer. Changes sample rate by using interpolation
+ *
+ * Together with the time stretcher this can be used for pitch shifting.
+ * @author Joren Six
+ */
+public class RateTransposer implements AudioProcessor {
+
+ private double factor;
+ private final Resampler r;
+
+ /**
+ * Create a new sample rate transposer. The factor determines the new sample
+ * rate. E.g. 0.5 is half the sample rate, 1.0 does not change a thing and
+ * 2.0 doubles the samplerate. If the samples are played at the original
+ * speed the pitch doubles (0.5), does not change (1.0) or halves (0.5)
+ * respectively. Playback length follows the same rules, obviously.
+ *
+ * @param factor
+ * Determines the new sample rate. E.g. 0.5 is half the sample
+ * rate, 1.0 does not change a thing and 2.0 doubles the sample
+ * rate. If the samples are played at the original speed the
+ * pitch doubles (0.5), does not change (1.0) or halves (0.5)
+ * respectively. Playback length follows the same rules,
+ * obviously.
+ */
+ public RateTransposer(double factor){
+ this.factor = factor;
+ r= new Resampler(false,0.1,4.0);
+ }
+
+ public void setFactor(double tempo){
+ this.factor = tempo;
+ }
+
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+ float[] src = audioEvent.getFloatBuffer();
+ //Creation of float array in loop could be prevented if src.length is known beforehand...
+ //Possible optimization is to instantiate it outside the loop and get a pointer to the
+ //array here, in the process method method.
+ float[] out = new float[(int) (src.length * factor)];
+ r.process(factor, src, 0, src.length, false, out, 0, out.length);
+ //The size of the output buffer changes (according to factor).
+ audioEvent.setFloatBuffer(out);
+ //Update overlap offset to match new buffer size
+ audioEvent.setOverlap((int) (audioEvent.getOverlap() * factor));
+ return true;
+ }
+
+ @Override
+ public void processingFinished() {
+
+ }
+
+}
diff --git a/app/src/main/java/be/tarsos/dsp/resample/Resampler.java b/app/src/main/java/be/tarsos/dsp/resample/Resampler.java
new file mode 100644
index 0000000..b1d1ac4
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/resample/Resampler.java
@@ -0,0 +1,474 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+/******************************************************************************
+ *
+ * libresample4j
+ * Copyright (c) 2009 Laszlo Systems, Inc. All Rights Reserved.
+ *
+ * libresample4j is a Java port of Dominic Mazzoni's libresample 0.1.3,
+ * which is in turn based on Julius Smith's Resample 1.7 library.
+ * http://www-ccrma.stanford.edu/~jos/resample/
+ *
+ * License: LGPL -- see the file LICENSE.txt for more information
+ *
+ *****************************************************************************/
+package be.tarsos.dsp.resample;
+
+import java.nio.FloatBuffer;
+
+public class Resampler {
+
+ public static class Result {
+ public final int inputSamplesConsumed;
+ public final int outputSamplesGenerated;
+
+ public Result(int inputSamplesConsumed, int outputSamplesGenerated) {
+ this.inputSamplesConsumed = inputSamplesConsumed;
+ this.outputSamplesGenerated = outputSamplesGenerated;
+ }
+ }
+
+ // number of values per 1/delta in impulse response
+ protected static final int Npc = 4096;
+
+ private final float[] Imp;
+ private final float[] ImpD;
+ private final float LpScl;
+ private final int Nmult;
+ private final int Nwing;
+ private final double minFactor;
+ private final double maxFactor;
+ private final int XSize;
+ private final float[] X;
+ private int Xp; // Current "now"-sample pointer for input
+ private int Xread; // Position to put new samples
+ private final int Xoff;
+ private final float[] Y;
+ private int Yp;
+ private double Time;
+
+ /**
+ * Clone an existing resampling session. Faster than creating one from scratch.
+ *
+ * @param other
+ */
+ public Resampler(Resampler other) {
+ this.Imp = other.Imp.clone();
+ this.ImpD = other.ImpD.clone();
+ this.LpScl = other.LpScl;
+ this.Nmult = other.Nmult;
+ this.Nwing = other.Nwing;
+ this.minFactor = other.minFactor;
+ this.maxFactor = other.maxFactor;
+ this.XSize = other.XSize;
+ this.X = other.X.clone();
+ this.Xp = other.Xp;
+ this.Xread = other.Xread;
+ this.Xoff = other.Xoff;
+ this.Y = other.Y.clone();
+ this.Yp = other.Yp;
+ this.Time = other.Time;
+ }
+
+ /**
+ * Create a new resampling session.
+ *
+ * @param highQuality true for better quality, slower processing time
+ * @param minFactor lower bound on resampling factor for this session
+ * @param maxFactor upper bound on resampling factor for this session
+ * @throws IllegalArgumentException if minFactor or maxFactor is not
+ * positive, or if maxFactor is less than minFactor
+ */
+ public Resampler(boolean highQuality, double minFactor, double maxFactor) {
+ if (minFactor <= 0.0 || maxFactor <= 0.0) {
+ throw new IllegalArgumentException("minFactor and maxFactor must be positive");
+ }
+ if (maxFactor < minFactor) {
+ throw new IllegalArgumentException("minFactor must be <= maxFactor");
+ }
+
+ this.minFactor = minFactor;
+ this.maxFactor = maxFactor;
+ this.Nmult = highQuality ? 35 : 11;
+ this.LpScl = 1.0f;
+ this.Nwing = Npc * (this.Nmult - 1) / 2; // # of filter coeffs in right wing
+
+ double Rolloff = 0.90;
+ double Beta = 6;
+
+ double[] Imp64 = new double[this.Nwing];
+
+ FilterKit.lrsLpFilter(Imp64, this.Nwing, 0.5 * Rolloff, Beta, Npc);
+ this.Imp = new float[this.Nwing];
+ this.ImpD = new float[this.Nwing];
+
+ for (int i = 0; i < this.Nwing; i++) {
+ this.Imp[i] = (float) Imp64[i];
+ }
+
+ // Storing deltas in ImpD makes linear interpolation
+ // of the filter coefficients faster
+ for (int i = 0; i < this.Nwing - 1; i++) {
+ this.ImpD[i] = this.Imp[i + 1] - this.Imp[i];
+ }
+
+ // Last coeff. not interpolated
+ this.ImpD[this.Nwing - 1] = -this.Imp[this.Nwing - 1];
+
+ // Calc reach of LP filter wing (plus some creeping room)
+ int Xoff_min = (int) (((this.Nmult + 1) / 2.0) * Math.max(1.0, 1.0 / minFactor) + 10);
+ int Xoff_max = (int) (((this.Nmult + 1) / 2.0) * Math.max(1.0, 1.0 / maxFactor) + 10);
+ this.Xoff = Math.max(Xoff_min, Xoff_max);
+
+ // Make the inBuffer size at least 4096, but larger if necessary
+ // in order to store the minimum reach of the LP filter and then some.
+ // Then allocate the buffer an extra Xoff larger so that
+ // we can zero-pad up to Xoff zeros at the end when we reach the
+ // end of the input samples.
+ this.XSize = Math.max(2 * this.Xoff + 10, 4096);
+ this.X = new float[this.XSize + this.Xoff];
+ this.Xp = this.Xoff;
+ this.Xread = this.Xoff;
+
+ // Make the outBuffer long enough to hold the entire processed
+ // output of one inBuffer
+ int YSize = (int) (((double) this.XSize) * maxFactor + 2.0);
+ this.Y = new float[YSize];
+ this.Yp = 0;
+
+ this.Time = (double) this.Xoff; // Current-time pointer for converter
+ }
+
+ public int getFilterWidth() {
+ return this.Xoff;
+ }
+
+ /**
+ * Process a batch of samples. There is no guarantee that the input buffer will be drained.
+ *
+ * @param factor factor at which to resample this batch
+ * @param buffers sample buffer for producing input and consuming output
+ * @param lastBatch true if this is known to be the last batch of samples
+ * @return true iff resampling is complete (ie. no input samples consumed and no output samples produced)
+ */
+ public boolean process(double factor, SampleBuffers buffers, boolean lastBatch) {
+ if (factor < this.minFactor || factor > this.maxFactor) {
+ throw new IllegalArgumentException("factor " + factor + " is not between minFactor=" + minFactor
+ + " and maxFactor=" + maxFactor);
+ }
+
+ int outBufferLen = buffers.getOutputBufferLength();
+ int inBufferLen = buffers.getInputBufferLength();
+
+ float[] Imp = this.Imp;
+ float[] ImpD = this.ImpD;
+ float LpScl = this.LpScl;
+ int Nwing = this.Nwing;
+ boolean interpFilt = false; // TRUE means interpolate filter coeffs
+
+ int inBufferUsed = 0;
+ int outSampleCount = 0;
+
+ // Start by copying any samples still in the Y buffer to the output
+ // buffer
+ if ((this.Yp != 0) && (outBufferLen - outSampleCount) > 0) {
+ int len = Math.min(outBufferLen - outSampleCount, this.Yp);
+
+ buffers.consumeOutput(this.Y, 0, len);
+ //for (int i = 0; i < len; i++) {
+ // outBuffer[outBufferOffset + outSampleCount + i] = this.Y[i];
+ //}
+
+ outSampleCount += len;
+ for (int i = 0; i < this.Yp - len; i++) {
+ this.Y[i] = this.Y[i + len];
+ }
+ this.Yp -= len;
+ }
+
+ // If there are still output samples left, return now - we need
+ // the full output buffer available to us...
+ if (this.Yp != 0) {
+ return inBufferUsed == 0 && outSampleCount == 0;
+ }
+
+ // Account for increased filter gain when using factors less than 1
+ if (factor < 1) {
+ LpScl = (float) (LpScl * factor);
+ }
+
+ while (true) {
+
+ // This is the maximum number of samples we can process
+ // per loop iteration
+
+ /*
+ * #ifdef DEBUG
+ * printf("XSize: %d Xoff: %d Xread: %d Xp: %d lastFlag: %d\n",
+ * this.XSize, this.Xoff, this.Xread, this.Xp, lastFlag); #endif
+ */
+
+ // Copy as many samples as we can from the input buffer into X
+ int len = this.XSize - this.Xread;
+
+ if (len >= inBufferLen - inBufferUsed) {
+ len = inBufferLen - inBufferUsed;
+ }
+
+ buffers.produceInput(this.X, this.Xread, len);
+ //for (int i = 0; i < len; i++) {
+ // this.X[this.Xread + i] = inBuffer[inBufferOffset + inBufferUsed + i];
+ //}
+
+ inBufferUsed += len;
+ this.Xread += len;
+
+ int Nx;
+ if (lastBatch && (inBufferUsed == inBufferLen)) {
+ // If these are the last samples, zero-pad the
+ // end of the input buffer and make sure we process
+ // all the way to the end
+ Nx = this.Xread - this.Xoff;
+ for (int i = 0; i < this.Xoff; i++) {
+ this.X[this.Xread + i] = 0;
+ }
+ } else {
+ Nx = this.Xread - 2 * this.Xoff;
+ }
+
+ /*
+ * #ifdef DEBUG fprintf(stderr, "new len=%d Nx=%d\n", len, Nx);
+ * #endif
+ */
+
+ if (Nx <= 0) {
+ break;
+ }
+
+ // Resample stuff in input buffer
+ int Nout;
+ if (factor >= 1) { // SrcUp() is faster if we can use it */
+ Nout = lrsSrcUp(this.X, this.Y, factor, /* &this.Time, */Nx, Nwing, LpScl, Imp, ImpD, interpFilt);
+ } else {
+ Nout = lrsSrcUD(this.X, this.Y, factor, /* &this.Time, */Nx, Nwing, LpScl, Imp, ImpD, interpFilt);
+ }
+
+ /*
+ * #ifdef DEBUG
+ * printf("Nout: %d\n", Nout);
+ * #endif
+ */
+
+ this.Time -= Nx; // Move converter Nx samples back in time
+ this.Xp += Nx; // Advance by number of samples processed
+
+ // Calc time accumulation in Time
+ int Ncreep = (int) (this.Time) - this.Xoff;
+ if (Ncreep != 0) {
+ this.Time -= Ncreep; // Remove time accumulation
+ this.Xp += Ncreep; // and add it to read pointer
+ }
+
+ // Copy part of input signal that must be re-used
+ int Nreuse = this.Xread - (this.Xp - this.Xoff);
+
+ for (int i = 0; i < Nreuse; i++) {
+ this.X[i] = this.X[i + (this.Xp - this.Xoff)];
+ }
+
+ /*
+ #ifdef DEBUG
+ printf("New Xread=%d\n", Nreuse);
+ #endif */
+
+ this.Xread = Nreuse; // Pos in input buff to read new data into
+ this.Xp = this.Xoff;
+
+ this.Yp = Nout;
+
+ // Copy as many samples as possible to the output buffer
+ if (this.Yp != 0 && (outBufferLen - outSampleCount) > 0) {
+ len = Math.min(outBufferLen - outSampleCount, this.Yp);
+
+ buffers.consumeOutput(this.Y, 0, len);
+ //for (int i = 0; i < len; i++) {
+ // outBuffer[outBufferOffset + outSampleCount + i] = this.Y[i];
+ //}
+
+ outSampleCount += len;
+ for (int i = 0; i < this.Yp - len; i++) {
+ this.Y[i] = this.Y[i + len];
+ }
+ this.Yp -= len;
+ }
+
+ // If there are still output samples left, return now,
+ // since we need the full output buffer available
+ if (this.Yp != 0) {
+ break;
+ }
+ }
+
+ return inBufferUsed == 0 && outSampleCount == 0;
+ }
+
+ /**
+ * Process a batch of samples. Convenience method for when the input and output are both floats.
+ *
+ * @param factor factor at which to resample this batch
+ * @param inputBuffer contains input samples in the range -1.0 to 1.0
+ * @param outputBuffer output samples will be deposited here
+ * @param lastBatch true if this is known to be the last batch of samples
+ * @return true iff resampling is complete (ie. no input samples consumed and no output samples produced)
+ */
+ public boolean process(double factor, final FloatBuffer inputBuffer, boolean lastBatch, final FloatBuffer outputBuffer) {
+ SampleBuffers sampleBuffers = new SampleBuffers() {
+ public int getInputBufferLength() {
+ return inputBuffer.remaining();
+ }
+
+ public int getOutputBufferLength() {
+ return outputBuffer.remaining();
+ }
+
+ public void produceInput(float[] array, int offset, int length) {
+ inputBuffer.get(array, offset, length);
+ }
+
+ public void consumeOutput(float[] array, int offset, int length) {
+ outputBuffer.put(array, offset, length);
+ }
+ };
+ return process(factor, sampleBuffers, lastBatch);
+ }
+
+ /**
+ * Process a batch of samples. Alternative interface if you prefer to work with arrays.
+ *
+ * @param factor resampling rate for this batch
+ * @param inBuffer array containing input samples in the range -1.0 to 1.0
+ * @param inBufferOffset offset into inBuffer at which to start processing
+ * @param inBufferLen number of valid elements in the inputBuffer
+ * @param lastBatch pass true if this is the last batch of samples
+ * @param outBuffer array to hold the resampled data
+ * @param outBufferOffset Offset in the output buffer.
+ * @param outBufferLen Output buffer length.
+ * @return the number of samples consumed and generated
+ */
+ public Result process(double factor, float[] inBuffer, int inBufferOffset, int inBufferLen, boolean lastBatch, float[] outBuffer, int outBufferOffset, int outBufferLen) {
+ FloatBuffer inputBuffer = FloatBuffer.wrap(inBuffer, inBufferOffset, inBufferLen);
+ FloatBuffer outputBuffer = FloatBuffer.wrap(outBuffer, outBufferOffset, outBufferLen);
+
+ process(factor, inputBuffer, lastBatch, outputBuffer);
+
+ return new Result(inputBuffer.position() - inBufferOffset, outputBuffer.position() - outBufferOffset);
+ }
+
+
+
+ /*
+ * Sampling rate up-conversion only subroutine; Slightly faster than
+ * down-conversion;
+ */
+ private int lrsSrcUp(float[] X, float[] Y, double factor, int Nx, int Nwing, float LpScl, float[] Imp,
+ float[] ImpD, boolean Interp) {
+
+ float[] Xp_array = X;
+ int Xp_index;
+
+ float[] Yp_array = Y;
+ int Yp_index = 0;
+
+ float v;
+
+ double CurrentTime = this.Time;
+ double dt; // Step through input signal
+ double endTime; // When Time reaches EndTime, return to user
+
+ dt = 1.0 / factor; // Output sampling period
+
+ endTime = CurrentTime + Nx;
+ while (CurrentTime < endTime) {
+ double LeftPhase = CurrentTime - Math.floor(CurrentTime);
+ double RightPhase = 1.0 - LeftPhase;
+
+ Xp_index = (int) CurrentTime; // Ptr to current input sample
+ // Perform left-wing inner product
+ v = FilterKit.lrsFilterUp(Imp, ImpD, Nwing, Interp, Xp_array, Xp_index++, LeftPhase, -1);
+ // Perform right-wing inner product
+ v += FilterKit.lrsFilterUp(Imp, ImpD, Nwing, Interp, Xp_array, Xp_index, RightPhase, 1);
+
+ v *= LpScl; // Normalize for unity filter gain
+
+ Yp_array[Yp_index++] = v; // Deposit output
+ CurrentTime += dt; // Move to next sample by time increment
+ }
+
+ this.Time = CurrentTime;
+ return Yp_index; // Return the number of output samples
+ }
+
+ private int lrsSrcUD(float[] X, float[] Y, double factor, int Nx, int Nwing, float LpScl, float[] Imp,
+ float[] ImpD, boolean Interp) {
+
+ float[] Xp_array = X;
+ int Xp_index;
+
+ float[] Yp_array = Y;
+ int Yp_index = 0;
+
+ float v;
+
+ double CurrentTime = this.Time;
+ double dh; // Step through filter impulse response
+ double dt; // Step through input signal
+ double endTime; // When Time reaches EndTime, return to user
+
+ dt = 1.0 / factor; // Output sampling period
+
+ dh = Math.min(Npc, factor * Npc); // Filter sampling period
+
+ endTime = CurrentTime + Nx;
+ while (CurrentTime < endTime) {
+ double LeftPhase = CurrentTime - Math.floor(CurrentTime);
+ double RightPhase = 1.0 - LeftPhase;
+
+ Xp_index = (int) CurrentTime; // Ptr to current input sample
+ // Perform left-wing inner product
+ v = FilterKit.lrsFilterUD(Imp, ImpD, Nwing, Interp, Xp_array, Xp_index++, LeftPhase, -1, dh);
+ // Perform right-wing inner product
+ v += FilterKit.lrsFilterUD(Imp, ImpD, Nwing, Interp, Xp_array, Xp_index, RightPhase, 1, dh);
+
+ v *= LpScl; // Normalize for unity filter gain
+
+ Yp_array[Yp_index++] = v; // Deposit output
+
+ CurrentTime += dt; // Move to next sample by time increment
+ }
+
+ this.Time = CurrentTime;
+ return Yp_index; // Return the number of output samples
+ }
+
+}
diff --git a/app/src/main/java/be/tarsos/dsp/resample/SampleBuffers.java b/app/src/main/java/be/tarsos/dsp/resample/SampleBuffers.java
new file mode 100644
index 0000000..c75b1e7
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/resample/SampleBuffers.java
@@ -0,0 +1,72 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+/******************************************************************************
+ *
+ * libresample4j
+ * Copyright (c) 2009 Laszlo Systems, Inc. All Rights Reserved.
+ *
+ * libresample4j is a Java port of Dominic Mazzoni's libresample 0.1.3,
+ * which is in turn based on Julius Smith's Resample 1.7 library.
+ * http://www-ccrma.stanford.edu/~jos/resample/
+ *
+ * License: LGPL -- see the file LICENSE.txt for more information
+ *
+ *****************************************************************************/
+package be.tarsos.dsp.resample;
+
+/**
+ * Callback for producing and consuming samples. Enables on-the-fly conversion between sample types
+ * (signed 16-bit integers to floats, for example) and/or writing directly to an output stream.
+ */
+interface SampleBuffers {
+ /**
+ * @return number of input samples available
+ */
+
+ int getInputBufferLength();
+
+ /**
+ * @return number of samples the output buffer has room for
+ */
+ int getOutputBufferLength();
+
+ /**
+ * Copy length
samples from the input buffer to the given array, starting at the given offset.
+ * Samples should be in the range -1.0f to 1.0f.
+ *
+ * @param array array to hold samples from the input buffer
+ * @param offset start writing samples here
+ * @param length write this many samples
+ */
+ void produceInput(float[] array, int offset, int length);
+
+ /**
+ * Copy length
samples from the given array to the output buffer, starting at the given offset.
+ *
+ * @param array array to read from
+ * @param offset start reading samples here
+ * @param length read this many samples
+ */
+ void consumeOutput(float[] array, int offset, int length);
+}
diff --git a/app/src/main/java/be/tarsos/dsp/resample/SoundTouchRateTransposer.java b/app/src/main/java/be/tarsos/dsp/resample/SoundTouchRateTransposer.java
new file mode 100644
index 0000000..7df1bef
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/resample/SoundTouchRateTransposer.java
@@ -0,0 +1,95 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.resample;
+
+import be.tarsos.dsp.AudioDispatcher;
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.AudioProcessor;
+
+/**
+ * Currently not working sample rate transposer, works only for integer factors.
+ * Changes sample rate by using linear interpolation.
+ *
+ * Together with the time stretcher this can be used for pitch shifting.
+ * @author Joren Six
+ * @author Olli Parviainen
+ */
+public class SoundTouchRateTransposer implements AudioProcessor {
+
+ private final double rate;
+ int slopeCount;
+ double prevSample;
+ private AudioDispatcher dispatcher;
+
+ public void setDispatcher(AudioDispatcher newDispatcher){
+ this.dispatcher = newDispatcher;
+ }
+
+ public SoundTouchRateTransposer(double d){
+ this.rate = d;
+ }
+
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+ int i, used;
+ float[] src = audioEvent.getFloatBuffer();
+ float[] dest = new float[(int) Math.round(audioEvent.getBufferSize() / rate)];
+ used = 0;
+ i = 0;
+
+ // Process the last sample saved from the previous call first...
+ while (slopeCount <= 1.0f) {
+ dest[i] = (float)((1.0f - slopeCount) * prevSample + slopeCount * src[0]);
+ i++;
+ slopeCount += rate;
+ }
+ slopeCount -= 1.0f;
+ end:
+ while(true){
+ while (slopeCount > 1.0f) {
+ slopeCount -= 1.0f;
+ used++;
+ if (used >= src.length - 1)
+ break end;
+ }
+ if(i < dest.length){
+ dest[i] = (float)((1.0f - slopeCount) * src[used] + slopeCount * src[used + 1]);
+ }
+ i++;
+ slopeCount += rate;
+ }
+
+ //Store the last sample for the next round
+ prevSample = src[src.length - 1];
+ dispatcher.setStepSizeAndOverlap(dest.length, 0);
+ audioEvent.setFloatBuffer(dest);
+ return true;
+ }
+
+ @Override
+ public void processingFinished() {
+
+ }
+
+}
diff --git a/app/src/main/java/be/tarsos/dsp/resample/package-info.java b/app/src/main/java/be/tarsos/dsp/resample/package-info.java
new file mode 100644
index 0000000..c223f43
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/resample/package-info.java
@@ -0,0 +1,28 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+/**
+ * A package with everything needed to resample audio.
+ */
+package be.tarsos.dsp.resample;
diff --git a/app/src/main/java/be/tarsos/dsp/synthesis/AmplitudeLFO.java b/app/src/main/java/be/tarsos/dsp/synthesis/AmplitudeLFO.java
new file mode 100644
index 0000000..31c63e0
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/synthesis/AmplitudeLFO.java
@@ -0,0 +1,76 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.synthesis;
+
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.AudioProcessor;
+
+/**
+ * A low frequency oscillator to change the amplitude of a signal.
+ */
+public class AmplitudeLFO implements AudioProcessor {
+
+ private final double frequency;
+ private final double scaleParameter;
+ private double phase;
+
+ /**
+ * Create a new low frequency oscillator with a default frequency (1.5Hz and scale 0.75)
+ */
+ public AmplitudeLFO(){
+ this(1.5,0.75);
+ }
+
+ /**
+ * Create a new low frequency oscillator
+ * @param frequency The frequency in Hz
+ * @param scaleParameter The scale between 0 and 1 to modify the amplitude.
+ */
+ public AmplitudeLFO(double frequency, double scaleParameter){
+ this.frequency = frequency;
+ this.scaleParameter = scaleParameter;
+ phase = 0;
+ }
+
+
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+ float[] buffer = audioEvent.getFloatBuffer();
+ double sampleRate = audioEvent.getSampleRate();
+ double twoPiF = 2 * Math.PI * frequency;
+ double time = 0;
+ for(int i = 0 ; i < buffer.length ; i++){
+ time = i / sampleRate;
+ float gain = (float) (scaleParameter * Math.sin(twoPiF * time + phase));
+ buffer[i] = gain * buffer[i];
+ }
+ phase = twoPiF * buffer.length / sampleRate + phase;
+ return true;
+ }
+
+ @Override
+ public void processingFinished() {
+ }
+
+}
diff --git a/app/src/main/java/be/tarsos/dsp/synthesis/NoiseGenerator.java b/app/src/main/java/be/tarsos/dsp/synthesis/NoiseGenerator.java
new file mode 100644
index 0000000..f147b53
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/synthesis/NoiseGenerator.java
@@ -0,0 +1,56 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.synthesis;
+
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.AudioProcessor;
+
+public class NoiseGenerator implements AudioProcessor{
+
+ private final double gain;
+
+ public NoiseGenerator(){
+ this(1.0);
+ }
+
+ public NoiseGenerator(double gain){
+ this.gain = gain;
+ }
+
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+ float[] buffer = audioEvent.getFloatBuffer();
+ for(int i = 0 ; i < buffer.length ; i++){
+ buffer[i] += (float) (Math.random() * gain);
+ }
+ return true;
+ }
+
+ @Override
+ public void processingFinished() {
+ }
+
+
+
+}
diff --git a/app/src/main/java/be/tarsos/dsp/synthesis/PitchResyntheziser.java b/app/src/main/java/be/tarsos/dsp/synthesis/PitchResyntheziser.java
new file mode 100644
index 0000000..494b92b
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/synthesis/PitchResyntheziser.java
@@ -0,0 +1,137 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.synthesis;
+
+import java.util.Arrays;
+
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.EnvelopeFollower;
+import be.tarsos.dsp.pitch.PitchDetectionHandler;
+import be.tarsos.dsp.pitch.PitchDetectionResult;
+
+/**
+ * This pitch detection handler replaces the audio buffer in the pipeline with a
+ * synthesized wave. It either follows the envelope of the original signal or
+ * not. Use it wisely. The following demonstrates how it can be used.
+ *
+ *
+ *
+ *
+ * @author Joren Six
+ */
+public class PitchResyntheziser implements PitchDetectionHandler {
+
+ private double phase = 0;
+ private double phaseFirst = 0;
+ private double phaseSecond = 0;
+ private double prevFrequency = 0;
+ private final float samplerate;
+ private final EnvelopeFollower envelopeFollower;
+ private final boolean usePureSine;
+ private final boolean followEnvelope;
+ private final double[] previousFrequencies;
+ private int previousFrequencyIndex;
+
+ public PitchResyntheziser(float samplerate){
+ this(samplerate,true,false);
+ }
+
+ public PitchResyntheziser(float samplerate,boolean followEnvelope,boolean pureSine){
+ this(samplerate,followEnvelope,pureSine,5);
+ }
+
+ public PitchResyntheziser(float samplerate,boolean followEnvelope,boolean pureSine,int filterSize){
+ envelopeFollower = new EnvelopeFollower(samplerate,0.005,0.01);
+ this.followEnvelope=followEnvelope;
+ this.usePureSine = pureSine;
+ this.samplerate = samplerate;
+ previousFrequencies = new double[filterSize];
+ previousFrequencyIndex = 0;
+ }
+
+ @Override
+ public void handlePitch(PitchDetectionResult pitchDetectionResult,
+ AudioEvent audioEvent) {
+ double frequency = pitchDetectionResult.getPitch();
+
+ if(frequency==-1){
+ frequency=prevFrequency;
+ }else{
+ if(previousFrequencies.length!=0){
+ //median filter
+ //store and adjust pointer
+ previousFrequencies[previousFrequencyIndex] = frequency;
+ previousFrequencyIndex++;
+ previousFrequencyIndex %= previousFrequencies.length;
+ //sort to get median frequency
+ double[] frequenciesCopy = previousFrequencies.clone();
+ Arrays.sort(frequenciesCopy);
+ //use the median as frequency
+ frequency = frequenciesCopy[frequenciesCopy.length/2];
+ }
+
+ prevFrequency = frequency;
+ }
+
+
+
+ final double twoPiF = 2 * Math.PI * frequency;
+ float[] audioBuffer = audioEvent.getFloatBuffer();
+ float[] envelope = null;
+ if(followEnvelope){
+ envelope = audioBuffer.clone();
+ envelopeFollower.calculateEnvelope(envelope);
+ }
+
+ for (int sample = 0; sample < audioBuffer.length; sample++) {
+ double time = sample / samplerate;
+ double wave = Math.sin(twoPiF * time + phase);
+ if(!usePureSine){
+ wave += 0.05 * Math.sin(twoPiF * 4 * time + phaseFirst);
+ wave += 0.01 * Math.sin(twoPiF * 8 * time + phaseSecond);
+ }
+ audioBuffer[sample] = (float) wave;
+ if(followEnvelope){
+ audioBuffer[sample] = audioBuffer[sample] * envelope[sample];
+ }
+ }
+
+ double timefactor = twoPiF * audioBuffer.length / samplerate;
+ phase = timefactor + phase;
+ if(!usePureSine){
+ phaseFirst = 4 * timefactor + phaseFirst;
+ phaseSecond = 8 * timefactor + phaseSecond;
+ }
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/synthesis/SineGenerator.java b/app/src/main/java/be/tarsos/dsp/synthesis/SineGenerator.java
new file mode 100644
index 0000000..ebc81e6
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/synthesis/SineGenerator.java
@@ -0,0 +1,62 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.synthesis;
+
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.AudioProcessor;
+
+public class SineGenerator implements AudioProcessor{
+
+ private final double gain;
+ private final double frequency;
+ private double phase;
+
+ public SineGenerator(){
+ this(1.0,440);
+ }
+
+ public SineGenerator(double gain,double frequency){
+ this.gain = gain;
+ this.frequency = frequency;
+ this.phase = 0;
+ }
+
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+ float[] buffer = audioEvent.getFloatBuffer();
+ double sampleRate = audioEvent.getSampleRate();
+ double twoPiF = 2 * Math.PI * frequency;
+ double time = 0;
+ for(int i = 0 ; i < buffer.length ; i++){
+ time = i / sampleRate;
+ buffer[i] += (float) (gain * Math.sin(twoPiF * time + phase));
+ }
+ phase = twoPiF * buffer.length / sampleRate + phase;
+ return true;
+ }
+
+ @Override
+ public void processingFinished() {
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/synthesis/package-info.java b/app/src/main/java/be/tarsos/dsp/synthesis/package-info.java
new file mode 100644
index 0000000..0c93db1
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/synthesis/package-info.java
@@ -0,0 +1,28 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+/**
+ * Some audio generates, sine waves, noise,....
+ */
+package be.tarsos.dsp.synthesis;
diff --git a/app/src/main/java/be/tarsos/dsp/util/AudioResourceUtils.java b/app/src/main/java/be/tarsos/dsp/util/AudioResourceUtils.java
new file mode 100644
index 0000000..97bd7b9
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/util/AudioResourceUtils.java
@@ -0,0 +1,170 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Some utility functions to handle audio resources.
+ *
+ * @author Joren Six
+ */
+public class AudioResourceUtils {
+
+ private AudioResourceUtils() {
+ }
+
+ /**
+ * Returns a more practical audio resource name. E.g. if
+ * http://stream.com/stream.pls is given, the PLS-file is parsed and the
+ * first audio file is returned. It supports PLS, M3U, AXS and XSPF"
+ *
+ * @param inputResource
+ * The input resource, a file, URL, PLS-file or M3U-file.
+ *
+ * @return A more practical audio resource name.
+ */
+ public static String sanitizeResource(String inputResource) {
+ if (inputResource.toLowerCase().endsWith("pls")) {
+ inputResource = parsePLS(inputResource);
+ } else if (inputResource.toLowerCase().endsWith("m3u")) {
+ inputResource = parseM3U(inputResource);
+ } else if (inputResource.toLowerCase().endsWith("asx")){
+ inputResource = parseASX(inputResource);
+ } else if (inputResource.toLowerCase().endsWith("xspf")){
+ inputResource = parseXSPF(inputResource);
+ }
+ return inputResource;
+ }
+
+ private static String parseXSPF(String inputResource){
+ String inputFile = "";
+ try {
+ String contents = readTextFromUrl(new URL(inputResource));
+ for (String line : contents.split("\n")) {
+ if (line.toLowerCase().contains("href")) {
+ String pattern = "(?i)
+ * PitchEstimationAlgorithm algo = PitchEstimationAlgorithm.FFT_YIN;
+ * PitchResyntheziser prs = new PitchResyntheziser(samplerate);
+ * AudioDispatcher dispatcher = AudioDispatcher.fromFile(new File("in.wav"),1024, 0);
+ * //Handle pitch detection
+ * dispatcher.addAudioProcessor(new PitchProcessor(algo, samplerate, size, prs));
+ * //Write the synthesized pitch to an output file.
+ * dispatcher.addAudioProcessor(new WaveformWriter(format, "out.wav"));//
+ * dispatcher.run();
+ *
+ *
(x+i*y) + (s+i*t) = (x+s)+i*(y+t).
+ @param w is the number to add.
+ @return z+w where z is this Complex number.
+*/
+public Complex plus(Complex w) {
+ return new Complex(x+w.real(),y+w.imag());
+}
+
+/**
+ Subtraction of Complex numbers (doesn't change this Complex number).
+
(x+i*y) - (s+i*t) = (x-s)+i*(y-t).
+ @param w is the number to subtract.
+ @return z-w where z is this Complex number.
+*/
+public Complex minus(Complex w) {
+ return new Complex(x-w.real(),y-w.imag());
+}
+
+/**
+ Complex multiplication (doesn't change this Complex number).
+ @param w is the number to multiply by.
+ @return z*w where z is this Complex number.
+*/
+public Complex times(Complex w) {
+ return new Complex(x*w.real()-y*w.imag(),x*w.imag()+y*w.real());
+}
+
+/**
+ Division of Complex numbers (doesn't change this Complex number).
+
(x+i*y)/(s+i*t) = ((x*s+y*t) + i*(y*s-y*t)) / (s^2+t^2)
+ @param w is the number to divide by
+ @return new Complex number z/w where z is this Complex number
+*/
+public Complex div(Complex w) {
+ double den=Math.pow(w.mod(),2);
+ return new Complex((x*w.real()+y*w.imag())/den,(y*w.real()-x*w.imag())/den);
+}
+
+/**
+ Complex exponential (doesn't change this Complex number).
+ @return exp(z) where z is this Complex number.
+*/
+public Complex exp() {
+ return new Complex(Math.exp(x)*Math.cos(y),Math.exp(x)*Math.sin(y));
+}
+
+/**
+ Principal branch of the Complex logarithm of this Complex number.
+ (doesn't change this Complex number).
+ The principal branch is the branch with -pi < arg <= pi.
+ @return log(z) where z is this Complex number.
+*/
+public Complex log() {
+ return new Complex(Math.log(this.mod()),this.arg());
+}
+
+/**
+ Complex square root (doesn't change this complex number).
+ Computes the principal branch of the square root, which
+ is the value with 0 <= arg < pi.
+ @return sqrt(z) where z is this Complex number.
+*/
+public Complex sqrt() {
+ double r=Math.sqrt(this.mod());
+ double theta=this.arg()/2;
+ return new Complex(r*Math.cos(theta),r*Math.sin(theta));
+}
+
+// Real cosh function (used to compute complex trig functions)
+private double cosh(double theta) {
+ return (Math.exp(theta)+Math.exp(-theta))/2;
+}
+
+// Real sinh function (used to compute complex trig functions)
+private double sinh(double theta) {
+ return (Math.exp(theta)-Math.exp(-theta))/2;
+}
+
+/**
+ Sine of this Complex number (doesn't change this Complex number).
+
sin(z) = (exp(i*z)-exp(-i*z))/(2*i).
+ @return sin(z) where z is this Complex number.
+*/
+public Complex sin() {
+ return new Complex(cosh(y)*Math.sin(x),sinh(y)*Math.cos(x));
+}
+
+/**
+ Cosine of this Complex number (doesn't change this Complex number).
+
cos(z) = (exp(i*z)+exp(-i*z))/ 2.
+ @return cos(z) where z is this Complex number.
+*/
+public Complex cos() {
+ return new Complex(cosh(y)*Math.cos(x),-sinh(y)*Math.sin(x));
+}
+
+/**
+ Hyperbolic sine of this Complex number
+ (doesn't change this Complex number).
+
sinh(z) = (exp(z)-exp(-z))/2.
+ @return sinh(z) where z is this Complex number.
+*/
+public Complex sinh() {
+ return new Complex(sinh(x)*Math.cos(y),cosh(x)*Math.sin(y));
+}
+
+/**
+ Hyperbolic cosine of this Complex number
+ (doesn't change this Complex number).
+
cosh(z) = (exp(z) + exp(-z)) / 2.
+ @return cosh(z) where z is this Complex number.
+*/
+public Complex cosh() {
+ return new Complex(cosh(x)*Math.cos(y),sinh(x)*Math.sin(y));
+}
+
+/**
+ Tangent of this Complex number (doesn't change this Complex number).
+
tan(z) = sin(z)/cos(z).
+ @return tan(z) where z is this Complex number.
+*/
+public Complex tan() {
+ return (this.sin()).div(this.cos());
+}
+
+/**
+ Negative of this complex number (chs stands for change sign).
+ This produces a new Complex number and doesn't change
+ this Complex number.
+
-(x+i*y) = -x-i*y.
+ @return -z where z is this Complex number.
+*/
+public Complex chs() {
+ return new Complex(-x,-y);
+}
+
+/**
+ String representation of this Complex number.
+ @return x+i*y, x-i*y, x, or i*y as appropriate.
+*/
+public String toString() {
+ if (x!=0 && y>0) {
+ return x+" + "+y+"i";
+ }
+ if (x!=0 && y<0) {
+ return x+" - "+(-y)+"i";
+ }
+ if (y==0) {
+ return String.valueOf(x);
+ }
+ if (x==0) {
+ return y+"i";
+ }
+ // shouldn't get here (unless Inf or NaN)
+ return x+" + i*"+y;
+
+}
+}
+
diff --git a/app/src/main/java/be/tarsos/dsp/util/ConcurrencyUtils.java b/app/src/main/java/be/tarsos/dsp/util/ConcurrencyUtils.java
new file mode 100644
index 0000000..ac3153c
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/util/ConcurrencyUtils.java
@@ -0,0 +1,331 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Parallel Colt.
+ *
+ * The Initial Developer of the Original Code is
+ * Piotr Wendykier, Emory University.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+package be.tarsos.dsp.util;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * Concurrency utilities.
+ *
+ * @author Piotr Wendykier (piotr.wendykier@gmail.com)
+ */
+public class ConcurrencyUtils {
+ /**
+ * Thread pool.
+ */
+ private static final ExecutorService THREAD_POOL = Executors.newCachedThreadPool(new CustomThreadFactory(new CustomExceptionHandler()));
+
+ private static int THREADS_BEGIN_N_1D_FFT_2THREADS = 8192;
+
+ private static int THREADS_BEGIN_N_1D_FFT_4THREADS = 65536;
+
+ private static int THREADS_BEGIN_N_2D = 65536;
+
+ private static int THREADS_BEGIN_N_3D = 65536;
+
+ private static int NTHREADS = prevPow2(getNumberOfProcessors());
+
+ private ConcurrencyUtils() {
+
+ }
+
+ private static class CustomExceptionHandler implements Thread.UncaughtExceptionHandler {
+ public void uncaughtException(Thread t, Throwable e) {
+ e.printStackTrace();
+ }
+
+ }
+
+ private static class CustomThreadFactory implements ThreadFactory {
+ private static final ThreadFactory defaultFactory = Executors.defaultThreadFactory();
+
+ private final Thread.UncaughtExceptionHandler handler;
+
+ CustomThreadFactory(Thread.UncaughtExceptionHandler handler) {
+ this.handler = handler;
+ }
+
+ public Thread newThread(Runnable r) {
+ Thread t = defaultFactory.newThread(r);
+ t.setUncaughtExceptionHandler(handler);
+ return t;
+ }
+ }
+
+ /**
+ * Returns the number of available processors.
+ *
+ * @return number of available processors
+ */
+ public static int getNumberOfProcessors() {
+ return Runtime.getRuntime().availableProcessors();
+ }
+
+ /**
+ * Returns the current number of threads.
+ *
+ * @return the current number of threads.
+ */
+ public static int getNumberOfThreads() {
+ return NTHREADS;
+ }
+
+ /**
+ * Sets the number of threads. If n is not a power-of-two number, then the
+ * number of threads is set to the closest power-of-two number less than n.
+ *
+ * @param n The number of threads
+ */
+ public static void setNumberOfThreads(int n) {
+ NTHREADS = prevPow2(n);
+ }
+
+ /**
+ * Returns the minimal size of 1D data for which two threads are used.
+ *
+ * @return the minimal size of 1D data for which two threads are used
+ */
+ public static int getThreadsBeginN_1D_FFT_2Threads() {
+ return THREADS_BEGIN_N_1D_FFT_2THREADS;
+ }
+
+ /**
+ * Returns the minimal size of 1D data for which four threads are used.
+ *
+ * @return the minimal size of 1D data for which four threads are used
+ */
+ public static int getThreadsBeginN_1D_FFT_4Threads() {
+ return THREADS_BEGIN_N_1D_FFT_4THREADS;
+ }
+
+ /**
+ * Returns the minimal size of 2D data for which threads are used.
+ *
+ * @return the minimal size of 2D data for which threads are used
+ */
+ public static int getThreadsBeginN_2D() {
+ return THREADS_BEGIN_N_2D;
+ }
+
+ /**
+ * Returns the minimal size of 3D data for which threads are used.
+ *
+ * @return the minimal size of 3D data for which threads are used
+ */
+ public static int getThreadsBeginN_3D() {
+ return THREADS_BEGIN_N_3D;
+ }
+
+ /**
+ * Sets the minimal size of 1D data for which two threads are used.
+ *
+ * @param n
+ * the minimal size of 1D data for which two threads are used
+ */
+ public static void setThreadsBeginN_1D_FFT_2Threads(int n) {
+ if (n < 512) {
+ THREADS_BEGIN_N_1D_FFT_2THREADS = 512;
+ } else {
+ THREADS_BEGIN_N_1D_FFT_2THREADS = n;
+ }
+ }
+
+ /**
+ * Sets the minimal size of 1D data for which four threads are used.
+ *
+ * @param n
+ * the minimal size of 1D data for which four threads are used
+ */
+ public static void setThreadsBeginN_1D_FFT_4Threads(int n) {
+ if (n < 512) {
+ THREADS_BEGIN_N_1D_FFT_4THREADS = 512;
+ } else {
+ THREADS_BEGIN_N_1D_FFT_4THREADS = n;
+ }
+ }
+
+ /**
+ * Sets the minimal size of 2D data for which threads are used.
+ *
+ * @param n
+ * the minimal size of 2D data for which threads are used
+ */
+ public static void setThreadsBeginN_2D(int n) {
+ THREADS_BEGIN_N_2D = n;
+ }
+
+ /**
+ * Sets the minimal size of 3D data for which threads are used.
+ *
+ * @param n
+ * the minimal size of 3D data for which threads are used
+ */
+ public static void setThreadsBeginN_3D(int n) {
+ THREADS_BEGIN_N_3D = n;
+ }
+
+ /**
+ * Resets the minimal size of 1D data for which two and four threads are
+ * used.
+ */
+ public static void resetThreadsBeginN_FFT() {
+ THREADS_BEGIN_N_1D_FFT_2THREADS = 8192;
+ THREADS_BEGIN_N_1D_FFT_4THREADS = 65536;
+ }
+
+ /**
+ * Resets the minimal size of 2D and 3D data for which threads are used.
+ */
+ public static void resetThreadsBeginN() {
+ THREADS_BEGIN_N_2D = 65536;
+ THREADS_BEGIN_N_3D = 65536;
+ }
+
+ /**
+ * Returns the closest power-of-two number greater than or equal to x.
+ *
+ * @param x the number to process
+ * @return the closest power-of-two number greater than or equal to x
+ */
+ public static int nextPow2(int x) {
+ if (x < 1)
+ throw new IllegalArgumentException("x must be greater or equal 1");
+ if ((x & (x - 1)) == 0) {
+ return x; // x is already a power-of-two number
+ }
+ x |= (x >>> 1);
+ x |= (x >>> 2);
+ x |= (x >>> 4);
+ x |= (x >>> 8);
+ x |= (x >>> 16);
+ x |= (x >>> 32);
+ return x + 1;
+ }
+
+ /**
+ * Returns the closest power-of-two number less than or equal to x.
+ *
+ * @param x the number to process
+ * @return the closest power-of-two number less then or equal to x
+ */
+ public static int prevPow2(int x) {
+ if (x < 1)
+ throw new IllegalArgumentException("x must be greater or equal 1");
+ return (int) Math.pow(2, Math.floor(Math.log(x) / Math.log(2)));
+ }
+
+ /**
+ * Checks if x is a power-of-two number.
+ *
+ * @param x the number to process
+ * @return true if x is a power-of-two number
+ */
+ public static boolean isPowerOf2(int x) {
+ if (x <= 0)
+ return false;
+ else
+ return (x & (x - 1)) == 0;
+ }
+
+ /**
+ * Causes the currently executing thread to sleep (temporarily cease
+ * execution) for the specified number of milliseconds.
+ *
+ * @param millis the number to millis to sleep
+ */
+ public static void sleep(long millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Submits a Runnable task for execution and returns a Future representing
+ * that task.
+ *
+ * @param task a Runnable task for execution
+ * @return a Future representing the task
+ */
+ public static Future> submit(Runnable task) {
+ return THREAD_POOL.submit(task);
+ }
+
+ /**
+ * Waits for all threads to complete computation.
+ *
+ * @param futures The futures which need completion.
+ */
+ public static void waitForCompletion(Future>[] futures) {
+ int size = futures.length;
+ try {
+ for (int j = 0; j < size; j++) {
+ futures[j].get();
+ }
+ } catch (ExecutionException ex) {
+ ex.printStackTrace();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/util/CubicSplineFast.java b/app/src/main/java/be/tarsos/dsp/util/CubicSplineFast.java
new file mode 100644
index 0000000..38540e7
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/util/CubicSplineFast.java
@@ -0,0 +1,187 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+/**********************************************************
+*
+* Class CubicSplineFast
+*
+* Class for performing an interpolation using a cubic spline
+* setTabulatedArrays and interpolate adapted, with modification to
+* an object-oriented approach, from Numerical Recipes in C (http://www.nr.com/)
+* Stripped down version of CubicSpline - all data checks have been removed for faster running
+*
+*
+* WRITTEN BY: Dr Michael Thomas Flanagan
+*
+* DATE: 26 December 2009 (Stripped down version of CubicSpline: May 2002 - 31 October 2009)
+* UPDATE: 14 January 2010
+*
+* DOCUMENTATION:
+* See Michael Thomas Flanagan's Java library on-LineWavelet web page:
+* http://www.ee.ucl.ac.uk/~mflanaga/java/CubicSplineFast.html
+* http://www.ee.ucl.ac.uk/~mflanaga/java/
+*
+* Copyright (c) 2002 - 2010 Michael Thomas Flanagan
+*
+* PERMISSION TO COPY:
+*
+* Permission to use, copy and modify this software and its documentation for NON-COMMERCIAL purposes is granted, without fee,
+* provided that an acknowledgement to the author, Dr Michael Thomas Flanagan at www.ee.ucl.ac.uk/~mflanaga, appears in all copies
+* and associated documentation or publications.
+*
+* Redistributions of the source code of this source code, or parts of the source codes, must retain the above copyright notice,
+* this list of conditions and the following disclaimer and requires written permission from the Michael Thomas Flanagan:
+*
+* Redistribution in binary form of all or parts of this class must reproduce the above copyright notice, this list of conditions and
+* the following disclaimer in the documentation and/or other materials provided with the distribution and requires written permission
+* from the Michael Thomas Flanagan:
+*
+* Dr Michael Thomas Flanagan makes no representations about the suitability or fitness of the software for any or for a particular purpose.
+* Dr Michael Thomas Flanagan shall not be liable for any damages suffered as a result of using, modifying or distributing this software
+* or its derivatives.
+*
+***************************************************************************************/
+
+
+package be.tarsos.dsp.util;
+
+/**
+ * Class for performing an interpolation using a cubic spline
+ * @author Dr Michael Thomas Flanagan
+ */
+public class CubicSplineFast{
+
+ private int nPoints = 0; // no. of tabulated points
+ private double[] y = null; // y=f(x) tabulated function
+ private double[] x = null; // x in tabulated function f(x)
+ private double[] d2ydx2 = null; // second derivatives of y
+
+ // Constructors
+ // Constructor with data arrays initialised to arrays x and y
+ public CubicSplineFast(double[] x, double[] y){
+ this.nPoints=x.length;
+ this.x = new double[nPoints];
+ this.y = new double[nPoints];
+ this.d2ydx2 = new double[nPoints];
+ for(int i=0; iSystem.getProperty("java.io.tmpdir")
.
+ *
+ * After downloading it makes the binary executable.
+ * The location of the downloaded binary is returned by ffmpegBinary();
+ *
+ * @author Joren Six
+ */
+public class FFMPEGDownloader {
+
+ private static String url = "https://0110.be/releases/TarsosDSP/TarsosDSP-static-ffmpeg/";
+
+ private final String ffmpegBinary;
+
+ private final static Logger LOG = Logger.getLogger(FFMPEGDownloader.class.getName());
+
+ /**
+ * A new FFMPEGDownloader
+ */
+ public FFMPEGDownloader(){
+ String filename = operatingSystemName() + "_" + processorArchitecture() + "_ffmpeg" + suffix();
+ url = url + filename;
+
+ String tempDirectory = System.getProperty("java.io.tmpdir");
+ String saveTo = new File(tempDirectory,filename).getAbsolutePath();
+
+ if(new File(saveTo).exists() && new File(saveTo).length() > 1000){
+ LOG.info("Found an already download ffmpeg static binary: " + saveTo);
+ ffmpegBinary = saveTo;
+ }else{
+ LOG.info("Started downloading an ffmpeg static binary from " + url + " to " + saveTo );
+ downloadExecutable(saveTo);
+
+ if(new File(saveTo).exists() && new File(saveTo).length() > 1000){
+ LOG.info("Downloaded an ffmpeg static binary. Stored at: " + saveTo);
+ //make it executable
+ new File(saveTo).setExecutable(true);
+ ffmpegBinary = saveTo;
+ }else{
+ //Unable to download or unknown architecture
+ LOG.warning("Unable to find or download an ffmpeg static binary. " + filename);
+ ffmpegBinary = null;
+ }
+ }
+ }
+
+ /**
+ * The path of the downloaded ffmpeg binary or null
+ * @return The path of the downloaded ffmpeg binary or null
+ */
+ public String ffmpegBinary(){
+ if(ffmpegBinary!=null){
+ return ffmpegBinary.replace(suffix(), "");
+ }
+ return null;
+ }
+
+ private void downloadExecutable(String saveTo){
+ try{
+ URL website = new URL(url);
+ ReadableByteChannel rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(saveTo);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ }catch(MalformedURLException e){
+ e.printStackTrace();
+ } catch (IOException e) {
+
+ e.printStackTrace();
+ }
+ }
+
+ private String operatingSystemName(){
+ String name;
+ String operatingSystem = System.getProperty("os.name").toLowerCase();
+ if(operatingSystem.indexOf("indows") > 0 ){
+ name = "windows";
+ }else if(operatingSystem.indexOf("nux") >= 0){
+ name="linux";
+ }else if(operatingSystem.indexOf("mac") >= 0){
+ name="mac_os_x";
+ }else{
+ name = null;
+ }
+ return name;
+ }
+
+ private String processorArchitecture(){
+ boolean is64bit = false;
+ if (System.getProperty("os.name").contains("Windows")) {
+ is64bit = (System.getenv("ProgramFiles(x86)") != null);
+ } else {
+ is64bit = (System.getProperty("os.arch").indexOf("64") != -1);
+ }
+ if(is64bit){
+ return "64_bits";
+ }else{
+ return "32_bits";
+ }
+ }
+
+ private String suffix(){
+ String suffix = "";
+ if (System.getProperty("os.name").contains("Windows")) {
+ suffix = ".exe";
+ }
+ return suffix;
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/util/PeakPicker.java b/app/src/main/java/be/tarsos/dsp/util/PeakPicker.java
new file mode 100644
index 0000000..37dbdf9
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/util/PeakPicker.java
@@ -0,0 +1,167 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.util;
+
+import java.util.Arrays;
+
+/**
+ * Implements a moving mean adaptive threshold peak picker.
+ *
+ * The implementation is a translation of peakpicker.c from Aubio, Copyright (C)
+ * 2003-2009 Paul Brossier piem@aubio.org
+ *
+ * @author Joren Six
+ * @author Paul Brossiers
+ */
+public class PeakPicker {
+ /** thresh: offset threshold [0.033 or 0.01] */
+ private double threshold;
+ /** win_post: median filter window length (causal part) [8] */
+ private final int win_post;
+ /** pre: median filter window (anti-causal part) [post-1] */
+ private final int win_pre;
+
+ /** biquad low pass filter */
+ private final BiQuadFilter biquad;
+
+ /** original onsets */
+ private final float[] onset_keep;
+ /** modified onsets */
+ private final float[] onset_proc;
+ /** peak picked window [3] */
+ private final float[] onset_peek;
+ /** scratch pad for biquad and median */
+ private final float[] scratch;
+
+ private float lastPeekValue;
+
+ /**
+ * Initializes a new moving mean adaptive threshold peak picker.
+ *
+ * @param threshold
+ * The threshold defines when a peak is selected. It should be
+ * between zero and one, 0.3 is a reasonable value. If too many
+ * peaks are detected go to 0.5 - 0.8.
+ */
+ public PeakPicker(double threshold) {
+ /* Low-pass filter cutoff [0.34, 1] */
+ biquad = new BiQuadFilter(0.1600,0.3200,0.1600,-0.5949,0.2348);
+ this.threshold = threshold;
+ win_post = 5;
+ win_pre = 1;
+
+ onset_keep = new float[win_post + win_pre +1];
+ onset_proc = new float[win_post + win_pre +1];
+ scratch = new float[win_post + win_pre +1];
+ onset_peek = new float[3];
+ }
+
+ /**
+ * Sets a new threshold.
+ *
+ * @param threshold
+ * The threshold defines when a peak is selected. It should be
+ * between zero and one, 0.3 is a reasonable value. If too many
+ * peaks are detected go to 0.5 - 0.8.
+ */
+ public void setThreshold(double threshold) {
+ this.threshold = threshold;
+ }
+
+ /**
+ * Modified version for real time, moving mean adaptive threshold this
+ * method is slightly more permissive than the off-LineWavelet one, and yields to
+ * an increase of false positives.
+ *
+ * @param onset
+ * The new onset value.
+ * @return True if a peak is detected, false otherwise.
+ **/
+ public boolean pickPeak(float onset) {
+ float mean = 0.f;
+ float median = 0.f;
+
+ int length = win_post + win_pre + 1;
+
+
+ /* store onset in onset_keep */
+ /* shift all elements but last, then write last */
+ /* for (i=0;i(12 * log2 (f / 440)) + 69
+ * E.g.
+ * 69.168 MIDI CENTS = MIDI NOTE 69 + 16,8 cents
+ * 69.168 MIDI CENTS = 440Hz + x Hz
+ *
+ * @param hertzValue
+ * The pitch in Hertz.
+ * @return The pitch in MIDI cent.
+ */
+ public static double hertzToMidiCent(final double hertzValue) {
+ double pitchInMidiCent = 0.0;
+ if (hertzValue != 0) {
+ pitchInMidiCent = 12 * Math.log(hertzValue / 440) / LOG_TWO + 69;
+ }
+ return pitchInMidiCent;
+ }
+
+ /**
+ * Converts a MIDI CENT frequency to a frequency in Hz.
+ *
+ * @param midiCent
+ * The pitch in MIDI CENT.
+ * @return The pitch in Hertz.
+ */
+ public static double midiCentToHertz(final double midiCent) {
+ return 440 * Math.pow(2, (midiCent - 69) / 12d);
+ }
+
+ /**
+ * Converts cent values to ratios. See
+ * "Ratios Make Cents: Conversions from ratios to cents and back again" in
+ * the book "Tuning Timbre Spectrum Scale" William A. Sethares.
+ *
+ * @param cent
+ * A cent value
+ * @return A ratio containing the same information.
+ */
+ public static double centToRatio(final double cent) {
+ final double ratio;
+ ratio = Math.pow(10, Math.log10(2) * cent / 1200.0);
+ return ratio;
+ }
+
+ /**
+ * Converts a ratio to cents.
+ * "Ratios Make Cents: Conversions from ratios to cents and back again" in
+ * the book "Tuning Timbre Spectrum Scale" William A. Sethares
+ *
+ * @param ratio
+ * A cent value
+ * @return A ratio containing the same information.
+ */
+ public static double ratioToCent(final double ratio) {
+ final double cent;
+ cent = 1200 / Math.log10(2) * Math.log10(ratio);
+ return cent;
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/util/fft/BartlettHannWindow.java b/app/src/main/java/be/tarsos/dsp/util/fft/BartlettHannWindow.java
new file mode 100644
index 0000000..37c6204
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/util/fft/BartlettHannWindow.java
@@ -0,0 +1,63 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+/*
+ * Copyright (c) 2007 - 2008 by Damien Di Fede 2*i
, the imaginary part 2*i+i
+ * @param data The array with imaginary numbers that is modified.
+ * @param other The array with imaginary numbers that is not modified.
+ * Data and other need to be the same length.
+ */
+ public void multiply(float[] data, float[] other){
+ assert data.length == other.length;
+ if(data.length!=other.length){
+ throw new IllegalArgumentException("Both arrays with imaginary numbers shouldb e of equal length");
+ }
+ for (int i = 1; i < data.length-1; i+=2) {
+ int realIndex = i;
+ int imgIndex = i + 1;
+ float tempReal = data[realIndex] * other[realIndex] + -1 * data[imgIndex] * other[imgIndex];
+ float tempImg = data[realIndex] * other[imgIndex] + data[imgIndex] * other[realIndex];
+ data[realIndex] = tempReal;
+ data[imgIndex] = tempImg;
+ //fix by perfecthu
+ //data[realIndex] = data[realIndex] * other[realIndex] + -1 * data[imgIndex] * other[imgIndex];
+ //data[imgIndex] = data[realIndex] * other[imgIndex] + data[imgIndex] * other[realIndex];
+ }
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/util/fft/FloatFFT.java b/app/src/main/java/be/tarsos/dsp/util/fft/FloatFFT.java
new file mode 100644
index 0000000..2b3b9c5
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/util/fft/FloatFFT.java
@@ -0,0 +1,6603 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is JTransforms.
+ *
+ * The Initial Developer of the Original Code is
+ * Piotr Wendykier, Emory University.
+ * Portions created by the Initial Developer are Copyright (C) 2007-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+package be.tarsos.dsp.util.fft;
+
+import java.util.concurrent.Future;
+
+import be.tarsos.dsp.util.ConcurrencyUtils;
+
+
+
+/**
+ * Computes 1D Discrete Fourier Transform (DFT) of complex and real, single
+ * precision data. The size of the data can be an arbitrary number. This is a
+ * parallel implementation of split-radix and mixed-radix algorithms optimized
+ * for SMP systems.
+ *
+ * This code is derived from General Purpose FFT Package written by Takuya Ooura
+ * (http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html) and from JFFTPack written
+ * by Baoshe Zhang (http://jfftpack.sourceforge.net/)
+ *
+ * @author Piotr Wendykier (piotr.wendykier@gmail.com)
+ *
+ */
+public class FloatFFT {
+
+ private enum Plans {
+ SPLIT_RADIX, MIXED_RADIX, BLUESTEIN
+ }
+
+ private final int n;
+
+ private int nBluestein;
+
+ private int[] ip;
+
+ private float[] w;
+
+ private int nw;
+
+ private int nc;
+
+ private float[] wtable;
+
+ private float[] wtable_r;
+
+ private float[] bk1;
+
+ private float[] bk2;
+
+ private final Plans plan;
+
+ private static final int[] factors = { 4, 2, 3, 5 };
+
+ private static final float PI = 3.14159265358979311599796346854418516f;
+
+ private static final float TWO_PI = 6.28318530717958623199592693708837032f;
+
+ /**
+ * Creates new instance of FloatFFT.
+ *
+ * @param n
+ * size of data
+ */
+ public FloatFFT(int n) {
+ if (n < 1) {
+ throw new IllegalArgumentException("n must be greater than 0");
+ }
+ this.n = n;
+
+ if (!ConcurrencyUtils.isPowerOf2(n)) {
+ if (getReminder(n, factors) >= 211) {
+ plan = Plans.BLUESTEIN;
+ nBluestein = ConcurrencyUtils.nextPow2(n * 2 - 1);
+ bk1 = new float[2 * nBluestein];
+ bk2 = new float[2 * nBluestein];
+ this.ip = new int[2 + (int) Math.ceil(2 + (1 << (int) (Math.log(nBluestein + 0.5) / Math.log(2)) / 2))];
+ this.w = new float[nBluestein];
+ int twon = 2 * nBluestein;
+ nw = ip[0];
+ if (twon > (nw << 2)) {
+ nw = twon >> 2;
+ makewt(nw);
+ }
+ nc = ip[1];
+ if (nBluestein > (nc << 2)) {
+ nc = nBluestein >> 2;
+ makect(nc, w, nw);
+ }
+ bluesteini();
+ } else {
+ plan = Plans.MIXED_RADIX;
+ wtable = new float[4 * n + 15];
+ wtable_r = new float[2 * n + 15];
+ cffti();
+ rffti();
+ }
+ } else {
+ plan = Plans.SPLIT_RADIX;
+ this.ip = new int[2 + (int) Math.ceil(2 + (1 << (int) (Math.log(n + 0.5) / Math.log(2)) / 2))];
+ this.w = new float[n];
+ int twon = 2 * n;
+ nw = ip[0];
+ if (twon > (nw << 2)) {
+ nw = twon >> 2;
+ makewt(nw);
+ }
+ nc = ip[1];
+ if (n > (nc << 2)) {
+ nc = n >> 2;
+ makect(nc, w, nw);
+ }
+ }
+ }
+
+ /**
+ * Computes 1D forward DFT of complex data leaving the result in
+ * a
. Complex number is stored as two float values in
+ * sequence: the real and imaginary part, i.e. the size of the input array
+ * must be greater or equal 2*n. The physical layout of the input data has
+ * to be as follows:
+ *
+ *
+ * a[2*k] = Re[k],
+ * a[2*k+1] = Im[k], 0<=k<n
+ *
+ *
+ * @param a
+ * data to transform
+ */
+ public void complexForward(float[] a) {
+ complexForward(a, 0);
+ }
+
+ /**
+ * Computes 1D forward DFT of complex data leaving the result in
+ * a
. Complex number is stored as two float values in
+ * sequence: the real and imaginary part, i.e. the size of the input array
+ * must be greater or equal 2*n. The physical layout of the input data has
+ * to be as follows:
+ *
+ *
+ * a[offa+2*k] = Re[k],
+ * a[offa+2*k+1] = Im[k], 0<=k<n
+ *
+ *
+ * @param a
+ * data to transform
+ * @param offa
+ * index of the first element in array a
+ */
+ public void complexForward(float[] a, int offa) {
+ if (n == 1)
+ return;
+ switch (plan) {
+ case SPLIT_RADIX:
+ cftbsub(2 * n, a, offa, ip, nw, w);
+ break;
+ case MIXED_RADIX:
+ cfftf(a, offa, -1);
+ break;
+ case BLUESTEIN:
+ bluestein_complex(a, offa, -1);
+ break;
+ }
+ }
+
+ /**
+ * Computes 1D inverse DFT of complex data leaving the result in
+ * a
. Complex number is stored as two float values in
+ * sequence: the real and imaginary part, i.e. the size of the input array
+ * must be greater or equal 2*n. The physical layout of the input data has
+ * to be as follows:
+ *
+ *
+ * a[2*k] = Re[k],
+ * a[2*k+1] = Im[k], 0<=k<n
+ *
+ *
+ * @param a
+ * data to transform
+ * @param scale
+ * if true then scaling is performed
+ */
+ public void complexInverse(float[] a, boolean scale) {
+ complexInverse(a, 0, scale);
+ }
+
+ /**
+ * Computes 1D inverse DFT of complex data leaving the result in
+ * a
. Complex number is stored as two float values in
+ * sequence: the real and imaginary part, i.e. the size of the input array
+ * must be greater or equal 2*n. The physical layout of the input data has
+ * to be as follows:
+ *
+ *
+ * a[offa+2*k] = Re[k],
+ * a[offa+2*k+1] = Im[k], 0<=k<n
+ *
+ *
+ * @param a
+ * data to transform
+ * @param offa
+ * index of the first element in array a
+ * @param scale
+ * if true then scaling is performed
+ */
+ public void complexInverse(float[] a, int offa, boolean scale) {
+ if (n == 1)
+ return;
+ switch (plan) {
+ case SPLIT_RADIX:
+ cftfsub(2 * n, a, offa, ip, nw, w);
+ break;
+ case MIXED_RADIX:
+ cfftf(a, offa, +1);
+ break;
+ case BLUESTEIN:
+ bluestein_complex(a, offa, 1);
+ break;
+ }
+ if (scale) {
+ scale(n, a, offa, true);
+ }
+ }
+
+ /**
+ * Computes 1D forward DFT of real data leaving the result in a
+ * . The physical layout of the output data is as follows:
+ *
+ * if n is even then
+ *
+ *
+ * a[2*k] = Re[k], 0<=k<n/2
+ * a[2*k+1] = Im[k], 0<k<n/2
+ * a[1] = Re[n/2]
+ *
+ *
+ * if n is odd then
+ *
+ *
+ * a[2*k] = Re[k], 0<=k<(n+1)/2
+ * a[2*k+1] = Im[k], 0<k<(n-1)/2
+ * a[1] = Im[(n-1)/2]
+ *
+ *
+ * This method computes only half of the elements of the real transform. The
+ * other half satisfies the symmetry condition. If you want the full real
+ * forward transform, use realForwardFull
. To get back the
+ * original data, use realInverse
on the output of this method.
+ *
+ * @param a
+ * data to transform
+ */
+ public void realForward(float[] a) {
+ realForward(a, 0);
+ }
+
+ /**
+ * Computes 1D forward DFT of real data leaving the result in a
+ * . The physical layout of the output data is as follows:
+ *
+ * if n is even then
+ *
+ *
+ * a[offa+2*k] = Re[k], 0<=k<n/2
+ * a[offa+2*k+1] = Im[k], 0<k<n/2
+ * a[offa+1] = Re[n/2]
+ *
+ *
+ * if n is odd then
+ *
+ *
+ * a[offa+2*k] = Re[k], 0<=k<(n+1)/2
+ * a[offa+2*k+1] = Im[k], 0<k<(n-1)/2
+ * a[offa+1] = Im[(n-1)/2]
+ *
+ *
+ * This method computes only half of the elements of the real transform. The
+ * other half satisfies the symmetry condition. If you want the full real
+ * forward transform, use realForwardFull
. To get back the
+ * original data, use realInverse
on the output of this method.
+ *
+ * @param a
+ * data to transform
+ * @param offa
+ * index of the first element in array a
+ */
+ public void realForward(float[] a, int offa) {
+ if (n == 1)
+ return;
+
+ switch (plan) {
+ case SPLIT_RADIX:
+ float xi;
+
+ if (n > 4) {
+ cftfsub(n, a, offa, ip, nw, w);
+ rftfsub(n, a, offa, nc, w, nw);
+ } else if (n == 4) {
+ cftx020(a, offa);
+ }
+ xi = a[offa] - a[offa + 1];
+ a[offa] += a[offa + 1];
+ a[offa + 1] = xi;
+ break;
+ case MIXED_RADIX:
+ rfftf(a, offa);
+ for (int k = n - 1; k >= 2; k--) {
+ int idx = offa + k;
+ float tmp = a[idx];
+ a[idx] = a[idx - 1];
+ a[idx - 1] = tmp;
+ }
+ break;
+ case BLUESTEIN:
+ bluestein_real_forward(a, offa);
+ break;
+ }
+ }
+
+ /**
+ * Computes 1D forward DFT of real data leaving the result in a
+ * . This method computes the full real forward transform, i.e. you will get
+ * the same result as from complexForward
called with all
+ * imaginary parts equal 0. Because the result is stored in a
,
+ * the size of the input array must greater or equal 2*n, with only the
+ * first n elements filled with real data. To get back the original data,
+ * use complexInverse
on the output of this method.
+ *
+ * @param a
+ * data to transform
+ */
+ public void realForwardFull(float[] a) {
+ realForwardFull(a, 0);
+ }
+
+ /**
+ * Computes 1D forward DFT of real data leaving the result in a
+ * . This method computes the full real forward transform, i.e. you will get
+ * the same result as from complexForward
called with all
+ * imaginary part equal 0. Because the result is stored in a
,
+ * the size of the input array must greater or equal 2*n, with only the
+ * first n elements filled with real data. To get back the original data,
+ * use complexInverse
on the output of this method.
+ *
+ * @param a
+ * data to transform
+ * @param offa
+ * index of the first element in array a
+ */
+ public void realForwardFull(final float[] a, final int offa) {
+
+ final int twon = 2 * n;
+ switch (plan) {
+ case SPLIT_RADIX:
+ realForward(a, offa);
+ int nthreads = ConcurrencyUtils.getNumberOfThreads();
+ if ((nthreads > 1) && (n / 2 > ConcurrencyUtils.getThreadsBeginN_1D_FFT_2Threads())) {
+ Future>[] futures = new Future[nthreads];
+ int k = n / 2 / nthreads;
+ for (int i = 0; i < nthreads; i++) {
+ final int firstIdx = i * k;
+ final int lastIdx = (i == (nthreads - 1)) ? n / 2 : firstIdx + k;
+ futures[i] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ int idx1, idx2;
+ for (int k = firstIdx; k < lastIdx; k++) {
+ idx1 = 2 * k;
+ idx2 = offa + ((twon - idx1) % twon);
+ a[idx2] = a[offa + idx1];
+ a[idx2 + 1] = -a[offa + idx1 + 1];
+ }
+ }
+ });
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+ } else {
+ int idx1, idx2;
+ for (int k = 0; k < n / 2; k++) {
+ idx1 = 2 * k;
+ idx2 = offa + ((twon - idx1) % twon);
+ a[idx2] = a[offa + idx1];
+ a[idx2 + 1] = -a[offa + idx1 + 1];
+ }
+ }
+ a[offa + n] = -a[offa + 1];
+ a[offa + 1] = 0;
+ break;
+ case MIXED_RADIX:
+ rfftf(a, offa);
+ int m;
+ if (n % 2 == 0) {
+ m = n / 2;
+ } else {
+ m = (n + 1) / 2;
+ }
+ for (int k = 1; k < m; k++) {
+ int idx1 = offa + twon - 2 * k;
+ int idx2 = offa + 2 * k;
+ a[idx1 + 1] = -a[idx2];
+ a[idx1] = a[idx2 - 1];
+ }
+ for (int k = 1; k < n; k++) {
+ int idx = offa + n - k;
+ float tmp = a[idx + 1];
+ a[idx + 1] = a[idx];
+ a[idx] = tmp;
+ }
+ a[offa + 1] = 0;
+ break;
+ case BLUESTEIN:
+ bluestein_real_full(a, offa, -1);
+ break;
+ }
+ }
+
+ /**
+ * Computes 1D inverse DFT of real data leaving the result in a
+ * . The physical layout of the input data has to be as follows:
+ *
+ * if n is even then
+ *
+ *
+ * a[2*k] = Re[k], 0<=k<n/2
+ * a[2*k+1] = Im[k], 0<k<n/2
+ * a[1] = Re[n/2]
+ *
+ *
+ * if n is odd then
+ *
+ *
+ * a[2*k] = Re[k], 0<=k<(n+1)/2
+ * a[2*k+1] = Im[k], 0<k<(n-1)/2
+ * a[1] = Im[(n-1)/2]
+ *
+ *
+ * This method computes only half of the elements of the real transform. The
+ * other half satisfies the symmetry condition. If you want the full real
+ * inverse transform, use realInverseFull
.
+ *
+ * @param a
+ * data to transform
+ *
+ * @param scale
+ * if true then scaling is performed
+ *
+ */
+ public void realInverse(float[] a, boolean scale) {
+ realInverse(a, 0, scale);
+ }
+
+ /**
+ * Computes 1D inverse DFT of real data leaving the result in a
+ * . The physical layout of the input data has to be as follows:
+ *
+ * if n is even then
+ *
+ *
+ * a[offa+2*k] = Re[k], 0<=k<n/2
+ * a[offa+2*k+1] = Im[k], 0<k<n/2
+ * a[offa+1] = Re[n/2]
+ *
+ *
+ * if n is odd then
+ *
+ *
+ * a[offa+2*k] = Re[k], 0<=k<(n+1)/2
+ * a[offa+2*k+1] = Im[k], 0<k<(n-1)/2
+ * a[offa+1] = Im[(n-1)/2]
+ *
+ *
+ * This method computes only half of the elements of the real transform. The
+ * other half satisfies the symmetry condition. If you want the full real
+ * inverse transform, use realInverseFull
.
+ *
+ * @param a
+ * data to transform
+ * @param offa
+ * index of the first element in array a
+ * @param scale
+ * if true then scaling is performed
+ *
+ */
+ public void realInverse(float[] a, int offa, boolean scale) {
+ if (n == 1)
+ return;
+ switch (plan) {
+ case SPLIT_RADIX:
+ a[offa + 1] = (float)(0.5 * (a[offa] - a[offa + 1]));
+ a[offa] -= a[offa + 1];
+ if (n > 4) {
+ rftfsub(n, a, offa, nc, w, nw);
+ cftbsub(n, a, offa, ip, nw, w);
+ } else if (n == 4) {
+ cftxc020(a, offa);
+ }
+ if (scale) {
+ scale(n / 2, a, offa, false);
+ }
+ break;
+ case MIXED_RADIX:
+ for (int k = 2; k < n; k++) {
+ int idx = offa + k;
+ float tmp = a[idx - 1];
+ a[idx - 1] = a[idx];
+ a[idx] = tmp;
+ }
+ rfftb(a, offa);
+ if (scale) {
+ scale(n, a, offa, false);
+ }
+ break;
+ case BLUESTEIN:
+ bluestein_real_inverse(a, offa);
+ if (scale) {
+ scale(n, a, offa, false);
+ }
+ break;
+ }
+
+ }
+
+ /**
+ * Computes 1D inverse DFT of real data leaving the result in a
+ * . This method computes the full real inverse transform, i.e. you will get
+ * the same result as from complexInverse
called with all
+ * imaginary part equal 0. Because the result is stored in a
,
+ * the size of the input array must greater or equal 2*n, with only the
+ * first n elements filled with real data.
+ *
+ * @param a
+ * data to transform
+ * @param scale
+ * if true then scaling is performed
+ */
+ public void realInverseFull(float[] a, boolean scale) {
+ realInverseFull(a, 0, scale);
+ }
+
+ /**
+ * Computes 1D inverse DFT of real data leaving the result in a
+ * . This method computes the full real inverse transform, i.e. you will get
+ * the same result as from complexInverse
called with all
+ * imaginary part equal 0. Because the result is stored in a
,
+ * the size of the input array must greater or equal 2*n, with only the
+ * first n elements filled with real data.
+ *
+ * @param a
+ * data to transform
+ * @param offa
+ * index of the first element in array a
+ * @param scale
+ * if true then scaling is performed
+ */
+ public void realInverseFull(final float[] a, final int offa, boolean scale) {
+ final int twon = 2 * n;
+ switch (plan) {
+ case SPLIT_RADIX:
+ realInverse2(a, offa, scale);
+ int nthreads = ConcurrencyUtils.getNumberOfThreads();
+ if ((nthreads > 1) && (n / 2 > ConcurrencyUtils.getThreadsBeginN_1D_FFT_2Threads())) {
+ Future>[] futures = new Future[nthreads];
+ int k = n / 2 / nthreads;
+ for (int i = 0; i < nthreads; i++) {
+ final int firstIdx = i * k;
+ final int lastIdx = (i == (nthreads - 1)) ? n / 2 : firstIdx + k;
+ futures[i] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ int idx1, idx2;
+ for (int k = firstIdx; k < lastIdx; k++) {
+ idx1 = 2 * k;
+ idx2 = offa + ((twon - idx1) % twon);
+ a[idx2] = a[offa + idx1];
+ a[idx2 + 1] = -a[offa + idx1 + 1];
+ }
+ }
+ });
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+ } else {
+ int idx1, idx2;
+ for (int k = 0; k < n / 2; k++) {
+ idx1 = 2 * k;
+ idx2 = offa + ((twon - idx1) % twon);
+ a[idx2] = a[offa + idx1];
+ a[idx2 + 1] = -a[offa + idx1 + 1];
+ }
+ }
+ a[offa + n] = -a[offa + 1];
+ a[offa + 1] = 0;
+ break;
+ case MIXED_RADIX:
+ rfftf(a, offa);
+ if (scale) {
+ scale(n, a, offa, false);
+ }
+ int m;
+ if (n % 2 == 0) {
+ m = n / 2;
+ } else {
+ m = (n + 1) / 2;
+ }
+ for (int k = 1; k < m; k++) {
+ int idx1 = offa + 2 * k;
+ int idx2 = offa + twon - 2 * k;
+ a[idx1] = -a[idx1];
+ a[idx2 + 1] = -a[idx1];
+ a[idx2] = a[idx1 - 1];
+ }
+ for (int k = 1; k < n; k++) {
+ int idx = offa + n - k;
+ float tmp = a[idx + 1];
+ a[idx + 1] = a[idx];
+ a[idx] = tmp;
+ }
+ a[offa + 1] = 0;
+ break;
+ case BLUESTEIN:
+ bluestein_real_full(a, offa, 1);
+ if (scale) {
+ scale(n, a, offa, true);
+ }
+ break;
+ }
+ }
+
+ private void realInverse2(float[] a, int offa, boolean scale) {
+ if (n == 1)
+ return;
+ switch (plan) {
+ case SPLIT_RADIX:
+ float xi;
+
+ if (n > 4) {
+ cftfsub(n, a, offa, ip, nw, w);
+ rftbsub(n, a, offa, nc, w, nw);
+ } else if (n == 4) {
+ cftbsub(n, a, offa, ip, nw, w);
+ }
+ xi = a[offa] - a[offa + 1];
+ a[offa] += a[offa + 1];
+ a[offa + 1] = xi;
+ if (scale) {
+ scale(n, a, offa, false);
+ }
+ break;
+ case MIXED_RADIX:
+ rfftf(a, offa);
+ for (int k = n - 1; k >= 2; k--) {
+ int idx = offa + k;
+ float tmp = a[idx];
+ a[idx] = a[idx - 1];
+ a[idx - 1] = tmp;
+ }
+ if (scale) {
+ scale(n, a, offa, false);
+ }
+ int m;
+ if (n % 2 == 0) {
+ m = n / 2;
+ for (int i = 1; i < m; i++) {
+ int idx = offa + 2 * i + 1;
+ a[idx] = -a[idx];
+ }
+ } else {
+ m = (n - 1) / 2;
+ for (int i = 0; i < m; i++) {
+ int idx = offa + 2 * i + 1;
+ a[idx] = -a[idx];
+ }
+ }
+ break;
+ case BLUESTEIN:
+ bluestein_real_inverse2(a, offa);
+ if (scale) {
+ scale(n, a, offa, false);
+ }
+ break;
+ }
+ }
+
+ private static int getReminder(int n, int[] factors) {
+ int reminder = n;
+
+ if (n <= 0) {
+ throw new IllegalArgumentException("n must be positive integer");
+ }
+
+ for (int i = 0; i < factors.length && reminder != 1; i++) {
+ int factor = factors[i];
+ while ((reminder % factor) == 0) {
+ reminder /= factor;
+ }
+ }
+ return reminder;
+ }
+
+ /* -------- initializing routines -------- */
+
+ /*---------------------------------------------------------
+ cffti: initialization of Complex FFT
+ --------------------------------------------------------*/
+
+ void cffti(int n, int offw) {
+ if (n == 1)
+ return;
+
+ final int twon = 2 * n;
+ final int fourn = 4 * n;
+ float argh;
+ int idot, ntry = 0, i, j;
+ float argld;
+ int i1, k1, l1, l2, ib;
+ float fi;
+ int ld, ii, nf, ip, nl, nq, nr;
+ float arg;
+ int ido, ipm;
+
+ nl = n;
+ nf = 0;
+ j = 0;
+
+ factorize_loop: while (true) {
+ j++;
+ if (j <= 4)
+ ntry = factors[j - 1];
+ else
+ ntry += 2;
+ do {
+ nq = nl / ntry;
+ nr = nl - ntry * nq;
+ if (nr != 0)
+ continue factorize_loop;
+ nf++;
+ wtable[offw + nf + 1 + fourn] = ntry;
+ nl = nq;
+ if (ntry == 2 && nf != 1) {
+ for (i = 2; i <= nf; i++) {
+ ib = nf - i + 2;
+ int idx = ib + fourn;
+ wtable[offw + idx + 1] = wtable[offw + idx];
+ }
+ wtable[offw + 2 + fourn] = 2;
+ }
+ } while (nl != 1);
+ break;
+ }
+ wtable[offw + fourn] = n;
+ wtable[offw + 1 + fourn] = nf;
+ argh = TWO_PI / (float) n;
+ i = 1;
+ l1 = 1;
+ for (k1 = 1; k1 <= nf; k1++) {
+ ip = (int) wtable[offw + k1 + 1 + fourn];
+ ld = 0;
+ l2 = l1 * ip;
+ ido = n / l2;
+ idot = ido + ido + 2;
+ ipm = ip - 1;
+ for (j = 1; j <= ipm; j++) {
+ i1 = i;
+ wtable[offw + i - 1 + twon] = 1;
+ wtable[offw + i + twon] = 0;
+ ld += l1;
+ fi = 0;
+ argld = ld * argh;
+ for (ii = 4; ii <= idot; ii += 2) {
+ i += 2;
+ fi += 1;
+ arg = fi * argld;
+ int idx = i + twon;
+ wtable[offw + idx - 1] = (float)Math.cos(arg);
+ wtable[offw + idx] = (float)Math.sin(arg);
+ }
+ if (ip > 5) {
+ int idx1 = i1 + twon;
+ int idx2 = i + twon;
+ wtable[offw + idx1 - 1] = wtable[offw + idx2 - 1];
+ wtable[offw + idx1] = wtable[offw + idx2];
+ }
+ }
+ l1 = l2;
+ }
+
+ }
+
+ void cffti() {
+ if (n == 1)
+ return;
+
+ final int twon = 2 * n;
+ final int fourn = 4 * n;
+ float argh;
+ int idot, ntry = 0, i, j;
+ float argld;
+ int i1, k1, l1, l2, ib;
+ float fi;
+ int ld, ii, nf, ip, nl, nq, nr;
+ float arg;
+ int ido, ipm;
+
+ nl = n;
+ nf = 0;
+ j = 0;
+
+ factorize_loop: while (true) {
+ j++;
+ if (j <= 4)
+ ntry = factors[j - 1];
+ else
+ ntry += 2;
+ do {
+ nq = nl / ntry;
+ nr = nl - ntry * nq;
+ if (nr != 0)
+ continue factorize_loop;
+ nf++;
+ wtable[nf + 1 + fourn] = ntry;
+ nl = nq;
+ if (ntry == 2 && nf != 1) {
+ for (i = 2; i <= nf; i++) {
+ ib = nf - i + 2;
+ int idx = ib + fourn;
+ wtable[idx + 1] = wtable[idx];
+ }
+ wtable[2 + fourn] = 2;
+ }
+ } while (nl != 1);
+ break;
+ }
+ wtable[fourn] = n;
+ wtable[1 + fourn] = nf;
+ argh = TWO_PI / (float) n;
+ i = 1;
+ l1 = 1;
+ for (k1 = 1; k1 <= nf; k1++) {
+ ip = (int) wtable[k1 + 1 + fourn];
+ ld = 0;
+ l2 = l1 * ip;
+ ido = n / l2;
+ idot = ido + ido + 2;
+ ipm = ip - 1;
+ for (j = 1; j <= ipm; j++) {
+ i1 = i;
+ wtable[i - 1 + twon] = 1;
+ wtable[i + twon] = 0;
+ ld += l1;
+ fi = 0;
+ argld = ld * argh;
+ for (ii = 4; ii <= idot; ii += 2) {
+ i += 2;
+ fi += 1;
+ arg = fi * argld;
+ int idx = i + twon;
+ wtable[idx - 1] = (float)Math.cos(arg);
+ wtable[idx] = (float)Math.sin(arg);
+ }
+ if (ip > 5) {
+ int idx1 = i1 + twon;
+ int idx2 = i + twon;
+ wtable[idx1 - 1] = wtable[idx2 - 1];
+ wtable[idx1] = wtable[idx2];
+ }
+ }
+ l1 = l2;
+ }
+
+ }
+
+ void rffti() {
+
+ if (n == 1)
+ return;
+ final int twon = 2 * n;
+ float argh;
+ int ntry = 0, i, j;
+ float argld;
+ int k1, l1, l2, ib;
+ float fi;
+ int ld, ii, nf, ip, nl, is, nq, nr;
+ float arg;
+ int ido, ipm;
+ int nfm1;
+
+ nl = n;
+ nf = 0;
+ j = 0;
+
+ factorize_loop: while (true) {
+ ++j;
+ if (j <= 4)
+ ntry = factors[j - 1];
+ else
+ ntry += 2;
+ do {
+ nq = nl / ntry;
+ nr = nl - ntry * nq;
+ if (nr != 0)
+ continue factorize_loop;
+ ++nf;
+ wtable_r[nf + 1 + twon] = ntry;
+
+ nl = nq;
+ if (ntry == 2 && nf != 1) {
+ for (i = 2; i <= nf; i++) {
+ ib = nf - i + 2;
+ int idx = ib + twon;
+ wtable_r[idx + 1] = wtable_r[idx];
+ }
+ wtable_r[2 + twon] = 2;
+ }
+ } while (nl != 1);
+ break;
+ }
+ wtable_r[twon] = n;
+ wtable_r[1 + twon] = nf;
+ argh = TWO_PI / (float) (n);
+ is = 0;
+ nfm1 = nf - 1;
+ l1 = 1;
+ if (nfm1 == 0)
+ return;
+ for (k1 = 1; k1 <= nfm1; k1++) {
+ ip = (int) wtable_r[k1 + 1 + twon];
+ ld = 0;
+ l2 = l1 * ip;
+ ido = n / l2;
+ ipm = ip - 1;
+ for (j = 1; j <= ipm; ++j) {
+ ld += l1;
+ i = is;
+ argld = (float) ld * argh;
+
+ fi = 0;
+ for (ii = 3; ii <= ido; ii += 2) {
+ i += 2;
+ fi += 1;
+ arg = fi * argld;
+ int idx = i + n;
+ wtable_r[idx - 2] = (float)Math.cos(arg);
+ wtable_r[idx - 1] = (float)Math.sin(arg);
+ }
+ is += ido;
+ }
+ l1 = l2;
+ }
+ }
+
+ private void bluesteini() {
+ int k = 0;
+ float arg;
+ float pi_n = PI / n;
+ bk1[0] = 1;
+ bk1[1] = 0;
+ for (int i = 1; i < n; i++) {
+ k += 2 * i - 1;
+ if (k >= 2 * n)
+ k -= 2 * n;
+ arg = pi_n * k;
+ bk1[2 * i] = (float)Math.cos(arg);
+ bk1[2 * i + 1] = (float)Math.sin(arg);
+ }
+ float scale = (float)(1.0 / nBluestein);
+ bk2[0] = bk1[0] * scale;
+ bk2[1] = bk1[1] * scale;
+ for (int i = 2; i < 2 * n; i += 2) {
+ bk2[i] = bk1[i] * scale;
+ bk2[i + 1] = bk1[i + 1] * scale;
+ bk2[2 * nBluestein - i] = bk2[i];
+ bk2[2 * nBluestein - i + 1] = bk2[i + 1];
+ }
+ cftbsub(2 * nBluestein, bk2, 0, ip, nw, w);
+ }
+
+ private void makewt(int nw) {
+ int j, nwh, nw0, nw1;
+ float delta, wn4r, wk1r, wk1i, wk3r, wk3i;
+ float delta2, deltaj, deltaj3;
+
+ ip[0] = nw;
+ ip[1] = 1;
+ if (nw > 2) {
+ nwh = nw >> 1;
+ delta = (float)(0.785398163397448278999490867136046290 / nwh);
+ delta2 = delta * 2;
+ wn4r = (float)Math.cos(delta * nwh);
+ w[0] = 1;
+ w[1] = wn4r;
+ if (nwh == 4) {
+ w[2] = (float)Math.cos(delta2);
+ w[3] = (float)Math.sin(delta2);
+ } else if (nwh > 4) {
+ makeipt(nw);
+ w[2] = (float)(0.5 / Math.cos(delta2));
+ w[3] = (float)(0.5 / Math.cos(delta * 6));
+ for (j = 4; j < nwh; j += 4) {
+ deltaj = delta * j;
+ deltaj3 = 3 * deltaj;
+ w[j] = (float)Math.cos(deltaj);
+ w[j + 1] = (float)Math.sin(deltaj);
+ w[j + 2] = (float)Math.cos(deltaj3);
+ w[j + 3] = (float)-Math.sin(deltaj3);
+ }
+ }
+ nw0 = 0;
+ while (nwh > 2) {
+ nw1 = nw0 + nwh;
+ nwh >>= 1;
+ w[nw1] = 1;
+ w[nw1 + 1] = wn4r;
+ if (nwh == 4) {
+ wk1r = w[nw0 + 4];
+ wk1i = w[nw0 + 5];
+ w[nw1 + 2] = wk1r;
+ w[nw1 + 3] = wk1i;
+ } else if (nwh > 4) {
+ wk1r = w[nw0 + 4];
+ wk3r = w[nw0 + 6];
+ w[nw1 + 2] = (float)(0.5 / wk1r);
+ w[nw1 + 3] = (float)(0.5 / wk3r);
+ for (j = 4; j < nwh; j += 4) {
+ int idx1 = nw0 + 2 * j;
+ int idx2 = nw1 + j;
+ wk1r = w[idx1];
+ wk1i = w[idx1 + 1];
+ wk3r = w[idx1 + 2];
+ wk3i = w[idx1 + 3];
+ w[idx2] = wk1r;
+ w[idx2 + 1] = wk1i;
+ w[idx2 + 2] = wk3r;
+ w[idx2 + 3] = wk3i;
+ }
+ }
+ nw0 = nw1;
+ }
+ }
+ }
+
+ private void makeipt(int nw) {
+ int j, l, m, m2, p, q;
+
+ ip[2] = 0;
+ ip[3] = 16;
+ m = 2;
+ for (l = nw; l > 32; l >>= 2) {
+ m2 = m << 1;
+ q = m2 << 3;
+ for (j = m; j < m2; j++) {
+ p = ip[j] << 2;
+ ip[m + j] = p;
+ ip[m2 + j] = p + q;
+ }
+ m = m2;
+ }
+ }
+
+ private void makect(int nc, float[] c, int startc) {
+ int j, nch;
+ float delta, deltaj;
+
+ ip[1] = nc;
+ if (nc > 1) {
+ nch = nc >> 1;
+ delta = (float)(0.785398163397448278999490867136046290 / nch);
+ c[startc] = (float)Math.cos(delta * nch);
+ c[startc + nch] = (float)(0.5 * c[startc]);
+ for (j = 1; j < nch; j++) {
+ deltaj = delta * j;
+ c[startc + j] = (float)(0.5 * Math.cos(deltaj));
+ c[startc + nc - j] = (float)(0.5 * Math.sin(deltaj));
+ }
+ }
+ }
+
+ private void bluestein_complex(final float[] a, final int offa, final int isign) {
+ final float[] ak = new float[2 * nBluestein];
+ int nthreads = ConcurrencyUtils.getNumberOfThreads();
+ if ((nthreads > 1) && (n > ConcurrencyUtils.getThreadsBeginN_1D_FFT_2Threads())) {
+ nthreads = 2;
+ if ((nthreads >= 4) && (n > ConcurrencyUtils.getThreadsBeginN_1D_FFT_4Threads())) {
+ nthreads = 4;
+ }
+ Future>[] futures = new Future[nthreads];
+ int k = n / nthreads;
+ for (int i = 0; i < nthreads; i++) {
+ final int firstIdx = i * k;
+ final int lastIdx = (i == (nthreads - 1)) ? n : firstIdx + k;
+ futures[i] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ if (isign > 0) {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + idx1;
+ int idx4 = offa + idx2;
+ ak[idx1] = a[idx3] * bk1[idx1] - a[idx4] * bk1[idx2];
+ ak[idx2] = a[idx3] * bk1[idx2] + a[idx4] * bk1[idx1];
+ }
+ } else {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + idx1;
+ int idx4 = offa + idx2;
+ ak[idx1] = a[idx3] * bk1[idx1] + a[idx4] * bk1[idx2];
+ ak[idx2] = -a[idx3] * bk1[idx2] + a[idx4] * bk1[idx1];
+ }
+ }
+ }
+ });
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+
+ cftbsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ k = nBluestein / nthreads;
+ for (int i = 0; i < nthreads; i++) {
+ final int firstIdx = i * k;
+ final int lastIdx = (i == (nthreads - 1)) ? nBluestein : firstIdx + k;
+ futures[i] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ if (isign > 0) {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ float im = -ak[idx1] * bk2[idx2] + ak[idx2] * bk2[idx1];
+ ak[idx1] = ak[idx1] * bk2[idx1] + ak[idx2] * bk2[idx2];
+ ak[idx2] = im;
+ }
+ } else {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ float im = ak[idx1] * bk2[idx2] + ak[idx2] * bk2[idx1];
+ ak[idx1] = ak[idx1] * bk2[idx1] - ak[idx2] * bk2[idx2];
+ ak[idx2] = im;
+ }
+ }
+ }
+ });
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+
+ cftfsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ k = n / nthreads;
+ for (int i = 0; i < nthreads; i++) {
+ final int firstIdx = i * k;
+ final int lastIdx = (i == (nthreads - 1)) ? n : firstIdx + k;
+ futures[i] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ if (isign > 0) {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + idx1;
+ int idx4 = offa + idx2;
+ a[idx3] = bk1[idx1] * ak[idx1] - bk1[idx2] * ak[idx2];
+ a[idx4] = bk1[idx2] * ak[idx1] + bk1[idx1] * ak[idx2];
+ }
+ } else {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + idx1;
+ int idx4 = offa + idx2;
+ a[idx3] = bk1[idx1] * ak[idx1] + bk1[idx2] * ak[idx2];
+ a[idx4] = -bk1[idx2] * ak[idx1] + bk1[idx1] * ak[idx2];
+ }
+ }
+ }
+ });
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+ } else {
+ if (isign > 0) {
+ for (int i = 0; i < n; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + idx1;
+ int idx4 = offa + idx2;
+ ak[idx1] = a[idx3] * bk1[idx1] - a[idx4] * bk1[idx2];
+ ak[idx2] = a[idx3] * bk1[idx2] + a[idx4] * bk1[idx1];
+ }
+ } else {
+ for (int i = 0; i < n; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + idx1;
+ int idx4 = offa + idx2;
+ ak[idx1] = a[idx3] * bk1[idx1] + a[idx4] * bk1[idx2];
+ ak[idx2] = -a[idx3] * bk1[idx2] + a[idx4] * bk1[idx1];
+ }
+ }
+
+ cftbsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ if (isign > 0) {
+ for (int i = 0; i < nBluestein; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ float im = -ak[idx1] * bk2[idx2] + ak[idx2] * bk2[idx1];
+ ak[idx1] = ak[idx1] * bk2[idx1] + ak[idx2] * bk2[idx2];
+ ak[idx2] = im;
+ }
+ } else {
+ for (int i = 0; i < nBluestein; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ float im = ak[idx1] * bk2[idx2] + ak[idx2] * bk2[idx1];
+ ak[idx1] = ak[idx1] * bk2[idx1] - ak[idx2] * bk2[idx2];
+ ak[idx2] = im;
+ }
+ }
+
+ cftfsub(2 * nBluestein, ak, 0, ip, nw, w);
+ if (isign > 0) {
+ for (int i = 0; i < n; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + idx1;
+ int idx4 = offa + idx2;
+ a[idx3] = bk1[idx1] * ak[idx1] - bk1[idx2] * ak[idx2];
+ a[idx4] = bk1[idx2] * ak[idx1] + bk1[idx1] * ak[idx2];
+ }
+ } else {
+ for (int i = 0; i < n; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + idx1;
+ int idx4 = offa + idx2;
+ a[idx3] = bk1[idx1] * ak[idx1] + bk1[idx2] * ak[idx2];
+ a[idx4] = -bk1[idx2] * ak[idx1] + bk1[idx1] * ak[idx2];
+ }
+ }
+ }
+ }
+
+ private void bluestein_real_full(final float[] a, final int offa, final int isign) {
+ final float[] ak = new float[2 * nBluestein];
+ int nthreads = ConcurrencyUtils.getNumberOfThreads();
+ if ((nthreads > 1) && (n > ConcurrencyUtils.getThreadsBeginN_1D_FFT_2Threads())) {
+ nthreads = 2;
+ if ((nthreads >= 4) && (n > ConcurrencyUtils.getThreadsBeginN_1D_FFT_4Threads())) {
+ nthreads = 4;
+ }
+ Future>[] futures = new Future[nthreads];
+ int k = n / nthreads;
+ for (int i = 0; i < nthreads; i++) {
+ final int firstIdx = i * k;
+ final int lastIdx = (i == (nthreads - 1)) ? n : firstIdx + k;
+ futures[i] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ if (isign > 0) {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + i;
+ ak[idx1] = a[idx3] * bk1[idx1];
+ ak[idx2] = a[idx3] * bk1[idx2];
+ }
+ } else {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + i;
+ ak[idx1] = a[idx3] * bk1[idx1];
+ ak[idx2] = -a[idx3] * bk1[idx2];
+ }
+ }
+ }
+ });
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+
+ cftbsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ k = nBluestein / nthreads;
+ for (int i = 0; i < nthreads; i++) {
+ final int firstIdx = i * k;
+ final int lastIdx = (i == (nthreads - 1)) ? nBluestein : firstIdx + k;
+ futures[i] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ if (isign > 0) {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ float im = -ak[idx1] * bk2[idx2] + ak[idx2] * bk2[idx1];
+ ak[idx1] = ak[idx1] * bk2[idx1] + ak[idx2] * bk2[idx2];
+ ak[idx2] = im;
+ }
+ } else {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ float im = ak[idx1] * bk2[idx2] + ak[idx2] * bk2[idx1];
+ ak[idx1] = ak[idx1] * bk2[idx1] - ak[idx2] * bk2[idx2];
+ ak[idx2] = im;
+ }
+ }
+ }
+ });
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+
+ cftfsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ k = n / nthreads;
+ for (int i = 0; i < nthreads; i++) {
+ final int firstIdx = i * k;
+ final int lastIdx = (i == (nthreads - 1)) ? n : firstIdx + k;
+ futures[i] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ if (isign > 0) {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ a[offa + idx1] = bk1[idx1] * ak[idx1] - bk1[idx2] * ak[idx2];
+ a[offa + idx2] = bk1[idx2] * ak[idx1] + bk1[idx1] * ak[idx2];
+ }
+ } else {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ a[offa + idx1] = bk1[idx1] * ak[idx1] + bk1[idx2] * ak[idx2];
+ a[offa + idx2] = -bk1[idx2] * ak[idx1] + bk1[idx1] * ak[idx2];
+ }
+ }
+ }
+ });
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+ } else {
+ if (isign > 0) {
+ for (int i = 0; i < n; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + i;
+ ak[idx1] = a[idx3] * bk1[idx1];
+ ak[idx2] = a[idx3] * bk1[idx2];
+ }
+ } else {
+ for (int i = 0; i < n; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + i;
+ ak[idx1] = a[idx3] * bk1[idx1];
+ ak[idx2] = -a[idx3] * bk1[idx2];
+ }
+ }
+
+ cftbsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ if (isign > 0) {
+ for (int i = 0; i < nBluestein; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ float im = -ak[idx1] * bk2[idx2] + ak[idx2] * bk2[idx1];
+ ak[idx1] = ak[idx1] * bk2[idx1] + ak[idx2] * bk2[idx2];
+ ak[idx2] = im;
+ }
+ } else {
+ for (int i = 0; i < nBluestein; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ float im = ak[idx1] * bk2[idx2] + ak[idx2] * bk2[idx1];
+ ak[idx1] = ak[idx1] * bk2[idx1] - ak[idx2] * bk2[idx2];
+ ak[idx2] = im;
+ }
+ }
+
+ cftfsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ if (isign > 0) {
+ for (int i = 0; i < n; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ a[offa + idx1] = bk1[idx1] * ak[idx1] - bk1[idx2] * ak[idx2];
+ a[offa + idx2] = bk1[idx2] * ak[idx1] + bk1[idx1] * ak[idx2];
+ }
+ } else {
+ for (int i = 0; i < n; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ a[offa + idx1] = bk1[idx1] * ak[idx1] + bk1[idx2] * ak[idx2];
+ a[offa + idx2] = -bk1[idx2] * ak[idx1] + bk1[idx1] * ak[idx2];
+ }
+ }
+ }
+ }
+
+ private void bluestein_real_forward(final float[] a, final int offa) {
+ final float[] ak = new float[2 * nBluestein];
+ int nthreads = ConcurrencyUtils.getNumberOfThreads();
+ if ((nthreads > 1) && (n > ConcurrencyUtils.getThreadsBeginN_1D_FFT_2Threads())) {
+ nthreads = 2;
+ if ((nthreads >= 4) && (n > ConcurrencyUtils.getThreadsBeginN_1D_FFT_4Threads())) {
+ nthreads = 4;
+ }
+ Future>[] futures = new Future[nthreads];
+ int k = n / nthreads;
+ for (int i = 0; i < nthreads; i++) {
+ final int firstIdx = i * k;
+ final int lastIdx = (i == (nthreads - 1)) ? n : firstIdx + k;
+ futures[i] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + i;
+ ak[idx1] = a[idx3] * bk1[idx1];
+ ak[idx2] = -a[idx3] * bk1[idx2];
+ }
+ }
+ });
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+
+ cftbsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ k = nBluestein / nthreads;
+ for (int i = 0; i < nthreads; i++) {
+ final int firstIdx = i * k;
+ final int lastIdx = (i == (nthreads - 1)) ? nBluestein : firstIdx + k;
+ futures[i] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ float im = ak[idx1] * bk2[idx2] + ak[idx2] * bk2[idx1];
+ ak[idx1] = ak[idx1] * bk2[idx1] - ak[idx2] * bk2[idx2];
+ ak[idx2] = im;
+ }
+ }
+ });
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+
+ } else {
+
+ for (int i = 0; i < n; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + i;
+ ak[idx1] = a[idx3] * bk1[idx1];
+ ak[idx2] = -a[idx3] * bk1[idx2];
+ }
+
+ cftbsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ for (int i = 0; i < nBluestein; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ float im = ak[idx1] * bk2[idx2] + ak[idx2] * bk2[idx1];
+ ak[idx1] = ak[idx1] * bk2[idx1] - ak[idx2] * bk2[idx2];
+ ak[idx2] = im;
+ }
+ }
+
+ cftfsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ if (n % 2 == 0) {
+ a[offa] = bk1[0] * ak[0] + bk1[1] * ak[1];
+ a[offa + 1] = bk1[n] * ak[n] + bk1[n + 1] * ak[n + 1];
+ for (int i = 1; i < n / 2; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ a[offa + idx1] = bk1[idx1] * ak[idx1] + bk1[idx2] * ak[idx2];
+ a[offa + idx2] = -bk1[idx2] * ak[idx1] + bk1[idx1] * ak[idx2];
+ }
+ } else {
+ a[offa] = bk1[0] * ak[0] + bk1[1] * ak[1];
+ a[offa + 1] = -bk1[n] * ak[n - 1] + bk1[n - 1] * ak[n];
+ for (int i = 1; i < (n - 1) / 2; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ a[offa + idx1] = bk1[idx1] * ak[idx1] + bk1[idx2] * ak[idx2];
+ a[offa + idx2] = -bk1[idx2] * ak[idx1] + bk1[idx1] * ak[idx2];
+ }
+ a[offa + n - 1] = bk1[n - 1] * ak[n - 1] + bk1[n] * ak[n];
+ }
+
+ }
+
+ private void bluestein_real_inverse(final float[] a, final int offa) {
+ final float[] ak = new float[2 * nBluestein];
+ if (n % 2 == 0) {
+ ak[0] = a[offa] * bk1[0];
+ ak[1] = a[offa] * bk1[1];
+
+ for (int i = 1; i < n / 2; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + idx1;
+ int idx4 = offa + idx2;
+ ak[idx1] = a[idx3] * bk1[idx1] - a[idx4] * bk1[idx2];
+ ak[idx2] = a[idx3] * bk1[idx2] + a[idx4] * bk1[idx1];
+ }
+
+ ak[n] = a[offa + 1] * bk1[n];
+ ak[n + 1] = a[offa + 1] * bk1[n + 1];
+
+ for (int i = n / 2 + 1; i < n; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + 2 * n - idx1;
+ int idx4 = idx3 + 1;
+ ak[idx1] = a[idx3] * bk1[idx1] + a[idx4] * bk1[idx2];
+ ak[idx2] = a[idx3] * bk1[idx2] - a[idx4] * bk1[idx1];
+ }
+
+ } else {
+ ak[0] = a[offa] * bk1[0];
+ ak[1] = a[offa] * bk1[1];
+
+ for (int i = 1; i < (n - 1) / 2; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + idx1;
+ int idx4 = offa + idx2;
+ ak[idx1] = a[idx3] * bk1[idx1] - a[idx4] * bk1[idx2];
+ ak[idx2] = a[idx3] * bk1[idx2] + a[idx4] * bk1[idx1];
+ }
+
+ ak[n - 1] = a[offa + n - 1] * bk1[n - 1] - a[offa + 1] * bk1[n];
+ ak[n] = a[offa + n - 1] * bk1[n] + a[offa + 1] * bk1[n - 1];
+
+ ak[n + 1] = a[offa + n - 1] * bk1[n + 1] + a[offa + 1] * bk1[n + 2];
+ ak[n + 2] = a[offa + n - 1] * bk1[n + 2] - a[offa + 1] * bk1[n + 1];
+
+ for (int i = (n - 1) / 2 + 2; i < n; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + 2 * n - idx1;
+ int idx4 = idx3 + 1;
+ ak[idx1] = a[idx3] * bk1[idx1] + a[idx4] * bk1[idx2];
+ ak[idx2] = a[idx3] * bk1[idx2] - a[idx4] * bk1[idx1];
+ }
+ }
+
+ cftbsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ int nthreads = ConcurrencyUtils.getNumberOfThreads();
+ if ((nthreads > 1) && (n > ConcurrencyUtils.getThreadsBeginN_1D_FFT_2Threads())) {
+ nthreads = 2;
+ if ((nthreads >= 4) && (n > ConcurrencyUtils.getThreadsBeginN_1D_FFT_4Threads())) {
+ nthreads = 4;
+ }
+ Future>[] futures = new Future[nthreads];
+ int k = nBluestein / nthreads;
+ for (int i = 0; i < nthreads; i++) {
+ final int firstIdx = i * k;
+ final int lastIdx = (i == (nthreads - 1)) ? nBluestein : firstIdx + k;
+ futures[i] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ float im = -ak[idx1] * bk2[idx2] + ak[idx2] * bk2[idx1];
+ ak[idx1] = ak[idx1] * bk2[idx1] + ak[idx2] * bk2[idx2];
+ ak[idx2] = im;
+ }
+ }
+ });
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+
+ cftfsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ k = n / nthreads;
+ for (int i = 0; i < nthreads; i++) {
+ final int firstIdx = i * k;
+ final int lastIdx = (i == (nthreads - 1)) ? n : firstIdx + k;
+ futures[i] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ a[offa + i] = bk1[idx1] * ak[idx1] - bk1[idx2] * ak[idx2];
+ }
+ }
+ });
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+
+ } else {
+
+ for (int i = 0; i < nBluestein; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ float im = -ak[idx1] * bk2[idx2] + ak[idx2] * bk2[idx1];
+ ak[idx1] = ak[idx1] * bk2[idx1] + ak[idx2] * bk2[idx2];
+ ak[idx2] = im;
+ }
+
+ cftfsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ for (int i = 0; i < n; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ a[offa + i] = bk1[idx1] * ak[idx1] - bk1[idx2] * ak[idx2];
+ }
+ }
+ }
+
+ private void bluestein_real_inverse2(final float[] a, final int offa) {
+ final float[] ak = new float[2 * nBluestein];
+ int nthreads = ConcurrencyUtils.getNumberOfThreads();
+ if ((nthreads > 1) && (n > ConcurrencyUtils.getThreadsBeginN_1D_FFT_2Threads())) {
+ nthreads = 2;
+ if ((nthreads >= 4) && (n > ConcurrencyUtils.getThreadsBeginN_1D_FFT_4Threads())) {
+ nthreads = 4;
+ }
+ Future>[] futures = new Future[nthreads];
+ int k = n / nthreads;
+ for (int i = 0; i < nthreads; i++) {
+ final int firstIdx = i * k;
+ final int lastIdx = (i == (nthreads - 1)) ? n : firstIdx + k;
+ futures[i] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + i;
+ ak[idx1] = a[idx3] * bk1[idx1];
+ ak[idx2] = a[idx3] * bk1[idx2];
+ }
+ }
+ });
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+
+ cftbsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ k = nBluestein / nthreads;
+ for (int i = 0; i < nthreads; i++) {
+ final int firstIdx = i * k;
+ final int lastIdx = (i == (nthreads - 1)) ? nBluestein : firstIdx + k;
+ futures[i] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ float im = -ak[idx1] * bk2[idx2] + ak[idx2] * bk2[idx1];
+ ak[idx1] = ak[idx1] * bk2[idx1] + ak[idx2] * bk2[idx2];
+ ak[idx2] = im;
+ }
+ }
+ });
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+
+ } else {
+
+ for (int i = 0; i < n; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ int idx3 = offa + i;
+ ak[idx1] = a[idx3] * bk1[idx1];
+ ak[idx2] = a[idx3] * bk1[idx2];
+ }
+
+ cftbsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ for (int i = 0; i < nBluestein; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ float im = -ak[idx1] * bk2[idx2] + ak[idx2] * bk2[idx1];
+ ak[idx1] = ak[idx1] * bk2[idx1] + ak[idx2] * bk2[idx2];
+ ak[idx2] = im;
+ }
+ }
+
+ cftfsub(2 * nBluestein, ak, 0, ip, nw, w);
+
+ if (n % 2 == 0) {
+ a[offa] = bk1[0] * ak[0] - bk1[1] * ak[1];
+ a[offa + 1] = bk1[n] * ak[n] - bk1[n + 1] * ak[n + 1];
+ for (int i = 1; i < n / 2; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ a[offa + idx1] = bk1[idx1] * ak[idx1] - bk1[idx2] * ak[idx2];
+ a[offa + idx2] = bk1[idx2] * ak[idx1] + bk1[idx1] * ak[idx2];
+ }
+ } else {
+ a[offa] = bk1[0] * ak[0] - bk1[1] * ak[1];
+ a[offa + 1] = bk1[n] * ak[n - 1] + bk1[n - 1] * ak[n];
+ for (int i = 1; i < (n - 1) / 2; i++) {
+ int idx1 = 2 * i;
+ int idx2 = idx1 + 1;
+ a[offa + idx1] = bk1[idx1] * ak[idx1] - bk1[idx2] * ak[idx2];
+ a[offa + idx2] = bk1[idx2] * ak[idx1] + bk1[idx1] * ak[idx2];
+ }
+ a[offa + n - 1] = bk1[n - 1] * ak[n - 1] - bk1[n] * ak[n];
+ }
+ }
+
+ /*---------------------------------------------------------
+ rfftf1: further processing of Real forward FFT
+ --------------------------------------------------------*/
+ void rfftf(final float[] a, final int offa) {
+ if (n == 1)
+ return;
+ int l1, l2, na, kh, nf, ip, iw, ido, idl1;
+
+ final float[] ch = new float[n];
+ final int twon = 2 * n;
+ nf = (int) wtable_r[1 + twon];
+ na = 1;
+ l2 = n;
+ iw = twon - 1;
+ for (int k1 = 1; k1 <= nf; ++k1) {
+ kh = nf - k1;
+ ip = (int) wtable_r[kh + 2 + twon];
+ l1 = l2 / ip;
+ ido = n / l2;
+ idl1 = ido * l1;
+ iw -= (ip - 1) * ido;
+ na = 1 - na;
+ switch (ip) {
+ case 2:
+ if (na == 0) {
+ radf2(ido, l1, a, offa, ch, 0, iw);
+ } else {
+ radf2(ido, l1, ch, 0, a, offa, iw);
+ }
+ break;
+ case 3:
+ if (na == 0) {
+ radf3(ido, l1, a, offa, ch, 0, iw);
+ } else {
+ radf3(ido, l1, ch, 0, a, offa, iw);
+ }
+ break;
+ case 4:
+ if (na == 0) {
+ radf4(ido, l1, a, offa, ch, 0, iw);
+ } else {
+ radf4(ido, l1, ch, 0, a, offa, iw);
+ }
+ break;
+ case 5:
+ if (na == 0) {
+ radf5(ido, l1, a, offa, ch, 0, iw);
+ } else {
+ radf5(ido, l1, ch, 0, a, offa, iw);
+ }
+ break;
+ default:
+ if (ido == 1)
+ na = 1 - na;
+ if (na == 0) {
+ radfg(ido, ip, l1, idl1, a, offa, ch, 0, iw);
+ na = 1;
+ } else {
+ radfg(ido, ip, l1, idl1, ch, 0, a, offa, iw);
+ na = 0;
+ }
+ break;
+ }
+ l2 = l1;
+ }
+ if (na == 1)
+ return;
+ System.arraycopy(ch, 0, a, offa, n);
+ }
+
+ /*---------------------------------------------------------
+ rfftb1: further processing of Real backward FFT
+ --------------------------------------------------------*/
+ void rfftb(final float[] a, final int offa) {
+ if (n == 1)
+ return;
+ int l1, l2, na, nf, ip, iw, ido, idl1;
+
+ float[] ch = new float[n];
+ final int twon = 2 * n;
+ nf = (int) wtable_r[1 + twon];
+ na = 0;
+ l1 = 1;
+ iw = n;
+ for (int k1 = 1; k1 <= nf; k1++) {
+ ip = (int) wtable_r[k1 + 1 + twon];
+ l2 = ip * l1;
+ ido = n / l2;
+ idl1 = ido * l1;
+ switch (ip) {
+ case 2:
+ if (na == 0) {
+ radb2(ido, l1, a, offa, ch, 0, iw);
+ } else {
+ radb2(ido, l1, ch, 0, a, offa, iw);
+ }
+ na = 1 - na;
+ break;
+ case 3:
+ if (na == 0) {
+ radb3(ido, l1, a, offa, ch, 0, iw);
+ } else {
+ radb3(ido, l1, ch, 0, a, offa, iw);
+ }
+ na = 1 - na;
+ break;
+ case 4:
+ if (na == 0) {
+ radb4(ido, l1, a, offa, ch, 0, iw);
+ } else {
+ radb4(ido, l1, ch, 0, a, offa, iw);
+ }
+ na = 1 - na;
+ break;
+ case 5:
+ if (na == 0) {
+ radb5(ido, l1, a, offa, ch, 0, iw);
+ } else {
+ radb5(ido, l1, ch, 0, a, offa, iw);
+ }
+ na = 1 - na;
+ break;
+ default:
+ if (na == 0) {
+ radbg(ido, ip, l1, idl1, a, offa, ch, 0, iw);
+ } else {
+ radbg(ido, ip, l1, idl1, ch, 0, a, offa, iw);
+ }
+ if (ido == 1)
+ na = 1 - na;
+ break;
+ }
+ l1 = l2;
+ iw += (ip - 1) * ido;
+ }
+ if (na == 0)
+ return;
+ System.arraycopy(ch, 0, a, offa, n);
+ }
+
+ /*-------------------------------------------------
+ radf2: Real FFT's forward processing of factor 2
+ -------------------------------------------------*/
+ void radf2(final int ido, final int l1, final float[] in, final int in_off, final float[] out, final int out_off, final int offset) {
+ int i, ic, idx0, idx1, idx2, idx3, idx4;
+ float t1i, t1r, w1r, w1i;
+ int iw1;
+ iw1 = offset;
+ idx0 = l1 * ido;
+ idx1 = 2 * ido;
+ for (int k = 0; k < l1; k++) {
+ int oidx1 = out_off + k * idx1;
+ int oidx2 = oidx1 + idx1 - 1;
+ int iidx1 = in_off + k * ido;
+ int iidx2 = iidx1 + idx0;
+
+ float i1r = in[iidx1];
+ float i2r = in[iidx2];
+
+ out[oidx1] = i1r + i2r;
+ out[oidx2] = i1r - i2r;
+ }
+ if (ido < 2)
+ return;
+ if (ido != 2) {
+ for (int k = 0; k < l1; k++) {
+ idx1 = k * ido;
+ idx2 = 2 * idx1;
+ idx3 = idx2 + ido;
+ idx4 = idx1 + idx0;
+ for (i = 2; i < ido; i += 2) {
+ ic = ido - i;
+ int widx1 = i - 1 + iw1;
+ int oidx1 = out_off + i + idx2;
+ int oidx2 = out_off + ic + idx3;
+ int iidx1 = in_off + i + idx1;
+ int iidx2 = in_off + i + idx4;
+
+ float a1i = in[iidx1 - 1];
+ float a1r = in[iidx1];
+ float a2i = in[iidx2 - 1];
+ float a2r = in[iidx2];
+
+ w1r = wtable_r[widx1 - 1];
+ w1i = wtable_r[widx1];
+
+ t1r = w1r * a2i + w1i * a2r;
+ t1i = w1r * a2r - w1i * a2i;
+
+ out[oidx1] = a1r + t1i;
+ out[oidx1 - 1] = a1i + t1r;
+
+ out[oidx2] = t1i - a1r;
+ out[oidx2 - 1] = a1i - t1r;
+ }
+ }
+ if (ido % 2 == 1)
+ return;
+ }
+ idx2 = 2 * idx1;
+ for (int k = 0; k < l1; k++) {
+ idx1 = k * ido;
+ int oidx1 = out_off + idx2 + ido;
+ int iidx1 = in_off + ido - 1 + idx1;
+
+ out[oidx1] = -in[iidx1 + idx0];
+ out[oidx1 - 1] = in[iidx1];
+ }
+ }
+
+ /*-------------------------------------------------
+ radb2: Real FFT's backward processing of factor 2
+ -------------------------------------------------*/
+ void radb2(final int ido, final int l1, final float[] in, final int in_off, final float[] out, final int out_off, final int offset) {
+ int i, ic;
+ float t1i, t1r, w1r, w1i;
+ int iw1 = offset;
+
+ int idx0 = l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int idx2 = 2 * idx1;
+ int idx3 = idx2 + ido;
+ int oidx1 = out_off + idx1;
+ int iidx1 = in_off + idx2;
+ int iidx2 = in_off + ido - 1 + idx3;
+ float i1r = in[iidx1];
+ float i2r = in[iidx2];
+ out[oidx1] = i1r + i2r;
+ out[oidx1 + idx0] = i1r - i2r;
+ }
+ if (ido < 2)
+ return;
+ if (ido != 2) {
+ for (int k = 0; k < l1; ++k) {
+ int idx1 = k * ido;
+ int idx2 = 2 * idx1;
+ int idx3 = idx2 + ido;
+ int idx4 = idx1 + idx0;
+ for (i = 2; i < ido; i += 2) {
+ ic = ido - i;
+ int idx5 = i - 1 + iw1;
+ int idx6 = out_off + i;
+ int idx7 = in_off + i;
+ int idx8 = in_off + ic;
+ w1r = wtable_r[idx5 - 1];
+ w1i = wtable_r[idx5];
+ int iidx1 = idx7 + idx2;
+ int iidx2 = idx8 + idx3;
+ int oidx1 = idx6 + idx1;
+ int oidx2 = idx6 + idx4;
+ t1r = in[iidx1 - 1] - in[iidx2 - 1];
+ t1i = in[iidx1] + in[iidx2];
+ float i1i = in[iidx1];
+ float i1r = in[iidx1 - 1];
+ float i2i = in[iidx2];
+ float i2r = in[iidx2 - 1];
+
+ out[oidx1 - 1] = i1r + i2r;
+ out[oidx1] = i1i - i2i;
+ out[oidx2 - 1] = w1r * t1r - w1i * t1i;
+ out[oidx2] = w1r * t1i + w1i * t1r;
+ }
+ }
+ if (ido % 2 == 1)
+ return;
+ }
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int idx2 = 2 * idx1;
+ int oidx1 = out_off + ido - 1 + idx1;
+ int iidx1 = in_off + idx2 + ido;
+ out[oidx1] = 2 * in[iidx1 - 1];
+ out[oidx1 + idx0] = -2 * in[iidx1];
+ }
+ }
+
+ /*-------------------------------------------------
+ radf3: Real FFT's forward processing of factor 3
+ -------------------------------------------------*/
+ void radf3(final int ido, final int l1, final float[] in, final int in_off, final float[] out, final int out_off, final int offset) {
+ final float taur = -0.5f;
+ final float taui = 0.866025403784438707610604524234076962f;
+ int i, ic;
+ float ci2, di2, di3, cr2, dr2, dr3, ti2, ti3, tr2, tr3, w1r, w2r, w1i, w2i;
+ int iw1, iw2;
+ iw1 = offset;
+ iw2 = iw1 + ido;
+
+ int idx0 = l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int idx3 = 2 * idx0;
+ int idx4 = (3 * k + 1) * ido;
+ int iidx1 = in_off + idx1;
+ int iidx2 = iidx1 + idx0;
+ int iidx3 = iidx1 + idx3;
+ float i1r = in[iidx1];
+ float i2r = in[iidx2];
+ float i3r = in[iidx3];
+ cr2 = i2r + i3r;
+ out[out_off + 3 * idx1] = i1r + cr2;
+ out[out_off + idx4 + ido] = taui * (i3r - i2r);
+ out[out_off + ido - 1 + idx4] = i1r + taur * cr2;
+ }
+ if (ido == 1)
+ return;
+ for (int k = 0; k < l1; k++) {
+ int idx3 = k * ido;
+ int idx4 = 3 * idx3;
+ int idx5 = idx3 + idx0;
+ int idx6 = idx5 + idx0;
+ int idx7 = idx4 + ido;
+ int idx8 = idx7 + ido;
+ for (i = 2; i < ido; i += 2) {
+ ic = ido - i;
+ int widx1 = i - 1 + iw1;
+ int widx2 = i - 1 + iw2;
+
+ w1r = wtable_r[widx1 - 1];
+ w1i = wtable_r[widx1];
+ w2r = wtable_r[widx2 - 1];
+ w2i = wtable_r[widx2];
+
+ int idx9 = in_off + i;
+ int idx10 = out_off + i;
+ int idx11 = out_off + ic;
+ int iidx1 = idx9 + idx3;
+ int iidx2 = idx9 + idx5;
+ int iidx3 = idx9 + idx6;
+
+ float i1i = in[iidx1 - 1];
+ float i1r = in[iidx1];
+ float i2i = in[iidx2 - 1];
+ float i2r = in[iidx2];
+ float i3i = in[iidx3 - 1];
+ float i3r = in[iidx3];
+
+ dr2 = w1r * i2i + w1i * i2r;
+ di2 = w1r * i2r - w1i * i2i;
+ dr3 = w2r * i3i + w2i * i3r;
+ di3 = w2r * i3r - w2i * i3i;
+ cr2 = dr2 + dr3;
+ ci2 = di2 + di3;
+ tr2 = i1i + taur * cr2;
+ ti2 = i1r + taur * ci2;
+ tr3 = taui * (di2 - di3);
+ ti3 = taui * (dr3 - dr2);
+
+ int oidx1 = idx10 + idx4;
+ int oidx2 = idx11 + idx7;
+ int oidx3 = idx10 + idx8;
+
+ out[oidx1 - 1] = i1i + cr2;
+ out[oidx1] = i1r + ci2;
+ out[oidx2 - 1] = tr2 - tr3;
+ out[oidx2] = ti3 - ti2;
+ out[oidx3 - 1] = tr2 + tr3;
+ out[oidx3] = ti2 + ti3;
+ }
+ }
+ }
+
+ /*-------------------------------------------------
+ radb3: Real FFT's backward processing of factor 3
+ -------------------------------------------------*/
+ void radb3(final int ido, final int l1, final float[] in, final int in_off, final float[] out, final int out_off, final int offset) {
+ final float taur = -0.5f;
+ final float taui = 0.866025403784438707610604524234076962f;
+ int i, ic;
+ float ci2, ci3, di2, di3, cr2, cr3, dr2, dr3, ti2, tr2, w1r, w2r, w1i, w2i;
+ int iw1, iw2;
+ iw1 = offset;
+ iw2 = iw1 + ido;
+
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int iidx1 = in_off + 3 * idx1;
+ int iidx2 = iidx1 + 2 * ido;
+ float i1i = in[iidx1];
+
+ tr2 = 2 * in[iidx2 - 1];
+ cr2 = i1i + taur * tr2;
+ ci3 = 2 * taui * in[iidx2];
+
+ out[out_off + idx1] = i1i + tr2;
+ out[out_off + (k + l1) * ido] = cr2 - ci3;
+ out[out_off + (k + 2 * l1) * ido] = cr2 + ci3;
+ }
+ if (ido == 1)
+ return;
+ int idx0 = l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int idx2 = 3 * idx1;
+ int idx3 = idx2 + ido;
+ int idx4 = idx3 + ido;
+ int idx5 = idx1 + idx0;
+ int idx6 = idx5 + idx0;
+ for (i = 2; i < ido; i += 2) {
+ ic = ido - i;
+ int idx7 = in_off + i;
+ int idx8 = in_off + ic;
+ int idx9 = out_off + i;
+ int iidx1 = idx7 + idx2;
+ int iidx2 = idx7 + idx4;
+ int iidx3 = idx8 + idx3;
+
+ float i1i = in[iidx1 - 1];
+ float i1r = in[iidx1];
+ float i2i = in[iidx2 - 1];
+ float i2r = in[iidx2];
+ float i3i = in[iidx3 - 1];
+ float i3r = in[iidx3];
+
+ tr2 = i2i + i3i;
+ cr2 = i1i + taur * tr2;
+ ti2 = i2r - i3r;
+ ci2 = i1r + taur * ti2;
+ cr3 = taui * (i2i - i3i);
+ ci3 = taui * (i2r + i3r);
+ dr2 = cr2 - ci3;
+ dr3 = cr2 + ci3;
+ di2 = ci2 + cr3;
+ di3 = ci2 - cr3;
+
+ int widx1 = i - 1 + iw1;
+ int widx2 = i - 1 + iw2;
+
+ w1r = wtable_r[widx1 - 1];
+ w1i = wtable_r[widx1];
+ w2r = wtable_r[widx2 - 1];
+ w2i = wtable_r[widx2];
+
+ int oidx1 = idx9 + idx1;
+ int oidx2 = idx9 + idx5;
+ int oidx3 = idx9 + idx6;
+
+ out[oidx1 - 1] = i1i + tr2;
+ out[oidx1] = i1r + ti2;
+ out[oidx2 - 1] = w1r * dr2 - w1i * di2;
+ out[oidx2] = w1r * di2 + w1i * dr2;
+ out[oidx3 - 1] = w2r * dr3 - w2i * di3;
+ out[oidx3] = w2r * di3 + w2i * dr3;
+ }
+ }
+ }
+
+ /*-------------------------------------------------
+ radf4: Real FFT's forward processing of factor 4
+ -------------------------------------------------*/
+ void radf4(final int ido, final int l1, final float[] in, final int in_off, final float[] out, final int out_off, final int offset) {
+ final float hsqt2 = 0.707106781186547572737310929369414225f;
+ int i, ic;
+ float ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4, w1r, w1i, w2r, w2i, w3r, w3i;
+ int iw1, iw2, iw3;
+ iw1 = offset;
+ iw2 = offset + ido;
+ iw3 = iw2 + ido;
+ int idx0 = l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int idx2 = 4 * idx1;
+ int idx3 = idx1 + idx0;
+ int idx4 = idx3 + idx0;
+ int idx5 = idx4 + idx0;
+ int idx6 = idx2 + ido;
+ float i1r = in[in_off + idx1];
+ float i2r = in[in_off + idx3];
+ float i3r = in[in_off + idx4];
+ float i4r = in[in_off + idx5];
+
+ tr1 = i2r + i4r;
+ tr2 = i1r + i3r;
+
+ int oidx1 = out_off + idx2;
+ int oidx2 = out_off + idx6 + ido;
+
+ out[oidx1] = tr1 + tr2;
+ out[oidx2 - 1 + ido + ido] = tr2 - tr1;
+ out[oidx2 - 1] = i1r - i3r;
+ out[oidx2] = i4r - i2r;
+ }
+ if (ido < 2)
+ return;
+ if (ido != 2) {
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int idx2 = idx1 + idx0;
+ int idx3 = idx2 + idx0;
+ int idx4 = idx3 + idx0;
+ int idx5 = 4 * idx1;
+ int idx6 = idx5 + ido;
+ int idx7 = idx6 + ido;
+ int idx8 = idx7 + ido;
+ for (i = 2; i < ido; i += 2) {
+ ic = ido - i;
+ int widx1 = i - 1 + iw1;
+ int widx2 = i - 1 + iw2;
+ int widx3 = i - 1 + iw3;
+ w1r = wtable_r[widx1 - 1];
+ w1i = wtable_r[widx1];
+ w2r = wtable_r[widx2 - 1];
+ w2i = wtable_r[widx2];
+ w3r = wtable_r[widx3 - 1];
+ w3i = wtable_r[widx3];
+
+ int idx9 = in_off + i;
+ int idx10 = out_off + i;
+ int idx11 = out_off + ic;
+ int iidx1 = idx9 + idx1;
+ int iidx2 = idx9 + idx2;
+ int iidx3 = idx9 + idx3;
+ int iidx4 = idx9 + idx4;
+
+ float i1i = in[iidx1 - 1];
+ float i1r = in[iidx1];
+ float i2i = in[iidx2 - 1];
+ float i2r = in[iidx2];
+ float i3i = in[iidx3 - 1];
+ float i3r = in[iidx3];
+ float i4i = in[iidx4 - 1];
+ float i4r = in[iidx4];
+
+ cr2 = w1r * i2i + w1i * i2r;
+ ci2 = w1r * i2r - w1i * i2i;
+ cr3 = w2r * i3i + w2i * i3r;
+ ci3 = w2r * i3r - w2i * i3i;
+ cr4 = w3r * i4i + w3i * i4r;
+ ci4 = w3r * i4r - w3i * i4i;
+ tr1 = cr2 + cr4;
+ tr4 = cr4 - cr2;
+ ti1 = ci2 + ci4;
+ ti4 = ci2 - ci4;
+ ti2 = i1r + ci3;
+ ti3 = i1r - ci3;
+ tr2 = i1i + cr3;
+ tr3 = i1i - cr3;
+
+ int oidx1 = idx10 + idx5;
+ int oidx2 = idx11 + idx6;
+ int oidx3 = idx10 + idx7;
+ int oidx4 = idx11 + idx8;
+
+ out[oidx1 - 1] = tr1 + tr2;
+ out[oidx4 - 1] = tr2 - tr1;
+ out[oidx1] = ti1 + ti2;
+ out[oidx4] = ti1 - ti2;
+ out[oidx3 - 1] = ti4 + tr3;
+ out[oidx2 - 1] = tr3 - ti4;
+ out[oidx3] = tr4 + ti3;
+ out[oidx2] = tr4 - ti3;
+ }
+ }
+ if (ido % 2 == 1)
+ return;
+ }
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int idx2 = 4 * idx1;
+ int idx3 = idx1 + idx0;
+ int idx4 = idx3 + idx0;
+ int idx5 = idx4 + idx0;
+ int idx6 = idx2 + ido;
+ int idx7 = idx6 + ido;
+ int idx8 = idx7 + ido;
+ int idx9 = in_off + ido;
+ int idx10 = out_off + ido;
+
+ float i1i = in[idx9 - 1 + idx1];
+ float i2i = in[idx9 - 1 + idx3];
+ float i3i = in[idx9 - 1 + idx4];
+ float i4i = in[idx9 - 1 + idx5];
+
+ ti1 = -hsqt2 * (i2i + i4i);
+ tr1 = hsqt2 * (i2i - i4i);
+
+ out[idx10 - 1 + idx2] = tr1 + i1i;
+ out[idx10 - 1 + idx7] = i1i - tr1;
+ out[out_off + idx6] = ti1 - i3i;
+ out[out_off + idx8] = ti1 + i3i;
+ }
+ }
+
+ /*-------------------------------------------------
+ radb4: Real FFT's backward processing of factor 4
+ -------------------------------------------------*/
+ void radb4(final int ido, final int l1, final float[] in, final int in_off, final float[] out, final int out_off, final int offset) {
+ final float sqrt2 = 1.41421356237309514547462185873882845f;
+ int i, ic;
+ float ci2, ci3, ci4, cr2, cr3, cr4;
+ float ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4, w1r, w1i, w2r, w2i, w3r, w3i;
+ int iw1, iw2, iw3;
+ iw1 = offset;
+ iw2 = iw1 + ido;
+ iw3 = iw2 + ido;
+
+ int idx0 = l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int idx2 = 4 * idx1;
+ int idx3 = idx1 + idx0;
+ int idx4 = idx3 + idx0;
+ int idx5 = idx4 + idx0;
+ int idx6 = idx2 + ido;
+ int idx7 = idx6 + ido;
+ int idx8 = idx7 + ido;
+
+ float i1r = in[in_off + idx2];
+ float i2r = in[in_off + idx7];
+ float i3r = in[in_off + ido - 1 + idx8];
+ float i4r = in[in_off + ido - 1 + idx6];
+
+ tr1 = i1r - i3r;
+ tr2 = i1r + i3r;
+ tr3 = i4r + i4r;
+ tr4 = i2r + i2r;
+
+ out[out_off + idx1] = tr2 + tr3;
+ out[out_off + idx3] = tr1 - tr4;
+ out[out_off + idx4] = tr2 - tr3;
+ out[out_off + idx5] = tr1 + tr4;
+ }
+ if (ido < 2)
+ return;
+ if (ido != 2) {
+ for (int k = 0; k < l1; ++k) {
+ int idx1 = k * ido;
+ int idx2 = idx1 + idx0;
+ int idx3 = idx2 + idx0;
+ int idx4 = idx3 + idx0;
+ int idx5 = 4 * idx1;
+ int idx6 = idx5 + ido;
+ int idx7 = idx6 + ido;
+ int idx8 = idx7 + ido;
+ for (i = 2; i < ido; i += 2) {
+ ic = ido - i;
+ int widx1 = i - 1 + iw1;
+ int widx2 = i - 1 + iw2;
+ int widx3 = i - 1 + iw3;
+ w1r = wtable_r[widx1 - 1];
+ w1i = wtable_r[widx1];
+ w2r = wtable_r[widx2 - 1];
+ w2i = wtable_r[widx2];
+ w3r = wtable_r[widx3 - 1];
+ w3i = wtable_r[widx3];
+
+ int idx12 = in_off + i;
+ int idx13 = in_off + ic;
+ int idx14 = out_off + i;
+
+ int iidx1 = idx12 + idx5;
+ int iidx2 = idx13 + idx6;
+ int iidx3 = idx12 + idx7;
+ int iidx4 = idx13 + idx8;
+
+ float i1i = in[iidx1 - 1];
+ float i1r = in[iidx1];
+ float i2i = in[iidx2 - 1];
+ float i2r = in[iidx2];
+ float i3i = in[iidx3 - 1];
+ float i3r = in[iidx3];
+ float i4i = in[iidx4 - 1];
+ float i4r = in[iidx4];
+
+ ti1 = i1r + i4r;
+ ti2 = i1r - i4r;
+ ti3 = i3r - i2r;
+ tr4 = i3r + i2r;
+ tr1 = i1i - i4i;
+ tr2 = i1i + i4i;
+ ti4 = i3i - i2i;
+ tr3 = i3i + i2i;
+ cr3 = tr2 - tr3;
+ ci3 = ti2 - ti3;
+ cr2 = tr1 - tr4;
+ cr4 = tr1 + tr4;
+ ci2 = ti1 + ti4;
+ ci4 = ti1 - ti4;
+
+ int oidx1 = idx14 + idx1;
+ int oidx2 = idx14 + idx2;
+ int oidx3 = idx14 + idx3;
+ int oidx4 = idx14 + idx4;
+
+ out[oidx1 - 1] = tr2 + tr3;
+ out[oidx1] = ti2 + ti3;
+ out[oidx2 - 1] = w1r * cr2 - w1i * ci2;
+ out[oidx2] = w1r * ci2 + w1i * cr2;
+ out[oidx3 - 1] = w2r * cr3 - w2i * ci3;
+ out[oidx3] = w2r * ci3 + w2i * cr3;
+ out[oidx4 - 1] = w3r * cr4 - w3i * ci4;
+ out[oidx4] = w3r * ci4 + w3i * cr4;
+ }
+ }
+ if (ido % 2 == 1)
+ return;
+ }
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int idx2 = 4 * idx1;
+ int idx3 = idx1 + idx0;
+ int idx4 = idx3 + idx0;
+ int idx5 = idx4 + idx0;
+ int idx6 = idx2 + ido;
+ int idx7 = idx6 + ido;
+ int idx8 = idx7 + ido;
+ int idx9 = in_off + ido;
+ int idx10 = out_off + ido;
+
+ float i1r = in[idx9 - 1 + idx2];
+ float i2r = in[idx9 - 1 + idx7];
+ float i3r = in[in_off + idx6];
+ float i4r = in[in_off + idx8];
+
+ ti1 = i3r + i4r;
+ ti2 = i4r - i3r;
+ tr1 = i1r - i2r;
+ tr2 = i1r + i2r;
+
+ out[idx10 - 1 + idx1] = tr2 + tr2;
+ out[idx10 - 1 + idx3] = sqrt2 * (tr1 - ti1);
+ out[idx10 - 1 + idx4] = ti2 + ti2;
+ out[idx10 - 1 + idx5] = -sqrt2 * (tr1 + ti1);
+ }
+ }
+
+ /*-------------------------------------------------
+ radf5: Real FFT's forward processing of factor 5
+ -------------------------------------------------*/
+ void radf5(final int ido, final int l1, final float[] in, final int in_off, final float[] out, final int out_off, final int offset) {
+ final float tr11 = 0.309016994374947451262869435595348477f;
+ final float ti11 = 0.951056516295153531181938433292089030f;
+ final float tr12 = -0.809016994374947340240566973079694435f;
+ final float ti12 = 0.587785252292473248125759255344746634f;
+ int i, ic;
+ float ci2, di2, ci4, ci5, di3, di4, di5, ci3, cr2, cr3, dr2, dr3, dr4, dr5, cr5, cr4, ti2, ti3, ti5, ti4, tr2, tr3, tr4, tr5, w1r, w1i, w2r, w2i, w3r, w3i, w4r, w4i;
+ int iw1, iw2, iw3, iw4;
+ iw1 = offset;
+ iw2 = iw1 + ido;
+ iw3 = iw2 + ido;
+ iw4 = iw3 + ido;
+
+ int idx0 = l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int idx2 = 5 * idx1;
+ int idx3 = idx2 + ido;
+ int idx4 = idx3 + ido;
+ int idx5 = idx4 + ido;
+ int idx6 = idx5 + ido;
+ int idx7 = idx1 + idx0;
+ int idx8 = idx7 + idx0;
+ int idx9 = idx8 + idx0;
+ int idx10 = idx9 + idx0;
+ int idx11 = out_off + ido - 1;
+
+ float i1r = in[in_off + idx1];
+ float i2r = in[in_off + idx7];
+ float i3r = in[in_off + idx8];
+ float i4r = in[in_off + idx9];
+ float i5r = in[in_off + idx10];
+
+ cr2 = i5r + i2r;
+ ci5 = i5r - i2r;
+ cr3 = i4r + i3r;
+ ci4 = i4r - i3r;
+
+ out[out_off + idx2] = i1r + cr2 + cr3;
+ out[idx11 + idx3] = i1r + tr11 * cr2 + tr12 * cr3;
+ out[out_off + idx4] = ti11 * ci5 + ti12 * ci4;
+ out[idx11 + idx5] = i1r + tr12 * cr2 + tr11 * cr3;
+ out[out_off + idx6] = ti12 * ci5 - ti11 * ci4;
+ }
+ if (ido == 1)
+ return;
+ for (int k = 0; k < l1; ++k) {
+ int idx1 = k * ido;
+ int idx2 = 5 * idx1;
+ int idx3 = idx2 + ido;
+ int idx4 = idx3 + ido;
+ int idx5 = idx4 + ido;
+ int idx6 = idx5 + ido;
+ int idx7 = idx1 + idx0;
+ int idx8 = idx7 + idx0;
+ int idx9 = idx8 + idx0;
+ int idx10 = idx9 + idx0;
+ for (i = 2; i < ido; i += 2) {
+ int widx1 = i - 1 + iw1;
+ int widx2 = i - 1 + iw2;
+ int widx3 = i - 1 + iw3;
+ int widx4 = i - 1 + iw4;
+ w1r = wtable_r[widx1 - 1];
+ w1i = wtable_r[widx1];
+ w2r = wtable_r[widx2 - 1];
+ w2i = wtable_r[widx2];
+ w3r = wtable_r[widx3 - 1];
+ w3i = wtable_r[widx3];
+ w4r = wtable_r[widx4 - 1];
+ w4i = wtable_r[widx4];
+
+ ic = ido - i;
+ int idx15 = in_off + i;
+ int idx16 = out_off + i;
+ int idx17 = out_off + ic;
+
+ int iidx1 = idx15 + idx1;
+ int iidx2 = idx15 + idx7;
+ int iidx3 = idx15 + idx8;
+ int iidx4 = idx15 + idx9;
+ int iidx5 = idx15 + idx10;
+
+ float i1i = in[iidx1 - 1];
+ float i1r = in[iidx1];
+ float i2i = in[iidx2 - 1];
+ float i2r = in[iidx2];
+ float i3i = in[iidx3 - 1];
+ float i3r = in[iidx3];
+ float i4i = in[iidx4 - 1];
+ float i4r = in[iidx4];
+ float i5i = in[iidx5 - 1];
+ float i5r = in[iidx5];
+
+ dr2 = w1r * i2i + w1i * i2r;
+ di2 = w1r * i2r - w1i * i2i;
+ dr3 = w2r * i3i + w2i * i3r;
+ di3 = w2r * i3r - w2i * i3i;
+ dr4 = w3r * i4i + w3i * i4r;
+ di4 = w3r * i4r - w3i * i4i;
+ dr5 = w4r * i5i + w4i * i5r;
+ di5 = w4r * i5r - w4i * i5i;
+
+ cr2 = dr2 + dr5;
+ ci5 = dr5 - dr2;
+ cr5 = di2 - di5;
+ ci2 = di2 + di5;
+ cr3 = dr3 + dr4;
+ ci4 = dr4 - dr3;
+ cr4 = di3 - di4;
+ ci3 = di3 + di4;
+
+ tr2 = i1i + tr11 * cr2 + tr12 * cr3;
+ ti2 = i1r + tr11 * ci2 + tr12 * ci3;
+ tr3 = i1i + tr12 * cr2 + tr11 * cr3;
+ ti3 = i1r + tr12 * ci2 + tr11 * ci3;
+ tr5 = ti11 * cr5 + ti12 * cr4;
+ ti5 = ti11 * ci5 + ti12 * ci4;
+ tr4 = ti12 * cr5 - ti11 * cr4;
+ ti4 = ti12 * ci5 - ti11 * ci4;
+
+ int oidx1 = idx16 + idx2;
+ int oidx2 = idx17 + idx3;
+ int oidx3 = idx16 + idx4;
+ int oidx4 = idx17 + idx5;
+ int oidx5 = idx16 + idx6;
+
+ out[oidx1 - 1] = i1i + cr2 + cr3;
+ out[oidx1] = i1r + ci2 + ci3;
+ out[oidx3 - 1] = tr2 + tr5;
+ out[oidx2 - 1] = tr2 - tr5;
+ out[oidx3] = ti2 + ti5;
+ out[oidx2] = ti5 - ti2;
+ out[oidx5 - 1] = tr3 + tr4;
+ out[oidx4 - 1] = tr3 - tr4;
+ out[oidx5] = ti3 + ti4;
+ out[oidx4] = ti4 - ti3;
+ }
+ }
+ }
+
+ /*-------------------------------------------------
+ radb5: Real FFT's backward processing of factor 5
+ -------------------------------------------------*/
+ void radb5(final int ido, final int l1, final float[] in, final int in_off, final float[] out, final int out_off, final int offset) {
+ final float tr11 = 0.309016994374947451262869435595348477f;
+ final float ti11 = 0.951056516295153531181938433292089030f;
+ final float tr12 = -0.809016994374947340240566973079694435f;
+ final float ti12 = 0.587785252292473248125759255344746634f;
+ int i, ic;
+ float ci2, ci3, ci4, ci5, di3, di4, di5, di2, cr2, cr3, cr5, cr4, ti2, ti3, ti4, ti5, dr3, dr4, dr5, dr2, tr2, tr3, tr4, tr5, w1r, w1i, w2r, w2i, w3r, w3i, w4r, w4i;
+ int iw1, iw2, iw3, iw4;
+ iw1 = offset;
+ iw2 = iw1 + ido;
+ iw3 = iw2 + ido;
+ iw4 = iw3 + ido;
+
+ int idx0 = l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int idx2 = 5 * idx1;
+ int idx3 = idx2 + ido;
+ int idx4 = idx3 + ido;
+ int idx5 = idx4 + ido;
+ int idx6 = idx5 + ido;
+ int idx7 = idx1 + idx0;
+ int idx8 = idx7 + idx0;
+ int idx9 = idx8 + idx0;
+ int idx10 = idx9 + idx0;
+ int idx11 = in_off + ido - 1;
+
+ float i1r = in[in_off + idx2];
+
+ ti5 = 2 * in[in_off + idx4];
+ ti4 = 2 * in[in_off + idx6];
+ tr2 = 2 * in[idx11 + idx3];
+ tr3 = 2 * in[idx11 + idx5];
+ cr2 = i1r + tr11 * tr2 + tr12 * tr3;
+ cr3 = i1r + tr12 * tr2 + tr11 * tr3;
+ ci5 = ti11 * ti5 + ti12 * ti4;
+ ci4 = ti12 * ti5 - ti11 * ti4;
+
+ out[out_off + idx1] = i1r + tr2 + tr3;
+ out[out_off + idx7] = cr2 - ci5;
+ out[out_off + idx8] = cr3 - ci4;
+ out[out_off + idx9] = cr3 + ci4;
+ out[out_off + idx10] = cr2 + ci5;
+ }
+ if (ido == 1)
+ return;
+ for (int k = 0; k < l1; ++k) {
+ int idx1 = k * ido;
+ int idx2 = 5 * idx1;
+ int idx3 = idx2 + ido;
+ int idx4 = idx3 + ido;
+ int idx5 = idx4 + ido;
+ int idx6 = idx5 + ido;
+ int idx7 = idx1 + idx0;
+ int idx8 = idx7 + idx0;
+ int idx9 = idx8 + idx0;
+ int idx10 = idx9 + idx0;
+ for (i = 2; i < ido; i += 2) {
+ ic = ido - i;
+ int widx1 = i - 1 + iw1;
+ int widx2 = i - 1 + iw2;
+ int widx3 = i - 1 + iw3;
+ int widx4 = i - 1 + iw4;
+ w1r = wtable_r[widx1 - 1];
+ w1i = wtable_r[widx1];
+ w2r = wtable_r[widx2 - 1];
+ w2i = wtable_r[widx2];
+ w3r = wtable_r[widx3 - 1];
+ w3i = wtable_r[widx3];
+ w4r = wtable_r[widx4 - 1];
+ w4i = wtable_r[widx4];
+
+ int idx15 = in_off + i;
+ int idx16 = in_off + ic;
+ int idx17 = out_off + i;
+
+ int iidx1 = idx15 + idx2;
+ int iidx2 = idx16 + idx3;
+ int iidx3 = idx15 + idx4;
+ int iidx4 = idx16 + idx5;
+ int iidx5 = idx15 + idx6;
+
+ float i1i = in[iidx1 - 1];
+ float i1r = in[iidx1];
+ float i2i = in[iidx2 - 1];
+ float i2r = in[iidx2];
+ float i3i = in[iidx3 - 1];
+ float i3r = in[iidx3];
+ float i4i = in[iidx4 - 1];
+ float i4r = in[iidx4];
+ float i5i = in[iidx5 - 1];
+ float i5r = in[iidx5];
+
+ ti5 = i3r + i2r;
+ ti2 = i3r - i2r;
+ ti4 = i5r + i4r;
+ ti3 = i5r - i4r;
+ tr5 = i3i - i2i;
+ tr2 = i3i + i2i;
+ tr4 = i5i - i4i;
+ tr3 = i5i + i4i;
+
+ cr2 = i1i + tr11 * tr2 + tr12 * tr3;
+ ci2 = i1r + tr11 * ti2 + tr12 * ti3;
+ cr3 = i1i + tr12 * tr2 + tr11 * tr3;
+ ci3 = i1r + tr12 * ti2 + tr11 * ti3;
+ cr5 = ti11 * tr5 + ti12 * tr4;
+ ci5 = ti11 * ti5 + ti12 * ti4;
+ cr4 = ti12 * tr5 - ti11 * tr4;
+ ci4 = ti12 * ti5 - ti11 * ti4;
+ dr3 = cr3 - ci4;
+ dr4 = cr3 + ci4;
+ di3 = ci3 + cr4;
+ di4 = ci3 - cr4;
+ dr5 = cr2 + ci5;
+ dr2 = cr2 - ci5;
+ di5 = ci2 - cr5;
+ di2 = ci2 + cr5;
+
+ int oidx1 = idx17 + idx1;
+ int oidx2 = idx17 + idx7;
+ int oidx3 = idx17 + idx8;
+ int oidx4 = idx17 + idx9;
+ int oidx5 = idx17 + idx10;
+
+ out[oidx1 - 1] = i1i + tr2 + tr3;
+ out[oidx1] = i1r + ti2 + ti3;
+ out[oidx2 - 1] = w1r * dr2 - w1i * di2;
+ out[oidx2] = w1r * di2 + w1i * dr2;
+ out[oidx3 - 1] = w2r * dr3 - w2i * di3;
+ out[oidx3] = w2r * di3 + w2i * dr3;
+ out[oidx4 - 1] = w3r * dr4 - w3i * di4;
+ out[oidx4] = w3r * di4 + w3i * dr4;
+ out[oidx5 - 1] = w4r * dr5 - w4i * di5;
+ out[oidx5] = w4r * di5 + w4i * dr5;
+ }
+ }
+ }
+
+ /*---------------------------------------------------------
+ radfg: Real FFT's forward processing of general factor
+ --------------------------------------------------------*/
+ void radfg(final int ido, final int ip, final int l1, final int idl1, final float[] in, final int in_off, final float[] out, final int out_off, final int offset) {
+ int idij, ipph, j2, ic, jc, lc, is, nbd;
+ float dc2, ai1, ai2, ar1, ar2, ds2, dcp, arg, dsp, ar1h, ar2h, w1r, w1i;
+ int iw1 = offset;
+
+ arg = TWO_PI / (float) ip;
+ dcp = (float)Math.cos(arg);
+ dsp = (float)Math.sin(arg);
+ ipph = (ip + 1) / 2;
+ nbd = (ido - 1) / 2;
+ if (ido != 1) {
+ if (idl1 >= 0) System.arraycopy(in, in_off + 0, out, out_off + 0, idl1);
+ for (int j = 1; j < ip; j++) {
+ int idx1 = j * l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx2 = k * ido + idx1;
+ out[out_off + idx2] = in[in_off + idx2];
+ }
+ }
+ if (nbd <= l1) {
+ is = -ido;
+ for (int j = 1; j < ip; j++) {
+ is += ido;
+ idij = is - 1;
+ int idx1 = j * l1 * ido;
+ for (int i = 2; i < ido; i += 2) {
+ idij += 2;
+ int idx2 = idij + iw1;
+ int idx4 = in_off + i;
+ int idx5 = out_off + i;
+ w1r = wtable_r[idx2 - 1];
+ w1i = wtable_r[idx2];
+ for (int k = 0; k < l1; k++) {
+ int idx3 = k * ido + idx1;
+ int oidx1 = idx5 + idx3;
+ int iidx1 = idx4 + idx3;
+ float i1i = in[iidx1 - 1];
+ float i1r = in[iidx1];
+
+ out[oidx1 - 1] = w1r * i1i + w1i * i1r;
+ out[oidx1] = w1r * i1r - w1i * i1i;
+ }
+ }
+ }
+ } else {
+ is = -ido;
+ for (int j = 1; j < ip; j++) {
+ is += ido;
+ int idx1 = j * l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ idij = is - 1;
+ int idx3 = k * ido + idx1;
+ for (int i = 2; i < ido; i += 2) {
+ idij += 2;
+ int idx2 = idij + iw1;
+ w1r = wtable_r[idx2 - 1];
+ w1i = wtable_r[idx2];
+ int oidx1 = out_off + i + idx3;
+ int iidx1 = in_off + i + idx3;
+ float i1i = in[iidx1 - 1];
+ float i1r = in[iidx1];
+
+ out[oidx1 - 1] = w1r * i1i + w1i * i1r;
+ out[oidx1] = w1r * i1r - w1i * i1i;
+ }
+ }
+ }
+ }
+ if (nbd >= l1) {
+ for (int j = 1; j < ipph; j++) {
+ jc = ip - j;
+ int idx1 = j * l1 * ido;
+ int idx2 = jc * l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx3 = k * ido + idx1;
+ int idx4 = k * ido + idx2;
+ for (int i = 2; i < ido; i += 2) {
+ int idx5 = in_off + i;
+ int idx6 = out_off + i;
+ int iidx1 = idx5 + idx3;
+ int iidx2 = idx5 + idx4;
+ int oidx1 = idx6 + idx3;
+ int oidx2 = idx6 + idx4;
+ float o1i = out[oidx1 - 1];
+ float o1r = out[oidx1];
+ float o2i = out[oidx2 - 1];
+ float o2r = out[oidx2];
+
+ in[iidx1 - 1] = o1i + o2i;
+ in[iidx1] = o1r + o2r;
+
+ in[iidx2 - 1] = o1r - o2r;
+ in[iidx2] = o2i - o1i;
+ }
+ }
+ }
+ } else {
+ for (int j = 1; j < ipph; j++) {
+ jc = ip - j;
+ int idx1 = j * l1 * ido;
+ int idx2 = jc * l1 * ido;
+ for (int i = 2; i < ido; i += 2) {
+ int idx5 = in_off + i;
+ int idx6 = out_off + i;
+ for (int k = 0; k < l1; k++) {
+ int idx3 = k * ido + idx1;
+ int idx4 = k * ido + idx2;
+ int iidx1 = idx5 + idx3;
+ int iidx2 = idx5 + idx4;
+ int oidx1 = idx6 + idx3;
+ int oidx2 = idx6 + idx4;
+ float o1i = out[oidx1 - 1];
+ float o1r = out[oidx1];
+ float o2i = out[oidx2 - 1];
+ float o2r = out[oidx2];
+
+ in[iidx1 - 1] = o1i + o2i;
+ in[iidx1] = o1r + o2r;
+ in[iidx2 - 1] = o1r - o2r;
+ in[iidx2] = o2i - o1i;
+ }
+ }
+ }
+ }
+ } else {
+ System.arraycopy(out, out_off, in, in_off, idl1);
+ }
+ for (int j = 1; j < ipph; j++) {
+ jc = ip - j;
+ int idx1 = j * l1 * ido;
+ int idx2 = jc * l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx3 = k * ido + idx1;
+ int idx4 = k * ido + idx2;
+ int oidx1 = out_off + idx3;
+ int oidx2 = out_off + idx4;
+ float o1r = out[oidx1];
+ float o2r = out[oidx2];
+
+ in[in_off + idx3] = o1r + o2r;
+ in[in_off + idx4] = o2r - o1r;
+ }
+ }
+
+ ar1 = 1;
+ ai1 = 0;
+ int idx0 = (ip - 1) * idl1;
+ for (int l = 1; l < ipph; l++) {
+ lc = ip - l;
+ ar1h = dcp * ar1 - dsp * ai1;
+ ai1 = dcp * ai1 + dsp * ar1;
+ ar1 = ar1h;
+ int idx1 = l * idl1;
+ int idx2 = lc * idl1;
+ for (int ik = 0; ik < idl1; ik++) {
+ int idx3 = out_off + ik;
+ int idx4 = in_off + ik;
+ out[idx3 + idx1] = in[idx4] + ar1 * in[idx4 + idl1];
+ out[idx3 + idx2] = ai1 * in[idx4 + idx0];
+ }
+ dc2 = ar1;
+ ds2 = ai1;
+ ar2 = ar1;
+ ai2 = ai1;
+ for (int j = 2; j < ipph; j++) {
+ jc = ip - j;
+ ar2h = dc2 * ar2 - ds2 * ai2;
+ ai2 = dc2 * ai2 + ds2 * ar2;
+ ar2 = ar2h;
+ int idx3 = j * idl1;
+ int idx4 = jc * idl1;
+ for (int ik = 0; ik < idl1; ik++) {
+ int idx5 = out_off + ik;
+ int idx6 = in_off + ik;
+ out[idx5 + idx1] += ar2 * in[idx6 + idx3];
+ out[idx5 + idx2] += ai2 * in[idx6 + idx4];
+ }
+ }
+ }
+ for (int j = 1; j < ipph; j++) {
+ int idx1 = j * idl1;
+ for (int ik = 0; ik < idl1; ik++) {
+ out[out_off + ik] += in[in_off + ik + idx1];
+ }
+ }
+
+ if (ido >= l1) {
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int idx2 = idx1 * ip;
+ for (int i = 0; i < ido; i++) {
+ in[in_off + i + idx2] = out[out_off + i + idx1];
+ }
+ }
+ } else {
+ for (int i = 0; i < ido; i++) {
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ in[in_off + i + idx1 * ip] = out[out_off + i + idx1];
+ }
+ }
+ }
+ int idx01 = ip * ido;
+ for (int j = 1; j < ipph; j++) {
+ jc = ip - j;
+ j2 = 2 * j;
+ int idx1 = j * l1 * ido;
+ int idx2 = jc * l1 * ido;
+ int idx3 = j2 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx4 = k * ido;
+ int idx5 = idx4 + idx1;
+ int idx6 = idx4 + idx2;
+ int idx7 = k * idx01;
+ in[in_off + ido - 1 + idx3 - ido + idx7] = out[out_off + idx5];
+ in[in_off + idx3 + idx7] = out[out_off + idx6];
+ }
+ }
+ if (ido == 1)
+ return;
+ if (nbd >= l1) {
+ for (int j = 1; j < ipph; j++) {
+ jc = ip - j;
+ j2 = 2 * j;
+ int idx1 = j * l1 * ido;
+ int idx2 = jc * l1 * ido;
+ int idx3 = j2 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx4 = k * idx01;
+ int idx5 = k * ido;
+ for (int i = 2; i < ido; i += 2) {
+ ic = ido - i;
+ int idx6 = in_off + i;
+ int idx7 = in_off + ic;
+ int idx8 = out_off + i;
+ int iidx1 = idx6 + idx3 + idx4;
+ int iidx2 = idx7 + idx3 - ido + idx4;
+ int oidx1 = idx8 + idx5 + idx1;
+ int oidx2 = idx8 + idx5 + idx2;
+ float o1i = out[oidx1 - 1];
+ float o1r = out[oidx1];
+ float o2i = out[oidx2 - 1];
+ float o2r = out[oidx2];
+
+ in[iidx1 - 1] = o1i + o2i;
+ in[iidx2 - 1] = o1i - o2i;
+ in[iidx1] = o1r + o2r;
+ in[iidx2] = o2r - o1r;
+ }
+ }
+ }
+ } else {
+ for (int j = 1; j < ipph; j++) {
+ jc = ip - j;
+ j2 = 2 * j;
+ int idx1 = j * l1 * ido;
+ int idx2 = jc * l1 * ido;
+ int idx3 = j2 * ido;
+ for (int i = 2; i < ido; i += 2) {
+ ic = ido - i;
+ int idx6 = in_off + i;
+ int idx7 = in_off + ic;
+ int idx8 = out_off + i;
+ for (int k = 0; k < l1; k++) {
+ int idx4 = k * idx01;
+ int idx5 = k * ido;
+ int iidx1 = idx6 + idx3 + idx4;
+ int iidx2 = idx7 + idx3 - ido + idx4;
+ int oidx1 = idx8 + idx5 + idx1;
+ int oidx2 = idx8 + idx5 + idx2;
+ float o1i = out[oidx1 - 1];
+ float o1r = out[oidx1];
+ float o2i = out[oidx2 - 1];
+ float o2r = out[oidx2];
+
+ in[iidx1 - 1] = o1i + o2i;
+ in[iidx2 - 1] = o1i - o2i;
+ in[iidx1] = o1r + o2r;
+ in[iidx2] = o2r - o1r;
+ }
+ }
+ }
+ }
+ }
+
+ /*---------------------------------------------------------
+ radbg: Real FFT's backward processing of general factor
+ --------------------------------------------------------*/
+ void radbg(final int ido, final int ip, final int l1, final int idl1, final float[] in, final int in_off, final float[] out, final int out_off, final int offset) {
+ int idij, ipph, j2, ic, jc, lc, is;
+ float dc2, ai1, ai2, ar1, ar2, ds2, w1r, w1i;
+ int nbd;
+ float dcp, arg, dsp, ar1h, ar2h;
+ int iw1 = offset;
+
+ arg = TWO_PI / (float) ip;
+ dcp = (float)Math.cos(arg);
+ dsp = (float)Math.sin(arg);
+ nbd = (ido - 1) / 2;
+ ipph = (ip + 1) / 2;
+ int idx0 = ip * ido;
+ if (ido >= l1) {
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int idx2 = k * idx0;
+ for (int i = 0; i < ido; i++) {
+ out[out_off + i + idx1] = in[in_off + i + idx2];
+ }
+ }
+ } else {
+ for (int i = 0; i < ido; i++) {
+ int idx1 = out_off + i;
+ int idx2 = in_off + i;
+ for (int k = 0; k < l1; k++) {
+ out[idx1 + k * ido] = in[idx2 + k * idx0];
+ }
+ }
+ }
+ int iidx0 = in_off + ido - 1;
+ for (int j = 1; j < ipph; j++) {
+ jc = ip - j;
+ j2 = 2 * j;
+ int idx1 = j * l1 * ido;
+ int idx2 = jc * l1 * ido;
+ int idx3 = j2 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx4 = k * ido;
+ int idx5 = idx4 * ip;
+ int iidx1 = iidx0 + idx3 + idx5 - ido;
+ int iidx2 = in_off + idx3 + idx5;
+ float i1r = in[iidx1];
+ float i2r = in[iidx2];
+
+ out[out_off + idx4 + idx1] = i1r + i1r;
+ out[out_off + idx4 + idx2] = i2r + i2r;
+ }
+ }
+
+ if (ido != 1) {
+ if (nbd >= l1) {
+ for (int j = 1; j < ipph; j++) {
+ jc = ip - j;
+ int idx1 = j * l1 * ido;
+ int idx2 = jc * l1 * ido;
+ int idx3 = 2 * j * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx4 = k * ido + idx1;
+ int idx5 = k * ido + idx2;
+ int idx6 = k * ip * ido + idx3;
+ for (int i = 2; i < ido; i += 2) {
+ ic = ido - i;
+ int idx7 = out_off + i;
+ int idx8 = in_off + ic;
+ int idx9 = in_off + i;
+ int oidx1 = idx7 + idx4;
+ int oidx2 = idx7 + idx5;
+ int iidx1 = idx9 + idx6;
+ int iidx2 = idx8 + idx6 - ido;
+ float a1i = in[iidx1 - 1];
+ float a1r = in[iidx1];
+ float a2i = in[iidx2 - 1];
+ float a2r = in[iidx2];
+
+ out[oidx1 - 1] = a1i + a2i;
+ out[oidx2 - 1] = a1i - a2i;
+ out[oidx1] = a1r - a2r;
+ out[oidx2] = a1r + a2r;
+ }
+ }
+ }
+ } else {
+ for (int j = 1; j < ipph; j++) {
+ jc = ip - j;
+ int idx1 = j * l1 * ido;
+ int idx2 = jc * l1 * ido;
+ int idx3 = 2 * j * ido;
+ for (int i = 2; i < ido; i += 2) {
+ ic = ido - i;
+ int idx7 = out_off + i;
+ int idx8 = in_off + ic;
+ int idx9 = in_off + i;
+ for (int k = 0; k < l1; k++) {
+ int idx4 = k * ido + idx1;
+ int idx5 = k * ido + idx2;
+ int idx6 = k * ip * ido + idx3;
+ int oidx1 = idx7 + idx4;
+ int oidx2 = idx7 + idx5;
+ int iidx1 = idx9 + idx6;
+ int iidx2 = idx8 + idx6 - ido;
+ float a1i = in[iidx1 - 1];
+ float a1r = in[iidx1];
+ float a2i = in[iidx2 - 1];
+ float a2r = in[iidx2];
+
+ out[oidx1 - 1] = a1i + a2i;
+ out[oidx2 - 1] = a1i - a2i;
+ out[oidx1] = a1r - a2r;
+ out[oidx2] = a1r + a2r;
+ }
+ }
+ }
+ }
+ }
+
+ ar1 = 1;
+ ai1 = 0;
+ int idx01 = (ip - 1) * idl1;
+ for (int l = 1; l < ipph; l++) {
+ lc = ip - l;
+ ar1h = dcp * ar1 - dsp * ai1;
+ ai1 = dcp * ai1 + dsp * ar1;
+ ar1 = ar1h;
+ int idx1 = l * idl1;
+ int idx2 = lc * idl1;
+ for (int ik = 0; ik < idl1; ik++) {
+ int idx3 = in_off + ik;
+ int idx4 = out_off + ik;
+ in[idx3 + idx1] = out[idx4] + ar1 * out[idx4 + idl1];
+ in[idx3 + idx2] = ai1 * out[idx4 + idx01];
+ }
+ dc2 = ar1;
+ ds2 = ai1;
+ ar2 = ar1;
+ ai2 = ai1;
+ for (int j = 2; j < ipph; j++) {
+ jc = ip - j;
+ ar2h = dc2 * ar2 - ds2 * ai2;
+ ai2 = dc2 * ai2 + ds2 * ar2;
+ ar2 = ar2h;
+ int idx5 = j * idl1;
+ int idx6 = jc * idl1;
+ for (int ik = 0; ik < idl1; ik++) {
+ int idx7 = in_off + ik;
+ int idx8 = out_off + ik;
+ in[idx7 + idx1] += ar2 * out[idx8 + idx5];
+ in[idx7 + idx2] += ai2 * out[idx8 + idx6];
+ }
+ }
+ }
+ for (int j = 1; j < ipph; j++) {
+ int idx1 = j * idl1;
+ for (int ik = 0; ik < idl1; ik++) {
+ int idx2 = out_off + ik;
+ out[idx2] += out[idx2 + idx1];
+ }
+ }
+ for (int j = 1; j < ipph; j++) {
+ jc = ip - j;
+ int idx1 = j * l1 * ido;
+ int idx2 = jc * l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx3 = k * ido;
+ int oidx1 = out_off + idx3;
+ int iidx1 = in_off + idx3 + idx1;
+ int iidx2 = in_off + idx3 + idx2;
+ float i1r = in[iidx1];
+ float i2r = in[iidx2];
+
+ out[oidx1 + idx1] = i1r - i2r;
+ out[oidx1 + idx2] = i1r + i2r;
+ }
+ }
+
+ if (ido == 1)
+ return;
+ if (nbd >= l1) {
+ for (int j = 1; j < ipph; j++) {
+ jc = ip - j;
+ int idx1 = j * l1 * ido;
+ int idx2 = jc * l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx3 = k * ido;
+ for (int i = 2; i < ido; i += 2) {
+ int idx4 = out_off + i;
+ int idx5 = in_off + i;
+ int oidx1 = idx4 + idx3 + idx1;
+ int oidx2 = idx4 + idx3 + idx2;
+ int iidx1 = idx5 + idx3 + idx1;
+ int iidx2 = idx5 + idx3 + idx2;
+ float i1i = in[iidx1 - 1];
+ float i1r = in[iidx1];
+ float i2i = in[iidx2 - 1];
+ float i2r = in[iidx2];
+
+ out[oidx1 - 1] = i1i - i2r;
+ out[oidx2 - 1] = i1i + i2r;
+ out[oidx1] = i1r + i2i;
+ out[oidx2] = i1r - i2i;
+ }
+ }
+ }
+ } else {
+ for (int j = 1; j < ipph; j++) {
+ jc = ip - j;
+ int idx1 = j * l1 * ido;
+ int idx2 = jc * l1 * ido;
+ for (int i = 2; i < ido; i += 2) {
+ int idx4 = out_off + i;
+ int idx5 = in_off + i;
+ for (int k = 0; k < l1; k++) {
+ int idx3 = k * ido;
+ int oidx1 = idx4 + idx3 + idx1;
+ int oidx2 = idx4 + idx3 + idx2;
+ int iidx1 = idx5 + idx3 + idx1;
+ int iidx2 = idx5 + idx3 + idx2;
+ float i1i = in[iidx1 - 1];
+ float i1r = in[iidx1];
+ float i2i = in[iidx2 - 1];
+ float i2r = in[iidx2];
+
+ out[oidx1 - 1] = i1i - i2r;
+ out[oidx2 - 1] = i1i + i2r;
+ out[oidx1] = i1r + i2i;
+ out[oidx2] = i1r - i2i;
+ }
+ }
+ }
+ }
+ System.arraycopy(out, out_off, in, in_off, idl1);
+ for (int j = 1; j < ip; j++) {
+ int idx1 = j * l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx2 = k * ido + idx1;
+ in[in_off + idx2] = out[out_off + idx2];
+ }
+ }
+ if (nbd <= l1) {
+ is = -ido;
+ for (int j = 1; j < ip; j++) {
+ is += ido;
+ idij = is - 1;
+ int idx1 = j * l1 * ido;
+ for (int i = 2; i < ido; i += 2) {
+ idij += 2;
+ int idx2 = idij + iw1;
+ w1r = wtable_r[idx2 - 1];
+ w1i = wtable_r[idx2];
+ int idx4 = in_off + i;
+ int idx5 = out_off + i;
+ for (int k = 0; k < l1; k++) {
+ int idx3 = k * ido + idx1;
+ int iidx1 = idx4 + idx3;
+ int oidx1 = idx5 + idx3;
+ float o1i = out[oidx1 - 1];
+ float o1r = out[oidx1];
+
+ in[iidx1 - 1] = w1r * o1i - w1i * o1r;
+ in[iidx1] = w1r * o1r + w1i * o1i;
+ }
+ }
+ }
+ } else {
+ is = -ido;
+ for (int j = 1; j < ip; j++) {
+ is += ido;
+ int idx1 = j * l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ idij = is - 1;
+ int idx3 = k * ido + idx1;
+ for (int i = 2; i < ido; i += 2) {
+ idij += 2;
+ int idx2 = idij + iw1;
+ w1r = wtable_r[idx2 - 1];
+ w1i = wtable_r[idx2];
+ int idx4 = in_off + i;
+ int idx5 = out_off + i;
+ int iidx1 = idx4 + idx3;
+ int oidx1 = idx5 + idx3;
+ float o1i = out[oidx1 - 1];
+ float o1r = out[oidx1];
+
+ in[iidx1 - 1] = w1r * o1i - w1i * o1r;
+ in[iidx1] = w1r * o1r + w1i * o1i;
+
+ }
+ }
+ }
+ }
+ }
+
+ /*---------------------------------------------------------
+ cfftf1: further processing of Complex forward FFT
+ --------------------------------------------------------*/
+ void cfftf(float[] a, int offa, int isign) {
+ int idot;
+ int l1, l2;
+ int na, nf, ip, iw, ido, idl1;
+ int[] nac = new int[1];
+ final int twon = 2 * n;
+
+ int iw1, iw2;
+ float[] ch = new float[twon];
+
+ iw1 = twon;
+ iw2 = 4 * n;
+ nac[0] = 0;
+ nf = (int) wtable[1 + iw2];
+ na = 0;
+ l1 = 1;
+ iw = iw1;
+ for (int k1 = 2; k1 <= nf + 1; k1++) {
+ ip = (int) wtable[k1 + iw2];
+ l2 = ip * l1;
+ ido = n / l2;
+ idot = ido + ido;
+ idl1 = idot * l1;
+ switch (ip) {
+ case 4:
+ if (na == 0) {
+ passf4(idot, l1, a, offa, ch, 0, iw, isign);
+ } else {
+ passf4(idot, l1, ch, 0, a, offa, iw, isign);
+ }
+ na = 1 - na;
+ break;
+ case 2:
+ if (na == 0) {
+ passf2(idot, l1, a, offa, ch, 0, iw, isign);
+ } else {
+ passf2(idot, l1, ch, 0, a, offa, iw, isign);
+ }
+ na = 1 - na;
+ break;
+ case 3:
+ if (na == 0) {
+ passf3(idot, l1, a, offa, ch, 0, iw, isign);
+ } else {
+ passf3(idot, l1, ch, 0, a, offa, iw, isign);
+ }
+ na = 1 - na;
+ break;
+ case 5:
+ if (na == 0) {
+ passf5(idot, l1, a, offa, ch, 0, iw, isign);
+ } else {
+ passf5(idot, l1, ch, 0, a, offa, iw, isign);
+ }
+ na = 1 - na;
+ break;
+ default:
+ if (na == 0) {
+ passfg(nac, idot, ip, l1, idl1, a, offa, ch, 0, iw, isign);
+ } else {
+ passfg(nac, idot, ip, l1, idl1, ch, 0, a, offa, iw, isign);
+ }
+ if (nac[0] != 0)
+ na = 1 - na;
+ break;
+ }
+ l1 = l2;
+ iw += (ip - 1) * idot;
+ }
+ if (na == 0)
+ return;
+ System.arraycopy(ch, 0, a, offa, twon);
+
+ }
+
+ /*----------------------------------------------------------------------
+ passf2: Complex FFT's forward/backward processing of factor 2;
+ isign is +1 for backward and -1 for forward transforms
+ ----------------------------------------------------------------------*/
+
+ void passf2(final int ido, final int l1, final float[] in, final int in_off, final float[] out, final int out_off, final int offset, final int isign) {
+ float t1i, t1r;
+ int iw1;
+ iw1 = offset;
+ int idx = ido * l1;
+ if (ido <= 2) {
+ for (int k = 0; k < l1; k++) {
+ int idx0 = k * ido;
+ int iidx1 = in_off + 2 * idx0;
+ int iidx2 = iidx1 + ido;
+ float a1r = in[iidx1];
+ float a1i = in[iidx1 + 1];
+ float a2r = in[iidx2];
+ float a2i = in[iidx2 + 1];
+
+ int oidx1 = out_off + idx0;
+ int oidx2 = oidx1 + idx;
+ out[oidx1] = a1r + a2r;
+ out[oidx1 + 1] = a1i + a2i;
+ out[oidx2] = a1r - a2r;
+ out[oidx2 + 1] = a1i - a2i;
+ }
+ } else {
+ for (int k = 0; k < l1; k++) {
+ for (int i = 0; i < ido - 1; i += 2) {
+ int idx0 = k * ido;
+ int iidx1 = in_off + i + 2 * idx0;
+ int iidx2 = iidx1 + ido;
+ float i1r = in[iidx1];
+ float i1i = in[iidx1 + 1];
+ float i2r = in[iidx2];
+ float i2i = in[iidx2 + 1];
+
+ int widx1 = i + iw1;
+ float w1r = wtable[widx1];
+ float w1i = isign * wtable[widx1 + 1];
+
+ t1r = i1r - i2r;
+ t1i = i1i - i2i;
+
+ int oidx1 = out_off + i + idx0;
+ int oidx2 = oidx1 + idx;
+ out[oidx1] = i1r + i2r;
+ out[oidx1 + 1] = i1i + i2i;
+ out[oidx2] = w1r * t1r - w1i * t1i;
+ out[oidx2 + 1] = w1r * t1i + w1i * t1r;
+ }
+ }
+ }
+ }
+
+ /*----------------------------------------------------------------------
+ passf3: Complex FFT's forward/backward processing of factor 3;
+ isign is +1 for backward and -1 for forward transforms
+ ----------------------------------------------------------------------*/
+ void passf3(final int ido, final int l1, final float[] in, final int in_off, final float[] out, final int out_off, final int offset, final int isign) {
+ final float taur = -0.5f;
+ final float taui = 0.866025403784438707610604524234076962f;
+ float ci2, ci3, di2, di3, cr2, cr3, dr2, dr3, ti2, tr2;
+ int iw1, iw2;
+
+ iw1 = offset;
+ iw2 = iw1 + ido;
+
+ final int idxt = l1 * ido;
+
+ if (ido == 2) {
+ for (int k = 1; k <= l1; k++) {
+ int iidx1 = in_off + (3 * k - 2) * ido;
+ int iidx2 = iidx1 + ido;
+ int iidx3 = iidx1 - ido;
+ float i1r = in[iidx1];
+ float i1i = in[iidx1 + 1];
+ float i2r = in[iidx2];
+ float i2i = in[iidx2 + 1];
+ float i3r = in[iidx3];
+ float i3i = in[iidx3 + 1];
+
+ tr2 = i1r + i2r;
+ cr2 = i3r + taur * tr2;
+ ti2 = i1i + i2i;
+ ci2 = i3i + taur * ti2;
+ cr3 = isign * taui * (i1r - i2r);
+ ci3 = isign * taui * (i1i - i2i);
+
+ int oidx1 = out_off + (k - 1) * ido;
+ int oidx2 = oidx1 + idxt;
+ int oidx3 = oidx2 + idxt;
+ out[oidx1] = in[iidx3] + tr2;
+ out[oidx1 + 1] = i3i + ti2;
+ out[oidx2] = cr2 - ci3;
+ out[oidx2 + 1] = ci2 + cr3;
+ out[oidx3] = cr2 + ci3;
+ out[oidx3 + 1] = ci2 - cr3;
+ }
+ } else {
+ for (int k = 1; k <= l1; k++) {
+ int idx1 = in_off + (3 * k - 2) * ido;
+ int idx2 = out_off + (k - 1) * ido;
+ for (int i = 0; i < ido - 1; i += 2) {
+ int iidx1 = i + idx1;
+ int iidx2 = iidx1 + ido;
+ int iidx3 = iidx1 - ido;
+ float a1r = in[iidx1];
+ float a1i = in[iidx1 + 1];
+ float a2r = in[iidx2];
+ float a2i = in[iidx2 + 1];
+ float a3r = in[iidx3];
+ float a3i = in[iidx3 + 1];
+
+ tr2 = a1r + a2r;
+ cr2 = a3r + taur * tr2;
+ ti2 = a1i + a2i;
+ ci2 = a3i + taur * ti2;
+ cr3 = isign * taui * (a1r - a2r);
+ ci3 = isign * taui * (a1i - a2i);
+ dr2 = cr2 - ci3;
+ dr3 = cr2 + ci3;
+ di2 = ci2 + cr3;
+ di3 = ci2 - cr3;
+
+ int widx1 = i + iw1;
+ int widx2 = i + iw2;
+ float w1r = wtable[widx1];
+ float w1i = isign * wtable[widx1 + 1];
+ float w2r = wtable[widx2];
+ float w2i = isign * wtable[widx2 + 1];
+
+ int oidx1 = i + idx2;
+ int oidx2 = oidx1 + idxt;
+ int oidx3 = oidx2 + idxt;
+ out[oidx1] = a3r + tr2;
+ out[oidx1 + 1] = a3i + ti2;
+ out[oidx2] = w1r * dr2 - w1i * di2;
+ out[oidx2 + 1] = w1r * di2 + w1i * dr2;
+ out[oidx3] = w2r * dr3 - w2i * di3;
+ out[oidx3 + 1] = w2r * di3 + w2i * dr3;
+ }
+ }
+ }
+ }
+
+ /*----------------------------------------------------------------------
+ passf4: Complex FFT's forward/backward processing of factor 4;
+ isign is +1 for backward and -1 for forward transforms
+ ----------------------------------------------------------------------*/
+ void passf4(final int ido, final int l1, final float[] in, final int in_off, final float[] out, final int out_off, final int offset, final int isign) {
+ float ci2, ci3, ci4, cr2, cr3, cr4, ti1, ti2, ti3, ti4, tr1, tr2, tr3, tr4;
+ int iw1, iw2, iw3;
+ iw1 = offset;
+ iw2 = iw1 + ido;
+ iw3 = iw2 + ido;
+
+ int idx0 = l1 * ido;
+ if (ido == 2) {
+ for (int k = 0; k < l1; k++) {
+ int idxt1 = k * ido;
+ int iidx1 = in_off + 4 * idxt1 + 1;
+ int iidx2 = iidx1 + ido;
+ int iidx3 = iidx2 + ido;
+ int iidx4 = iidx3 + ido;
+
+ float i1i = in[iidx1 - 1];
+ float i1r = in[iidx1];
+ float i2i = in[iidx2 - 1];
+ float i2r = in[iidx2];
+ float i3i = in[iidx3 - 1];
+ float i3r = in[iidx3];
+ float i4i = in[iidx4 - 1];
+ float i4r = in[iidx4];
+
+ ti1 = i1r - i3r;
+ ti2 = i1r + i3r;
+ tr4 = i4r - i2r;
+ ti3 = i2r + i4r;
+ tr1 = i1i - i3i;
+ tr2 = i1i + i3i;
+ ti4 = i2i - i4i;
+ tr3 = i2i + i4i;
+
+ int oidx1 = out_off + idxt1;
+ int oidx2 = oidx1 + idx0;
+ int oidx3 = oidx2 + idx0;
+ int oidx4 = oidx3 + idx0;
+ out[oidx1] = tr2 + tr3;
+ out[oidx1 + 1] = ti2 + ti3;
+ out[oidx2] = tr1 + isign * tr4;
+ out[oidx2 + 1] = ti1 + isign * ti4;
+ out[oidx3] = tr2 - tr3;
+ out[oidx3 + 1] = ti2 - ti3;
+ out[oidx4] = tr1 - isign * tr4;
+ out[oidx4 + 1] = ti1 - isign * ti4;
+ }
+ } else {
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int idx2 = in_off + 1 + 4 * idx1;
+ for (int i = 0; i < ido - 1; i += 2) {
+ int iidx1 = i + idx2;
+ int iidx2 = iidx1 + ido;
+ int iidx3 = iidx2 + ido;
+ int iidx4 = iidx3 + ido;
+ float i1i = in[iidx1 - 1];
+ float i1r = in[iidx1];
+ float i2i = in[iidx2 - 1];
+ float i2r = in[iidx2];
+ float i3i = in[iidx3 - 1];
+ float i3r = in[iidx3];
+ float i4i = in[iidx4 - 1];
+ float i4r = in[iidx4];
+
+ ti1 = i1r - i3r;
+ ti2 = i1r + i3r;
+ ti3 = i2r + i4r;
+ tr4 = i4r - i2r;
+ tr1 = i1i - i3i;
+ tr2 = i1i + i3i;
+ ti4 = i2i - i4i;
+ tr3 = i2i + i4i;
+ cr3 = tr2 - tr3;
+ ci3 = ti2 - ti3;
+ cr2 = tr1 + isign * tr4;
+ cr4 = tr1 - isign * tr4;
+ ci2 = ti1 + isign * ti4;
+ ci4 = ti1 - isign * ti4;
+
+ int widx1 = i + iw1;
+ int widx2 = i + iw2;
+ int widx3 = i + iw3;
+ float w1r = wtable[widx1];
+ float w1i = isign * wtable[widx1 + 1];
+ float w2r = wtable[widx2];
+ float w2i = isign * wtable[widx2 + 1];
+ float w3r = wtable[widx3];
+ float w3i = isign * wtable[widx3 + 1];
+
+ int oidx1 = out_off + i + idx1;
+ int oidx2 = oidx1 + idx0;
+ int oidx3 = oidx2 + idx0;
+ int oidx4 = oidx3 + idx0;
+ out[oidx1] = tr2 + tr3;
+ out[oidx1 + 1] = ti2 + ti3;
+ out[oidx2] = w1r * cr2 - w1i * ci2;
+ out[oidx2 + 1] = w1r * ci2 + w1i * cr2;
+ out[oidx3] = w2r * cr3 - w2i * ci3;
+ out[oidx3 + 1] = w2r * ci3 + w2i * cr3;
+ out[oidx4] = w3r * cr4 - w3i * ci4;
+ out[oidx4 + 1] = w3r * ci4 + w3i * cr4;
+ }
+ }
+ }
+ }
+
+ /*----------------------------------------------------------------------
+ passf5: Complex FFT's forward/backward processing of factor 5;
+ isign is +1 for backward and -1 for forward transforms
+ ----------------------------------------------------------------------*/
+ void passf5(final int ido, final int l1, final float[] in, final int in_off, final float[] out, final int out_off, final int offset, final int isign)
+ /* isign==-1 for forward transform and+1 for backward transform */
+ {
+ final float tr11 = 0.309016994374947451262869435595348477f;
+ final float ti11 = 0.951056516295153531181938433292089030f;
+ final float tr12 = -0.809016994374947340240566973079694435f;
+ final float ti12 = 0.587785252292473248125759255344746634f;
+ float ci2, ci3, ci4, ci5, di3, di4, di5, di2, cr2, cr3, cr5, cr4, ti2, ti3, ti4, ti5, dr3, dr4, dr5, dr2, tr2, tr3, tr4, tr5;
+ int iw1, iw2, iw3, iw4;
+
+ iw1 = offset;
+ iw2 = iw1 + ido;
+ iw3 = iw2 + ido;
+ iw4 = iw3 + ido;
+
+ int idx0 = l1 * ido;
+
+ if (ido == 2) {
+ for (int k = 1; k <= l1; ++k) {
+ int iidx1 = in_off + (5 * k - 4) * ido + 1;
+ int iidx2 = iidx1 + ido;
+ int iidx3 = iidx1 - ido;
+ int iidx4 = iidx2 + ido;
+ int iidx5 = iidx4 + ido;
+
+ float i1i = in[iidx1 - 1];
+ float i1r = in[iidx1];
+ float i2i = in[iidx2 - 1];
+ float i2r = in[iidx2];
+ float i3i = in[iidx3 - 1];
+ float i3r = in[iidx3];
+ float i4i = in[iidx4 - 1];
+ float i4r = in[iidx4];
+ float i5i = in[iidx5 - 1];
+ float i5r = in[iidx5];
+
+ ti5 = i1r - i5r;
+ ti2 = i1r + i5r;
+ ti4 = i2r - i4r;
+ ti3 = i2r + i4r;
+ tr5 = i1i - i5i;
+ tr2 = i1i + i5i;
+ tr4 = i2i - i4i;
+ tr3 = i2i + i4i;
+ cr2 = i3i + tr11 * tr2 + tr12 * tr3;
+ ci2 = i3r + tr11 * ti2 + tr12 * ti3;
+ cr3 = i3i + tr12 * tr2 + tr11 * tr3;
+ ci3 = i3r + tr12 * ti2 + tr11 * ti3;
+ cr5 = isign * (ti11 * tr5 + ti12 * tr4);
+ ci5 = isign * (ti11 * ti5 + ti12 * ti4);
+ cr4 = isign * (ti12 * tr5 - ti11 * tr4);
+ ci4 = isign * (ti12 * ti5 - ti11 * ti4);
+
+ int oidx1 = out_off + (k - 1) * ido;
+ int oidx2 = oidx1 + idx0;
+ int oidx3 = oidx2 + idx0;
+ int oidx4 = oidx3 + idx0;
+ int oidx5 = oidx4 + idx0;
+ out[oidx1] = i3i + tr2 + tr3;
+ out[oidx1 + 1] = i3r + ti2 + ti3;
+ out[oidx2] = cr2 - ci5;
+ out[oidx2 + 1] = ci2 + cr5;
+ out[oidx3] = cr3 - ci4;
+ out[oidx3 + 1] = ci3 + cr4;
+ out[oidx4] = cr3 + ci4;
+ out[oidx4 + 1] = ci3 - cr4;
+ out[oidx5] = cr2 + ci5;
+ out[oidx5 + 1] = ci2 - cr5;
+ }
+ } else {
+ for (int k = 1; k <= l1; k++) {
+ int idx1 = in_off + 1 + (k * 5 - 4) * ido;
+ int idx2 = out_off + (k - 1) * ido;
+ for (int i = 0; i < ido - 1; i += 2) {
+ int iidx1 = i + idx1;
+ int iidx2 = iidx1 + ido;
+ int iidx3 = iidx1 - ido;
+ int iidx4 = iidx2 + ido;
+ int iidx5 = iidx4 + ido;
+ float i1i = in[iidx1 - 1];
+ float i1r = in[iidx1];
+ float i2i = in[iidx2 - 1];
+ float i2r = in[iidx2];
+ float i3i = in[iidx3 - 1];
+ float i3r = in[iidx3];
+ float i4i = in[iidx4 - 1];
+ float i4r = in[iidx4];
+ float i5i = in[iidx5 - 1];
+ float i5r = in[iidx5];
+
+ ti5 = i1r - i5r;
+ ti2 = i1r + i5r;
+ ti4 = i2r - i4r;
+ ti3 = i2r + i4r;
+ tr5 = i1i - i5i;
+ tr2 = i1i + i5i;
+ tr4 = i2i - i4i;
+ tr3 = i2i + i4i;
+ cr2 = i3i + tr11 * tr2 + tr12 * tr3;
+ ci2 = i3r + tr11 * ti2 + tr12 * ti3;
+ cr3 = i3i + tr12 * tr2 + tr11 * tr3;
+ ci3 = i3r + tr12 * ti2 + tr11 * ti3;
+ cr5 = isign * (ti11 * tr5 + ti12 * tr4);
+ ci5 = isign * (ti11 * ti5 + ti12 * ti4);
+ cr4 = isign * (ti12 * tr5 - ti11 * tr4);
+ ci4 = isign * (ti12 * ti5 - ti11 * ti4);
+ dr3 = cr3 - ci4;
+ dr4 = cr3 + ci4;
+ di3 = ci3 + cr4;
+ di4 = ci3 - cr4;
+ dr5 = cr2 + ci5;
+ dr2 = cr2 - ci5;
+ di5 = ci2 - cr5;
+ di2 = ci2 + cr5;
+
+ int widx1 = i + iw1;
+ int widx2 = i + iw2;
+ int widx3 = i + iw3;
+ int widx4 = i + iw4;
+ float w1r = wtable[widx1];
+ float w1i = isign * wtable[widx1 + 1];
+ float w2r = wtable[widx2];
+ float w2i = isign * wtable[widx2 + 1];
+ float w3r = wtable[widx3];
+ float w3i = isign * wtable[widx3 + 1];
+ float w4r = wtable[widx4];
+ float w4i = isign * wtable[widx4 + 1];
+
+ int oidx1 = i + idx2;
+ int oidx2 = oidx1 + idx0;
+ int oidx3 = oidx2 + idx0;
+ int oidx4 = oidx3 + idx0;
+ int oidx5 = oidx4 + idx0;
+ out[oidx1] = i3i + tr2 + tr3;
+ out[oidx1 + 1] = i3r + ti2 + ti3;
+ out[oidx2] = w1r * dr2 - w1i * di2;
+ out[oidx2 + 1] = w1r * di2 + w1i * dr2;
+ out[oidx3] = w2r * dr3 - w2i * di3;
+ out[oidx3 + 1] = w2r * di3 + w2i * dr3;
+ out[oidx4] = w3r * dr4 - w3i * di4;
+ out[oidx4 + 1] = w3r * di4 + w3i * dr4;
+ out[oidx5] = w4r * dr5 - w4i * di5;
+ out[oidx5 + 1] = w4r * di5 + w4i * dr5;
+ }
+ }
+ }
+ }
+
+ /*----------------------------------------------------------------------
+ passfg: Complex FFT's forward/backward processing of general factor;
+ isign is +1 for backward and -1 for forward transforms
+ ----------------------------------------------------------------------*/
+ void passfg(final int[] nac, final int ido, final int ip, final int l1, final int idl1, final float[] in, final int in_off, final float[] out, final int out_off, final int offset, final int isign) {
+ int idij, idlj, idot, ipph, l, jc, lc, idj, idl, inc, idp;
+ float w1r, w1i, w2i, w2r;
+ int iw1;
+
+ iw1 = offset;
+ idot = ido / 2;
+ ipph = (ip + 1) / 2;
+ idp = ip * ido;
+ if (ido >= l1) {
+ for (int j = 1; j < ipph; j++) {
+ jc = ip - j;
+ int idx1 = j * ido;
+ int idx2 = jc * ido;
+ for (int k = 0; k < l1; k++) {
+ int idx3 = k * ido;
+ int idx4 = idx3 + idx1 * l1;
+ int idx5 = idx3 + idx2 * l1;
+ int idx6 = idx3 * ip;
+ for (int i = 0; i < ido; i++) {
+ int oidx1 = out_off + i;
+ float i1r = in[in_off + i + idx1 + idx6];
+ float i2r = in[in_off + i + idx2 + idx6];
+ out[oidx1 + idx4] = i1r + i2r;
+ out[oidx1 + idx5] = i1r - i2r;
+ }
+ }
+ }
+ for (int k = 0; k < l1; k++) {
+ int idxt1 = k * ido;
+ int idxt2 = idxt1 * ip;
+ for (int i = 0; i < ido; i++) {
+ out[out_off + i + idxt1] = in[in_off + i + idxt2];
+ }
+ }
+ } else {
+ for (int j = 1; j < ipph; j++) {
+ jc = ip - j;
+ int idxt1 = j * l1 * ido;
+ int idxt2 = jc * l1 * ido;
+ int idxt3 = j * ido;
+ int idxt4 = jc * ido;
+ for (int i = 0; i < ido; i++) {
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ int idx2 = idx1 * ip;
+ int idx3 = out_off + i;
+ int idx4 = in_off + i;
+ float i1r = in[idx4 + idxt3 + idx2];
+ float i2r = in[idx4 + idxt4 + idx2];
+ out[idx3 + idx1 + idxt1] = i1r + i2r;
+ out[idx3 + idx1 + idxt2] = i1r - i2r;
+ }
+ }
+ }
+ for (int i = 0; i < ido; i++) {
+ for (int k = 0; k < l1; k++) {
+ int idx1 = k * ido;
+ out[out_off + i + idx1] = in[in_off + i + idx1 * ip];
+ }
+ }
+ }
+
+ idl = 2 - ido;
+ inc = 0;
+ int idxt0 = (ip - 1) * idl1;
+ for (l = 1; l < ipph; l++) {
+ lc = ip - l;
+ idl += ido;
+ int idxt1 = l * idl1;
+ int idxt2 = lc * idl1;
+ int idxt3 = idl + iw1;
+ w1r = wtable[idxt3 - 2];
+ w1i = isign * wtable[idxt3 - 1];
+ for (int ik = 0; ik < idl1; ik++) {
+ int idx1 = in_off + ik;
+ int idx2 = out_off + ik;
+ in[idx1 + idxt1] = out[idx2] + w1r * out[idx2 + idl1];
+ in[idx1 + idxt2] = w1i * out[idx2 + idxt0];
+ }
+ idlj = idl;
+ inc += ido;
+ for (int j = 2; j < ipph; j++) {
+ jc = ip - j;
+ idlj += inc;
+ if (idlj > idp)
+ idlj -= idp;
+ int idxt4 = idlj + iw1;
+ w2r = wtable[idxt4 - 2];
+ w2i = isign * wtable[idxt4 - 1];
+ int idxt5 = j * idl1;
+ int idxt6 = jc * idl1;
+ for (int ik = 0; ik < idl1; ik++) {
+ int idx1 = in_off + ik;
+ int idx2 = out_off + ik;
+ in[idx1 + idxt1] += w2r * out[idx2 + idxt5];
+ in[idx1 + idxt2] += w2i * out[idx2 + idxt6];
+ }
+ }
+ }
+ for (int j = 1; j < ipph; j++) {
+ int idxt1 = j * idl1;
+ for (int ik = 0; ik < idl1; ik++) {
+ int idx1 = out_off + ik;
+ out[idx1] += out[idx1 + idxt1];
+ }
+ }
+ for (int j = 1; j < ipph; j++) {
+ jc = ip - j;
+ int idx1 = j * idl1;
+ int idx2 = jc * idl1;
+ for (int ik = 1; ik < idl1; ik += 2) {
+ int idx3 = out_off + ik;
+ int idx4 = in_off + ik;
+ int iidx1 = idx4 + idx1;
+ int iidx2 = idx4 + idx2;
+ float i1i = in[iidx1 - 1];
+ float i1r = in[iidx1];
+ float i2i = in[iidx2 - 1];
+ float i2r = in[iidx2];
+
+ int oidx1 = idx3 + idx1;
+ int oidx2 = idx3 + idx2;
+ out[oidx1 - 1] = i1i - i2r;
+ out[oidx2 - 1] = i1i + i2r;
+ out[oidx1] = i1r + i2i;
+ out[oidx2] = i1r - i2i;
+ }
+ }
+ nac[0] = 1;
+ if (ido == 2)
+ return;
+ nac[0] = 0;
+ System.arraycopy(out, out_off, in, in_off, idl1);
+ int idx0 = l1 * ido;
+ for (int j = 1; j < ip; j++) {
+ int idx1 = j * idx0;
+ for (int k = 0; k < l1; k++) {
+ int idx2 = k * ido;
+ int oidx1 = out_off + idx2 + idx1;
+ int iidx1 = in_off + idx2 + idx1;
+ in[iidx1] = out[oidx1];
+ in[iidx1 + 1] = out[oidx1 + 1];
+ }
+ }
+ if (idot <= l1) {
+ idij = 0;
+ for (int j = 1; j < ip; j++) {
+ idij += 2;
+ int idx1 = j * l1 * ido;
+ for (int i = 3; i < ido; i += 2) {
+ idij += 2;
+ int idx2 = idij + iw1 - 1;
+ w1r = wtable[idx2 - 1];
+ w1i = isign * wtable[idx2];
+ int idx3 = in_off + i;
+ int idx4 = out_off + i;
+ for (int k = 0; k < l1; k++) {
+ int idx5 = k * ido + idx1;
+ int iidx1 = idx3 + idx5;
+ int oidx1 = idx4 + idx5;
+ float o1i = out[oidx1 - 1];
+ float o1r = out[oidx1];
+ in[iidx1 - 1] = w1r * o1i - w1i * o1r;
+ in[iidx1] = w1r * o1r + w1i * o1i;
+ }
+ }
+ }
+ } else {
+ idj = 2 - ido;
+ for (int j = 1; j < ip; j++) {
+ idj += ido;
+ int idx1 = j * l1 * ido;
+ for (int k = 0; k < l1; k++) {
+ idij = idj;
+ int idx3 = k * ido + idx1;
+ for (int i = 3; i < ido; i += 2) {
+ idij += 2;
+ int idx2 = idij - 1 + iw1;
+ w1r = wtable[idx2 - 1];
+ w1i = isign * wtable[idx2];
+ int iidx1 = in_off + i + idx3;
+ int oidx1 = out_off + i + idx3;
+ float o1i = out[oidx1 - 1];
+ float o1r = out[oidx1];
+ in[iidx1 - 1] = w1r * o1i - w1i * o1r;
+ in[iidx1] = w1r * o1r + w1i * o1i;
+ }
+ }
+ }
+ }
+ }
+
+ private void cftfsub(int n, float[] a, int offa, int[] ip, int nw, float[] w) {
+ if (n > 8) {
+ if (n > 32) {
+ cftf1st(n, a, offa, w, nw - (n >> 2));
+ if ((ConcurrencyUtils.getNumberOfThreads() > 1) && (n > ConcurrencyUtils.getThreadsBeginN_1D_FFT_2Threads())) {
+ cftrec4_th(n, a, offa, nw, w);
+ } else if (n > 512) {
+ cftrec4(n, a, offa, nw, w);
+ } else if (n > 128) {
+ cftleaf(n, 1, a, offa, nw, w);
+ } else {
+ cftfx41(n, a, offa, nw, w);
+ }
+ bitrv2(n, ip, a, offa);
+ } else if (n == 32) {
+ cftf161(a, offa, w, nw - 8);
+ bitrv216(a, offa);
+ } else {
+ cftf081(a, offa, w, 0);
+ bitrv208(a, offa);
+ }
+ } else if (n == 8) {
+ cftf040(a, offa);
+ } else if (n == 4) {
+ cftxb020(a, offa);
+ }
+ }
+
+ private void cftbsub(int n, float[] a, int offa, int[] ip, int nw, float[] w) {
+ if (n > 8) {
+ if (n > 32) {
+ cftb1st(n, a, offa, w, nw - (n >> 2));
+ if ((ConcurrencyUtils.getNumberOfThreads() > 1) && (n > ConcurrencyUtils.getThreadsBeginN_1D_FFT_2Threads())) {
+ cftrec4_th(n, a, offa, nw, w);
+ } else if (n > 512) {
+ cftrec4(n, a, offa, nw, w);
+ } else if (n > 128) {
+ cftleaf(n, 1, a, offa, nw, w);
+ } else {
+ cftfx41(n, a, offa, nw, w);
+ }
+ bitrv2conj(n, ip, a, offa);
+ } else if (n == 32) {
+ cftf161(a, offa, w, nw - 8);
+ bitrv216neg(a, offa);
+ } else {
+ cftf081(a, offa, w, 0);
+ bitrv208neg(a, offa);
+ }
+ } else if (n == 8) {
+ cftb040(a, offa);
+ } else if (n == 4) {
+ cftxb020(a, offa);
+ }
+ }
+
+ private void bitrv2(int n, int[] ip, float[] a, int offa) {
+ int j1, k1, l, m, nh, nm;
+ float xr, xi, yr, yi;
+ int idx0, idx1, idx2;
+
+ m = 1;
+ for (l = n >> 2; l > 8; l >>= 2) {
+ m <<= 1;
+ }
+ nh = n >> 1;
+ nm = 4 * m;
+ if (l == 8) {
+ for (int k = 0; k < m; k++) {
+ idx0 = 4 * k;
+ for (int j = 0; j < k; j++) {
+ j1 = 4 * j + 2 * ip[m + k];
+ k1 = idx0 + 2 * ip[m + j];
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 += 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 -= nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 += 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nh;
+ k1 += 2;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 -= 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 += nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 -= 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += 2;
+ k1 += nh;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 += 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 -= nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 += 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nh;
+ k1 -= 2;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 -= 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 += nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 -= 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ }
+ k1 = idx0 + 2 * ip[m + k];
+ j1 = k1 + 2;
+ k1 += nh;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 += 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 -= nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= 2;
+ k1 -= nh;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nh + 2;
+ k1 += nh + 2;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nh - nm;
+ k1 += 2 * nm - 2;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ }
+ } else {
+ for (int k = 0; k < m; k++) {
+ idx0 = 4 * k;
+ for (int j = 0; j < k; j++) {
+ j1 = 4 * j + ip[m + k];
+ k1 = idx0 + ip[m + j];
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 += nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nh;
+ k1 += 2;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 -= nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += 2;
+ k1 += nh;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 += nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nh;
+ k1 -= 2;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 -= nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ }
+ k1 = idx0 + ip[m + k];
+ j1 = k1 + 2;
+ k1 += nh;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 += nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = a[idx1 + 1];
+ yr = a[idx2];
+ yi = a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ }
+ }
+ }
+
+ private void bitrv2conj(int n, int[] ip, float[] a, int offa) {
+ int j1, k1, l, m, nh, nm;
+ float xr, xi, yr, yi;
+ int idx0, idx1, idx2;
+
+ m = 1;
+ for (l = n >> 2; l > 8; l >>= 2) {
+ m <<= 1;
+ }
+ nh = n >> 1;
+ nm = 4 * m;
+ if (l == 8) {
+ for (int k = 0; k < m; k++) {
+ idx0 = 4 * k;
+ for (int j = 0; j < k; j++) {
+ j1 = 4 * j + 2 * ip[m + k];
+ k1 = idx0 + 2 * ip[m + j];
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 += 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 -= nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 += 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nh;
+ k1 += 2;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 -= 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 += nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 -= 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += 2;
+ k1 += nh;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 += 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 -= nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 += 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nh;
+ k1 -= 2;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 -= 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 += nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 -= 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ }
+ k1 = idx0 + 2 * ip[m + k];
+ j1 = k1 + 2;
+ k1 += nh;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ a[idx1 - 1] = -a[idx1 - 1];
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ a[idx2 + 3] = -a[idx2 + 3];
+ j1 += nm;
+ k1 += 2 * nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 -= nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= 2;
+ k1 -= nh;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nh + 2;
+ k1 += nh + 2;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nh - nm;
+ k1 += 2 * nm - 2;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ a[idx1 - 1] = -a[idx1 - 1];
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ a[idx2 + 3] = -a[idx2 + 3];
+ }
+ } else {
+ for (int k = 0; k < m; k++) {
+ idx0 = 4 * k;
+ for (int j = 0; j < k; j++) {
+ j1 = 4 * j + ip[m + k];
+ k1 = idx0 + ip[m + j];
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 += nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nh;
+ k1 += 2;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 -= nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += 2;
+ k1 += nh;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 += nm;
+ k1 += nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nh;
+ k1 -= 2;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ j1 -= nm;
+ k1 -= nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ }
+ k1 = idx0 + ip[m + k];
+ j1 = k1 + 2;
+ k1 += nh;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ a[idx1 - 1] = -a[idx1 - 1];
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ a[idx2 + 3] = -a[idx2 + 3];
+ j1 += nm;
+ k1 += nm;
+ idx1 = offa + j1;
+ idx2 = offa + k1;
+ a[idx1 - 1] = -a[idx1 - 1];
+ xr = a[idx1];
+ xi = -a[idx1 + 1];
+ yr = a[idx2];
+ yi = -a[idx2 + 1];
+ a[idx1] = yr;
+ a[idx1 + 1] = yi;
+ a[idx2] = xr;
+ a[idx2 + 1] = xi;
+ a[idx2 + 3] = -a[idx2 + 3];
+ }
+ }
+ }
+
+ private void bitrv216(float[] a, int offa) {
+ float x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, x5r, x5i, x7r, x7i, x8r, x8i, x10r, x10i, x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i;
+
+ x1r = a[offa + 2];
+ x1i = a[offa + 3];
+ x2r = a[offa + 4];
+ x2i = a[offa + 5];
+ x3r = a[offa + 6];
+ x3i = a[offa + 7];
+ x4r = a[offa + 8];
+ x4i = a[offa + 9];
+ x5r = a[offa + 10];
+ x5i = a[offa + 11];
+ x7r = a[offa + 14];
+ x7i = a[offa + 15];
+ x8r = a[offa + 16];
+ x8i = a[offa + 17];
+ x10r = a[offa + 20];
+ x10i = a[offa + 21];
+ x11r = a[offa + 22];
+ x11i = a[offa + 23];
+ x12r = a[offa + 24];
+ x12i = a[offa + 25];
+ x13r = a[offa + 26];
+ x13i = a[offa + 27];
+ x14r = a[offa + 28];
+ x14i = a[offa + 29];
+ a[offa + 2] = x8r;
+ a[offa + 3] = x8i;
+ a[offa + 4] = x4r;
+ a[offa + 5] = x4i;
+ a[offa + 6] = x12r;
+ a[offa + 7] = x12i;
+ a[offa + 8] = x2r;
+ a[offa + 9] = x2i;
+ a[offa + 10] = x10r;
+ a[offa + 11] = x10i;
+ a[offa + 14] = x14r;
+ a[offa + 15] = x14i;
+ a[offa + 16] = x1r;
+ a[offa + 17] = x1i;
+ a[offa + 20] = x5r;
+ a[offa + 21] = x5i;
+ a[offa + 22] = x13r;
+ a[offa + 23] = x13i;
+ a[offa + 24] = x3r;
+ a[offa + 25] = x3i;
+ a[offa + 26] = x11r;
+ a[offa + 27] = x11i;
+ a[offa + 28] = x7r;
+ a[offa + 29] = x7i;
+ }
+
+ private void bitrv216neg(float[] a, int offa) {
+ float x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, x5r, x5i, x6r, x6i, x7r, x7i, x8r, x8i, x9r, x9i, x10r, x10i, x11r, x11i, x12r, x12i, x13r, x13i, x14r, x14i, x15r, x15i;
+
+ x1r = a[offa + 2];
+ x1i = a[offa + 3];
+ x2r = a[offa + 4];
+ x2i = a[offa + 5];
+ x3r = a[offa + 6];
+ x3i = a[offa + 7];
+ x4r = a[offa + 8];
+ x4i = a[offa + 9];
+ x5r = a[offa + 10];
+ x5i = a[offa + 11];
+ x6r = a[offa + 12];
+ x6i = a[offa + 13];
+ x7r = a[offa + 14];
+ x7i = a[offa + 15];
+ x8r = a[offa + 16];
+ x8i = a[offa + 17];
+ x9r = a[offa + 18];
+ x9i = a[offa + 19];
+ x10r = a[offa + 20];
+ x10i = a[offa + 21];
+ x11r = a[offa + 22];
+ x11i = a[offa + 23];
+ x12r = a[offa + 24];
+ x12i = a[offa + 25];
+ x13r = a[offa + 26];
+ x13i = a[offa + 27];
+ x14r = a[offa + 28];
+ x14i = a[offa + 29];
+ x15r = a[offa + 30];
+ x15i = a[offa + 31];
+ a[offa + 2] = x15r;
+ a[offa + 3] = x15i;
+ a[offa + 4] = x7r;
+ a[offa + 5] = x7i;
+ a[offa + 6] = x11r;
+ a[offa + 7] = x11i;
+ a[offa + 8] = x3r;
+ a[offa + 9] = x3i;
+ a[offa + 10] = x13r;
+ a[offa + 11] = x13i;
+ a[offa + 12] = x5r;
+ a[offa + 13] = x5i;
+ a[offa + 14] = x9r;
+ a[offa + 15] = x9i;
+ a[offa + 16] = x1r;
+ a[offa + 17] = x1i;
+ a[offa + 18] = x14r;
+ a[offa + 19] = x14i;
+ a[offa + 20] = x6r;
+ a[offa + 21] = x6i;
+ a[offa + 22] = x10r;
+ a[offa + 23] = x10i;
+ a[offa + 24] = x2r;
+ a[offa + 25] = x2i;
+ a[offa + 26] = x12r;
+ a[offa + 27] = x12i;
+ a[offa + 28] = x4r;
+ a[offa + 29] = x4i;
+ a[offa + 30] = x8r;
+ a[offa + 31] = x8i;
+ }
+
+ private void bitrv208(float[] a, int offa) {
+ float x1r, x1i, x3r, x3i, x4r, x4i, x6r, x6i;
+
+ x1r = a[offa + 2];
+ x1i = a[offa + 3];
+ x3r = a[offa + 6];
+ x3i = a[offa + 7];
+ x4r = a[offa + 8];
+ x4i = a[offa + 9];
+ x6r = a[offa + 12];
+ x6i = a[offa + 13];
+ a[offa + 2] = x4r;
+ a[offa + 3] = x4i;
+ a[offa + 6] = x6r;
+ a[offa + 7] = x6i;
+ a[offa + 8] = x1r;
+ a[offa + 9] = x1i;
+ a[offa + 12] = x3r;
+ a[offa + 13] = x3i;
+ }
+
+ private void bitrv208neg(float[] a, int offa) {
+ float x1r, x1i, x2r, x2i, x3r, x3i, x4r, x4i, x5r, x5i, x6r, x6i, x7r, x7i;
+
+ x1r = a[offa + 2];
+ x1i = a[offa + 3];
+ x2r = a[offa + 4];
+ x2i = a[offa + 5];
+ x3r = a[offa + 6];
+ x3i = a[offa + 7];
+ x4r = a[offa + 8];
+ x4i = a[offa + 9];
+ x5r = a[offa + 10];
+ x5i = a[offa + 11];
+ x6r = a[offa + 12];
+ x6i = a[offa + 13];
+ x7r = a[offa + 14];
+ x7i = a[offa + 15];
+ a[offa + 2] = x7r;
+ a[offa + 3] = x7i;
+ a[offa + 4] = x3r;
+ a[offa + 5] = x3i;
+ a[offa + 6] = x5r;
+ a[offa + 7] = x5i;
+ a[offa + 8] = x1r;
+ a[offa + 9] = x1i;
+ a[offa + 10] = x6r;
+ a[offa + 11] = x6i;
+ a[offa + 12] = x2r;
+ a[offa + 13] = x2i;
+ a[offa + 14] = x4r;
+ a[offa + 15] = x4i;
+ }
+
+ private void cftf1st(int n, float[] a, int offa, float[] w, int startw) {
+ int j0, j1, j2, j3, k, m, mh;
+ float wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i;
+ float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i;
+ int idx0, idx1, idx2, idx3, idx4, idx5;
+ mh = n >> 3;
+ m = 2 * mh;
+ j1 = m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+ x0r = a[offa] + a[idx2];
+ x0i = a[offa + 1] + a[idx2 + 1];
+ x1r = a[offa] - a[idx2];
+ x1i = a[offa + 1] - a[idx2 + 1];
+ x2r = a[idx1] + a[idx3];
+ x2i = a[idx1 + 1] + a[idx3 + 1];
+ x3r = a[idx1] - a[idx3];
+ x3i = a[idx1 + 1] - a[idx3 + 1];
+ a[offa] = x0r + x2r;
+ a[offa + 1] = x0i + x2i;
+ a[idx1] = x0r - x2r;
+ a[idx1 + 1] = x0i - x2i;
+ a[idx2] = x1r - x3i;
+ a[idx2 + 1] = x1i + x3r;
+ a[idx3] = x1r + x3i;
+ a[idx3 + 1] = x1i - x3r;
+ wn4r = w[startw + 1];
+ csc1 = w[startw + 2];
+ csc3 = w[startw + 3];
+ wd1r = 1;
+ wd1i = 0;
+ wd3r = 1;
+ wd3i = 0;
+ k = 0;
+ for (int j = 2; j < mh - 2; j += 4) {
+ k += 4;
+ idx4 = startw + k;
+ wk1r = csc1 * (wd1r + w[idx4]);
+ wk1i = csc1 * (wd1i + w[idx4 + 1]);
+ wk3r = csc3 * (wd3r + w[idx4 + 2]);
+ wk3i = csc3 * (wd3i + w[idx4 + 3]);
+ wd1r = w[idx4];
+ wd1i = w[idx4 + 1];
+ wd3r = w[idx4 + 2];
+ wd3i = w[idx4 + 3];
+ j1 = j + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+ idx5 = offa + j;
+ x0r = a[idx5] + a[idx2];
+ x0i = a[idx5 + 1] + a[idx2 + 1];
+ x1r = a[idx5] - a[idx2];
+ x1i = a[idx5 + 1] - a[idx2 + 1];
+ y0r = a[idx5 + 2] + a[idx2 + 2];
+ y0i = a[idx5 + 3] + a[idx2 + 3];
+ y1r = a[idx5 + 2] - a[idx2 + 2];
+ y1i = a[idx5 + 3] - a[idx2 + 3];
+ x2r = a[idx1] + a[idx3];
+ x2i = a[idx1 + 1] + a[idx3 + 1];
+ x3r = a[idx1] - a[idx3];
+ x3i = a[idx1 + 1] - a[idx3 + 1];
+ y2r = a[idx1 + 2] + a[idx3 + 2];
+ y2i = a[idx1 + 3] + a[idx3 + 3];
+ y3r = a[idx1 + 2] - a[idx3 + 2];
+ y3i = a[idx1 + 3] - a[idx3 + 3];
+ a[idx5] = x0r + x2r;
+ a[idx5 + 1] = x0i + x2i;
+ a[idx5 + 2] = y0r + y2r;
+ a[idx5 + 3] = y0i + y2i;
+ a[idx1] = x0r - x2r;
+ a[idx1 + 1] = x0i - x2i;
+ a[idx1 + 2] = y0r - y2r;
+ a[idx1 + 3] = y0i - y2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ a[idx2] = wk1r * x0r - wk1i * x0i;
+ a[idx2 + 1] = wk1r * x0i + wk1i * x0r;
+ x0r = y1r - y3i;
+ x0i = y1i + y3r;
+ a[idx2 + 2] = wd1r * x0r - wd1i * x0i;
+ a[idx2 + 3] = wd1r * x0i + wd1i * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ a[idx3] = wk3r * x0r + wk3i * x0i;
+ a[idx3 + 1] = wk3r * x0i - wk3i * x0r;
+ x0r = y1r + y3i;
+ x0i = y1i - y3r;
+ a[idx3 + 2] = wd3r * x0r + wd3i * x0i;
+ a[idx3 + 3] = wd3r * x0i - wd3i * x0r;
+ j0 = m - j;
+ j1 = j0 + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx0 = offa + j0;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+ x0r = a[idx0] + a[idx2];
+ x0i = a[idx0 + 1] + a[idx2 + 1];
+ x1r = a[idx0] - a[idx2];
+ x1i = a[idx0 + 1] - a[idx2 + 1];
+ y0r = a[idx0 - 2] + a[idx2 - 2];
+ y0i = a[idx0 - 1] + a[idx2 - 1];
+ y1r = a[idx0 - 2] - a[idx2 - 2];
+ y1i = a[idx0 - 1] - a[idx2 - 1];
+ x2r = a[idx1] + a[idx3];
+ x2i = a[idx1 + 1] + a[idx3 + 1];
+ x3r = a[idx1] - a[idx3];
+ x3i = a[idx1 + 1] - a[idx3 + 1];
+ y2r = a[idx1 - 2] + a[idx3 - 2];
+ y2i = a[idx1 - 1] + a[idx3 - 1];
+ y3r = a[idx1 - 2] - a[idx3 - 2];
+ y3i = a[idx1 - 1] - a[idx3 - 1];
+ a[idx0] = x0r + x2r;
+ a[idx0 + 1] = x0i + x2i;
+ a[idx0 - 2] = y0r + y2r;
+ a[idx0 - 1] = y0i + y2i;
+ a[idx1] = x0r - x2r;
+ a[idx1 + 1] = x0i - x2i;
+ a[idx1 - 2] = y0r - y2r;
+ a[idx1 - 1] = y0i - y2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ a[idx2] = wk1i * x0r - wk1r * x0i;
+ a[idx2 + 1] = wk1i * x0i + wk1r * x0r;
+ x0r = y1r - y3i;
+ x0i = y1i + y3r;
+ a[idx2 - 2] = wd1i * x0r - wd1r * x0i;
+ a[idx2 - 1] = wd1i * x0i + wd1r * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ a[idx3] = wk3i * x0r + wk3r * x0i;
+ a[idx3 + 1] = wk3i * x0i - wk3r * x0r;
+ x0r = y1r + y3i;
+ x0i = y1i - y3r;
+ a[offa + j3 - 2] = wd3i * x0r + wd3r * x0i;
+ a[offa + j3 - 1] = wd3i * x0i - wd3r * x0r;
+ }
+ wk1r = csc1 * (wd1r + wn4r);
+ wk1i = csc1 * (wd1i + wn4r);
+ wk3r = csc3 * (wd3r - wn4r);
+ wk3i = csc3 * (wd3i - wn4r);
+ j0 = mh;
+ j1 = j0 + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx0 = offa + j0;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+ x0r = a[idx0 - 2] + a[idx2 - 2];
+ x0i = a[idx0 - 1] + a[idx2 - 1];
+ x1r = a[idx0 - 2] - a[idx2 - 2];
+ x1i = a[idx0 - 1] - a[idx2 - 1];
+ x2r = a[idx1 - 2] + a[idx3 - 2];
+ x2i = a[idx1 - 1] + a[idx3 - 1];
+ x3r = a[idx1 - 2] - a[idx3 - 2];
+ x3i = a[idx1 - 1] - a[idx3 - 1];
+ a[idx0 - 2] = x0r + x2r;
+ a[idx0 - 1] = x0i + x2i;
+ a[idx1 - 2] = x0r - x2r;
+ a[idx1 - 1] = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ a[idx2 - 2] = wk1r * x0r - wk1i * x0i;
+ a[idx2 - 1] = wk1r * x0i + wk1i * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ a[idx3 - 2] = wk3r * x0r + wk3i * x0i;
+ a[idx3 - 1] = wk3r * x0i - wk3i * x0r;
+ x0r = a[idx0] + a[idx2];
+ x0i = a[idx0 + 1] + a[idx2 + 1];
+ x1r = a[idx0] - a[idx2];
+ x1i = a[idx0 + 1] - a[idx2 + 1];
+ x2r = a[idx1] + a[idx3];
+ x2i = a[idx1 + 1] + a[idx3 + 1];
+ x3r = a[idx1] - a[idx3];
+ x3i = a[idx1 + 1] - a[idx3 + 1];
+ a[idx0] = x0r + x2r;
+ a[idx0 + 1] = x0i + x2i;
+ a[idx1] = x0r - x2r;
+ a[idx1 + 1] = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ a[idx2] = wn4r * (x0r - x0i);
+ a[idx2 + 1] = wn4r * (x0i + x0r);
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ a[idx3] = -wn4r * (x0r + x0i);
+ a[idx3 + 1] = -wn4r * (x0i - x0r);
+ x0r = a[idx0 + 2] + a[idx2 + 2];
+ x0i = a[idx0 + 3] + a[idx2 + 3];
+ x1r = a[idx0 + 2] - a[idx2 + 2];
+ x1i = a[idx0 + 3] - a[idx2 + 3];
+ x2r = a[idx1 + 2] + a[idx3 + 2];
+ x2i = a[idx1 + 3] + a[idx3 + 3];
+ x3r = a[idx1 + 2] - a[idx3 + 2];
+ x3i = a[idx1 + 3] - a[idx3 + 3];
+ a[idx0 + 2] = x0r + x2r;
+ a[idx0 + 3] = x0i + x2i;
+ a[idx1 + 2] = x0r - x2r;
+ a[idx1 + 3] = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ a[idx2 + 2] = wk1i * x0r - wk1r * x0i;
+ a[idx2 + 3] = wk1i * x0i + wk1r * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ a[idx3 + 2] = wk3i * x0r + wk3r * x0i;
+ a[idx3 + 3] = wk3i * x0i - wk3r * x0r;
+ }
+
+ private void cftb1st(int n, float[] a, int offa, float[] w, int startw) {
+ int j0, j1, j2, j3, k, m, mh;
+ float wn4r, csc1, csc3, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i;
+ float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i;
+ int idx0, idx1, idx2, idx3, idx4, idx5;
+ mh = n >> 3;
+ m = 2 * mh;
+ j1 = m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+
+ x0r = a[offa] + a[idx2];
+ x0i = -a[offa + 1] - a[idx2 + 1];
+ x1r = a[offa] - a[idx2];
+ x1i = -a[offa + 1] + a[idx2 + 1];
+ x2r = a[idx1] + a[idx3];
+ x2i = a[idx1 + 1] + a[idx3 + 1];
+ x3r = a[idx1] - a[idx3];
+ x3i = a[idx1 + 1] - a[idx3 + 1];
+ a[offa] = x0r + x2r;
+ a[offa + 1] = x0i - x2i;
+ a[idx1] = x0r - x2r;
+ a[idx1 + 1] = x0i + x2i;
+ a[idx2] = x1r + x3i;
+ a[idx2 + 1] = x1i + x3r;
+ a[idx3] = x1r - x3i;
+ a[idx3 + 1] = x1i - x3r;
+ wn4r = w[startw + 1];
+ csc1 = w[startw + 2];
+ csc3 = w[startw + 3];
+ wd1r = 1;
+ wd1i = 0;
+ wd3r = 1;
+ wd3i = 0;
+ k = 0;
+ for (int j = 2; j < mh - 2; j += 4) {
+ k += 4;
+ idx4 = startw + k;
+ wk1r = csc1 * (wd1r + w[idx4]);
+ wk1i = csc1 * (wd1i + w[idx4 + 1]);
+ wk3r = csc3 * (wd3r + w[idx4 + 2]);
+ wk3i = csc3 * (wd3i + w[idx4 + 3]);
+ wd1r = w[idx4];
+ wd1i = w[idx4 + 1];
+ wd3r = w[idx4 + 2];
+ wd3i = w[idx4 + 3];
+ j1 = j + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+ idx5 = offa + j;
+ x0r = a[idx5] + a[idx2];
+ x0i = -a[idx5 + 1] - a[idx2 + 1];
+ x1r = a[idx5] - a[offa + j2];
+ x1i = -a[idx5 + 1] + a[idx2 + 1];
+ y0r = a[idx5 + 2] + a[idx2 + 2];
+ y0i = -a[idx5 + 3] - a[idx2 + 3];
+ y1r = a[idx5 + 2] - a[idx2 + 2];
+ y1i = -a[idx5 + 3] + a[idx2 + 3];
+ x2r = a[idx1] + a[idx3];
+ x2i = a[idx1 + 1] + a[idx3 + 1];
+ x3r = a[idx1] - a[idx3];
+ x3i = a[idx1 + 1] - a[idx3 + 1];
+ y2r = a[idx1 + 2] + a[idx3 + 2];
+ y2i = a[idx1 + 3] + a[idx3 + 3];
+ y3r = a[idx1 + 2] - a[idx3 + 2];
+ y3i = a[idx1 + 3] - a[idx3 + 3];
+ a[idx5] = x0r + x2r;
+ a[idx5 + 1] = x0i - x2i;
+ a[idx5 + 2] = y0r + y2r;
+ a[idx5 + 3] = y0i - y2i;
+ a[idx1] = x0r - x2r;
+ a[idx1 + 1] = x0i + x2i;
+ a[idx1 + 2] = y0r - y2r;
+ a[idx1 + 3] = y0i + y2i;
+ x0r = x1r + x3i;
+ x0i = x1i + x3r;
+ a[idx2] = wk1r * x0r - wk1i * x0i;
+ a[idx2 + 1] = wk1r * x0i + wk1i * x0r;
+ x0r = y1r + y3i;
+ x0i = y1i + y3r;
+ a[idx2 + 2] = wd1r * x0r - wd1i * x0i;
+ a[idx2 + 3] = wd1r * x0i + wd1i * x0r;
+ x0r = x1r - x3i;
+ x0i = x1i - x3r;
+ a[idx3] = wk3r * x0r + wk3i * x0i;
+ a[idx3 + 1] = wk3r * x0i - wk3i * x0r;
+ x0r = y1r - y3i;
+ x0i = y1i - y3r;
+ a[idx3 + 2] = wd3r * x0r + wd3i * x0i;
+ a[idx3 + 3] = wd3r * x0i - wd3i * x0r;
+ j0 = m - j;
+ j1 = j0 + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx0 = offa + j0;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+ x0r = a[idx0] + a[idx2];
+ x0i = -a[idx0 + 1] - a[idx2 + 1];
+ x1r = a[idx0] - a[idx2];
+ x1i = -a[idx0 + 1] + a[idx2 + 1];
+ y0r = a[idx0 - 2] + a[idx2 - 2];
+ y0i = -a[idx0 - 1] - a[idx2 - 1];
+ y1r = a[idx0 - 2] - a[idx2 - 2];
+ y1i = -a[idx0 - 1] + a[idx2 - 1];
+ x2r = a[idx1] + a[idx3];
+ x2i = a[idx1 + 1] + a[idx3 + 1];
+ x3r = a[idx1] - a[idx3];
+ x3i = a[idx1 + 1] - a[idx3 + 1];
+ y2r = a[idx1 - 2] + a[idx3 - 2];
+ y2i = a[idx1 - 1] + a[idx3 - 1];
+ y3r = a[idx1 - 2] - a[idx3 - 2];
+ y3i = a[idx1 - 1] - a[idx3 - 1];
+ a[idx0] = x0r + x2r;
+ a[idx0 + 1] = x0i - x2i;
+ a[idx0 - 2] = y0r + y2r;
+ a[idx0 - 1] = y0i - y2i;
+ a[idx1] = x0r - x2r;
+ a[idx1 + 1] = x0i + x2i;
+ a[idx1 - 2] = y0r - y2r;
+ a[idx1 - 1] = y0i + y2i;
+ x0r = x1r + x3i;
+ x0i = x1i + x3r;
+ a[idx2] = wk1i * x0r - wk1r * x0i;
+ a[idx2 + 1] = wk1i * x0i + wk1r * x0r;
+ x0r = y1r + y3i;
+ x0i = y1i + y3r;
+ a[idx2 - 2] = wd1i * x0r - wd1r * x0i;
+ a[idx2 - 1] = wd1i * x0i + wd1r * x0r;
+ x0r = x1r - x3i;
+ x0i = x1i - x3r;
+ a[idx3] = wk3i * x0r + wk3r * x0i;
+ a[idx3 + 1] = wk3i * x0i - wk3r * x0r;
+ x0r = y1r - y3i;
+ x0i = y1i - y3r;
+ a[idx3 - 2] = wd3i * x0r + wd3r * x0i;
+ a[idx3 - 1] = wd3i * x0i - wd3r * x0r;
+ }
+ wk1r = csc1 * (wd1r + wn4r);
+ wk1i = csc1 * (wd1i + wn4r);
+ wk3r = csc3 * (wd3r - wn4r);
+ wk3i = csc3 * (wd3i - wn4r);
+ j0 = mh;
+ j1 = j0 + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx0 = offa + j0;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+ x0r = a[idx0 - 2] + a[idx2 - 2];
+ x0i = -a[idx0 - 1] - a[idx2 - 1];
+ x1r = a[idx0 - 2] - a[idx2 - 2];
+ x1i = -a[idx0 - 1] + a[idx2 - 1];
+ x2r = a[idx1 - 2] + a[idx3 - 2];
+ x2i = a[idx1 - 1] + a[idx3 - 1];
+ x3r = a[idx1 - 2] - a[idx3 - 2];
+ x3i = a[idx1 - 1] - a[idx3 - 1];
+ a[idx0 - 2] = x0r + x2r;
+ a[idx0 - 1] = x0i - x2i;
+ a[idx1 - 2] = x0r - x2r;
+ a[idx1 - 1] = x0i + x2i;
+ x0r = x1r + x3i;
+ x0i = x1i + x3r;
+ a[idx2 - 2] = wk1r * x0r - wk1i * x0i;
+ a[idx2 - 1] = wk1r * x0i + wk1i * x0r;
+ x0r = x1r - x3i;
+ x0i = x1i - x3r;
+ a[idx3 - 2] = wk3r * x0r + wk3i * x0i;
+ a[idx3 - 1] = wk3r * x0i - wk3i * x0r;
+ x0r = a[idx0] + a[idx2];
+ x0i = -a[idx0 + 1] - a[idx2 + 1];
+ x1r = a[idx0] - a[idx2];
+ x1i = -a[idx0 + 1] + a[idx2 + 1];
+ x2r = a[idx1] + a[idx3];
+ x2i = a[idx1 + 1] + a[idx3 + 1];
+ x3r = a[idx1] - a[idx3];
+ x3i = a[idx1 + 1] - a[idx3 + 1];
+ a[idx0] = x0r + x2r;
+ a[idx0 + 1] = x0i - x2i;
+ a[idx1] = x0r - x2r;
+ a[idx1 + 1] = x0i + x2i;
+ x0r = x1r + x3i;
+ x0i = x1i + x3r;
+ a[idx2] = wn4r * (x0r - x0i);
+ a[idx2 + 1] = wn4r * (x0i + x0r);
+ x0r = x1r - x3i;
+ x0i = x1i - x3r;
+ a[idx3] = -wn4r * (x0r + x0i);
+ a[idx3 + 1] = -wn4r * (x0i - x0r);
+ x0r = a[idx0 + 2] + a[idx2 + 2];
+ x0i = -a[idx0 + 3] - a[idx2 + 3];
+ x1r = a[idx0 + 2] - a[idx2 + 2];
+ x1i = -a[idx0 + 3] + a[idx2 + 3];
+ x2r = a[idx1 + 2] + a[idx3 + 2];
+ x2i = a[idx1 + 3] + a[idx3 + 3];
+ x3r = a[idx1 + 2] - a[idx3 + 2];
+ x3i = a[idx1 + 3] - a[idx3 + 3];
+ a[idx0 + 2] = x0r + x2r;
+ a[idx0 + 3] = x0i - x2i;
+ a[idx1 + 2] = x0r - x2r;
+ a[idx1 + 3] = x0i + x2i;
+ x0r = x1r + x3i;
+ x0i = x1i + x3r;
+ a[idx2 + 2] = wk1i * x0r - wk1r * x0i;
+ a[idx2 + 3] = wk1i * x0i + wk1r * x0r;
+ x0r = x1r - x3i;
+ x0i = x1i - x3r;
+ a[idx3 + 2] = wk3i * x0r + wk3r * x0i;
+ a[idx3 + 3] = wk3i * x0i - wk3r * x0r;
+ }
+
+ private void cftrec4_th(final int n, final float[] a, final int offa, final int nw, final float[] w) {
+ int i;
+ int idiv4, m, nthreads;
+ int idx = 0;
+ nthreads = 2;
+ idiv4 = 0;
+ m = n >> 1;
+ if (n > ConcurrencyUtils.getThreadsBeginN_1D_FFT_4Threads()) {
+ nthreads = 4;
+ idiv4 = 1;
+ m >>= 1;
+ }
+ Future>[] futures = new Future[nthreads];
+ final int mf = m;
+ for (i = 0; i < nthreads; i++) {
+ final int firstIdx = offa + i * m;
+ if (i != idiv4) {
+ futures[idx++] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ int isplt, j, k, m;
+ int idx1 = firstIdx + mf;
+ m = n;
+ while (m > 512) {
+ m >>= 2;
+ cftmdl1(m, a, idx1 - m, w, nw - (m >> 1));
+ }
+ cftleaf(m, 1, a, idx1 - m, nw, w);
+ k = 0;
+ int idx2 = firstIdx - m;
+ for (j = mf - m; j > 0; j -= m) {
+ k++;
+ isplt = cfttree(m, j, k, a, firstIdx, nw, w);
+ cftleaf(m, isplt, a, idx2 + j, nw, w);
+ }
+ }
+ });
+ } else {
+ futures[idx++] = ConcurrencyUtils.submit(new Runnable() {
+ public void run() {
+ int isplt, j, k, m;
+ int idx1 = firstIdx + mf;
+ k = 1;
+ m = n;
+ while (m > 512) {
+ m >>= 2;
+ k <<= 2;
+ cftmdl2(m, a, idx1 - m, w, nw - m);
+ }
+ cftleaf(m, 0, a, idx1 - m, nw, w);
+ k >>= 1;
+ int idx2 = firstIdx - m;
+ for (j = mf - m; j > 0; j -= m) {
+ k++;
+ isplt = cfttree(m, j, k, a, firstIdx, nw, w);
+ cftleaf(m, isplt, a, idx2 + j, nw, w);
+ }
+ }
+ });
+ }
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+ }
+
+ private void cftrec4(int n, float[] a, int offa, int nw, float[] w) {
+ int isplt, j, k, m;
+
+ m = n;
+ int idx1 = offa + n;
+ while (m > 512) {
+ m >>= 2;
+ cftmdl1(m, a, idx1 - m, w, nw - (m >> 1));
+ }
+ cftleaf(m, 1, a, idx1 - m, nw, w);
+ k = 0;
+ int idx2 = offa - m;
+ for (j = n - m; j > 0; j -= m) {
+ k++;
+ isplt = cfttree(m, j, k, a, offa, nw, w);
+ cftleaf(m, isplt, a, idx2 + j, nw, w);
+ }
+ }
+
+ private int cfttree(int n, int j, int k, float[] a, int offa, int nw, float[] w) {
+ int i, isplt, m;
+ int idx1 = offa - n;
+ if ((k & 3) != 0) {
+ isplt = k & 1;
+ if (isplt != 0) {
+ cftmdl1(n, a, idx1 + j, w, nw - (n >> 1));
+ } else {
+ cftmdl2(n, a, idx1 + j, w, nw - n);
+ }
+ } else {
+ m = n;
+ for (i = k; (i & 3) == 0; i >>= 2) {
+ m <<= 2;
+ }
+ isplt = i & 1;
+ int idx2 = offa + j;
+ if (isplt != 0) {
+ while (m > 128) {
+ cftmdl1(m, a, idx2 - m, w, nw - (m >> 1));
+ m >>= 2;
+ }
+ } else {
+ while (m > 128) {
+ cftmdl2(m, a, idx2 - m, w, nw - m);
+ m >>= 2;
+ }
+ }
+ }
+ return isplt;
+ }
+
+ private void cftleaf(int n, int isplt, float[] a, int offa, int nw, float[] w) {
+ if (n == 512) {
+ cftmdl1(128, a, offa, w, nw - 64);
+ cftf161(a, offa, w, nw - 8);
+ cftf162(a, offa + 32, w, nw - 32);
+ cftf161(a, offa + 64, w, nw - 8);
+ cftf161(a, offa + 96, w, nw - 8);
+ cftmdl2(128, a, offa + 128, w, nw - 128);
+ cftf161(a, offa + 128, w, nw - 8);
+ cftf162(a, offa + 160, w, nw - 32);
+ cftf161(a, offa + 192, w, nw - 8);
+ cftf162(a, offa + 224, w, nw - 32);
+ cftmdl1(128, a, offa + 256, w, nw - 64);
+ cftf161(a, offa + 256, w, nw - 8);
+ cftf162(a, offa + 288, w, nw - 32);
+ cftf161(a, offa + 320, w, nw - 8);
+ cftf161(a, offa + 352, w, nw - 8);
+ if (isplt != 0) {
+ cftmdl1(128, a, offa + 384, w, nw - 64);
+ cftf161(a, offa + 480, w, nw - 8);
+ } else {
+ cftmdl2(128, a, offa + 384, w, nw - 128);
+ cftf162(a, offa + 480, w, nw - 32);
+ }
+ cftf161(a, offa + 384, w, nw - 8);
+ cftf162(a, offa + 416, w, nw - 32);
+ cftf161(a, offa + 448, w, nw - 8);
+ } else {
+ cftmdl1(64, a, offa, w, nw - 32);
+ cftf081(a, offa, w, nw - 8);
+ cftf082(a, offa + 16, w, nw - 8);
+ cftf081(a, offa + 32, w, nw - 8);
+ cftf081(a, offa + 48, w, nw - 8);
+ cftmdl2(64, a, offa + 64, w, nw - 64);
+ cftf081(a, offa + 64, w, nw - 8);
+ cftf082(a, offa + 80, w, nw - 8);
+ cftf081(a, offa + 96, w, nw - 8);
+ cftf082(a, offa + 112, w, nw - 8);
+ cftmdl1(64, a, offa + 128, w, nw - 32);
+ cftf081(a, offa + 128, w, nw - 8);
+ cftf082(a, offa + 144, w, nw - 8);
+ cftf081(a, offa + 160, w, nw - 8);
+ cftf081(a, offa + 176, w, nw - 8);
+ if (isplt != 0) {
+ cftmdl1(64, a, offa + 192, w, nw - 32);
+ cftf081(a, offa + 240, w, nw - 8);
+ } else {
+ cftmdl2(64, a, offa + 192, w, nw - 64);
+ cftf082(a, offa + 240, w, nw - 8);
+ }
+ cftf081(a, offa + 192, w, nw - 8);
+ cftf082(a, offa + 208, w, nw - 8);
+ cftf081(a, offa + 224, w, nw - 8);
+ }
+ }
+
+ private void cftmdl1(int n, float[] a, int offa, float[] w, int startw) {
+ int j0, j1, j2, j3, k, m, mh;
+ float wn4r, wk1r, wk1i, wk3r, wk3i;
+ float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+ int idx0, idx1, idx2, idx3, idx4, idx5;
+
+ mh = n >> 3;
+ m = 2 * mh;
+ j1 = m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+ x0r = a[offa] + a[idx2];
+ x0i = a[offa + 1] + a[idx2 + 1];
+ x1r = a[offa] - a[idx2];
+ x1i = a[offa + 1] - a[idx2 + 1];
+ x2r = a[idx1] + a[idx3];
+ x2i = a[idx1 + 1] + a[idx3 + 1];
+ x3r = a[idx1] - a[idx3];
+ x3i = a[idx1 + 1] - a[idx3 + 1];
+ a[offa] = x0r + x2r;
+ a[offa + 1] = x0i + x2i;
+ a[idx1] = x0r - x2r;
+ a[idx1 + 1] = x0i - x2i;
+ a[idx2] = x1r - x3i;
+ a[idx2 + 1] = x1i + x3r;
+ a[idx3] = x1r + x3i;
+ a[idx3 + 1] = x1i - x3r;
+ wn4r = w[startw + 1];
+ k = 0;
+ for (int j = 2; j < mh; j += 2) {
+ k += 4;
+ idx4 = startw + k;
+ wk1r = w[idx4];
+ wk1i = w[idx4 + 1];
+ wk3r = w[idx4 + 2];
+ wk3i = w[idx4 + 3];
+ j1 = j + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+ idx5 = offa + j;
+ x0r = a[idx5] + a[idx2];
+ x0i = a[idx5 + 1] + a[idx2 + 1];
+ x1r = a[idx5] - a[idx2];
+ x1i = a[idx5 + 1] - a[idx2 + 1];
+ x2r = a[idx1] + a[idx3];
+ x2i = a[idx1 + 1] + a[idx3 + 1];
+ x3r = a[idx1] - a[idx3];
+ x3i = a[idx1 + 1] - a[idx3 + 1];
+ a[idx5] = x0r + x2r;
+ a[idx5 + 1] = x0i + x2i;
+ a[idx1] = x0r - x2r;
+ a[idx1 + 1] = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ a[idx2] = wk1r * x0r - wk1i * x0i;
+ a[idx2 + 1] = wk1r * x0i + wk1i * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ a[idx3] = wk3r * x0r + wk3i * x0i;
+ a[idx3 + 1] = wk3r * x0i - wk3i * x0r;
+ j0 = m - j;
+ j1 = j0 + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx0 = offa + j0;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+ x0r = a[idx0] + a[idx2];
+ x0i = a[idx0 + 1] + a[idx2 + 1];
+ x1r = a[idx0] - a[idx2];
+ x1i = a[idx0 + 1] - a[idx2 + 1];
+ x2r = a[idx1] + a[idx3];
+ x2i = a[idx1 + 1] + a[idx3 + 1];
+ x3r = a[idx1] - a[idx3];
+ x3i = a[idx1 + 1] - a[idx3 + 1];
+ a[idx0] = x0r + x2r;
+ a[idx0 + 1] = x0i + x2i;
+ a[idx1] = x0r - x2r;
+ a[idx1 + 1] = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ a[idx2] = wk1i * x0r - wk1r * x0i;
+ a[idx2 + 1] = wk1i * x0i + wk1r * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ a[idx3] = wk3i * x0r + wk3r * x0i;
+ a[idx3 + 1] = wk3i * x0i - wk3r * x0r;
+ }
+ j0 = mh;
+ j1 = j0 + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx0 = offa + j0;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+ x0r = a[idx0] + a[idx2];
+ x0i = a[idx0 + 1] + a[idx2 + 1];
+ x1r = a[idx0] - a[idx2];
+ x1i = a[idx0 + 1] - a[idx2 + 1];
+ x2r = a[idx1] + a[idx3];
+ x2i = a[idx1 + 1] + a[idx3 + 1];
+ x3r = a[idx1] - a[idx3];
+ x3i = a[idx1 + 1] - a[idx3 + 1];
+ a[idx0] = x0r + x2r;
+ a[idx0 + 1] = x0i + x2i;
+ a[idx1] = x0r - x2r;
+ a[idx1 + 1] = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ a[idx2] = wn4r * (x0r - x0i);
+ a[idx2 + 1] = wn4r * (x0i + x0r);
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ a[idx3] = -wn4r * (x0r + x0i);
+ a[idx3 + 1] = -wn4r * (x0i - x0r);
+ }
+
+ private void cftmdl2(int n, float[] a, int offa, float[] w, int startw) {
+ int j0, j1, j2, j3, k, kr, m, mh;
+ float wn4r, wk1r, wk1i, wk3r, wk3i, wd1r, wd1i, wd3r, wd3i;
+ float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y2r, y2i;
+ int idx0, idx1, idx2, idx3, idx4, idx5, idx6;
+
+ mh = n >> 3;
+ m = 2 * mh;
+ wn4r = w[startw + 1];
+ j1 = m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+ x0r = a[offa] - a[idx2 + 1];
+ x0i = a[offa + 1] + a[idx2];
+ x1r = a[offa] + a[idx2 + 1];
+ x1i = a[offa + 1] - a[idx2];
+ x2r = a[idx1] - a[idx3 + 1];
+ x2i = a[idx1 + 1] + a[idx3];
+ x3r = a[idx1] + a[idx3 + 1];
+ x3i = a[idx1 + 1] - a[idx3];
+ y0r = wn4r * (x2r - x2i);
+ y0i = wn4r * (x2i + x2r);
+ a[offa] = x0r + y0r;
+ a[offa + 1] = x0i + y0i;
+ a[idx1] = x0r - y0r;
+ a[idx1 + 1] = x0i - y0i;
+ y0r = wn4r * (x3r - x3i);
+ y0i = wn4r * (x3i + x3r);
+ a[idx2] = x1r - y0i;
+ a[idx2 + 1] = x1i + y0r;
+ a[idx3] = x1r + y0i;
+ a[idx3 + 1] = x1i - y0r;
+ k = 0;
+ kr = 2 * m;
+ for (int j = 2; j < mh; j += 2) {
+ k += 4;
+ idx4 = startw + k;
+ wk1r = w[idx4];
+ wk1i = w[idx4 + 1];
+ wk3r = w[idx4 + 2];
+ wk3i = w[idx4 + 3];
+ kr -= 4;
+ idx5 = startw + kr;
+ wd1i = w[idx5];
+ wd1r = w[idx5 + 1];
+ wd3i = w[idx5 + 2];
+ wd3r = w[idx5 + 3];
+ j1 = j + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+ idx6 = offa + j;
+ x0r = a[idx6] - a[idx2 + 1];
+ x0i = a[idx6 + 1] + a[idx2];
+ x1r = a[idx6] + a[idx2 + 1];
+ x1i = a[idx6 + 1] - a[idx2];
+ x2r = a[idx1] - a[idx3 + 1];
+ x2i = a[idx1 + 1] + a[idx3];
+ x3r = a[idx1] + a[idx3 + 1];
+ x3i = a[idx1 + 1] - a[idx3];
+ y0r = wk1r * x0r - wk1i * x0i;
+ y0i = wk1r * x0i + wk1i * x0r;
+ y2r = wd1r * x2r - wd1i * x2i;
+ y2i = wd1r * x2i + wd1i * x2r;
+ a[idx6] = y0r + y2r;
+ a[idx6 + 1] = y0i + y2i;
+ a[idx1] = y0r - y2r;
+ a[idx1 + 1] = y0i - y2i;
+ y0r = wk3r * x1r + wk3i * x1i;
+ y0i = wk3r * x1i - wk3i * x1r;
+ y2r = wd3r * x3r + wd3i * x3i;
+ y2i = wd3r * x3i - wd3i * x3r;
+ a[idx2] = y0r + y2r;
+ a[idx2 + 1] = y0i + y2i;
+ a[idx3] = y0r - y2r;
+ a[idx3 + 1] = y0i - y2i;
+ j0 = m - j;
+ j1 = j0 + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx0 = offa + j0;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+ x0r = a[idx0] - a[idx2 + 1];
+ x0i = a[idx0 + 1] + a[idx2];
+ x1r = a[idx0] + a[idx2 + 1];
+ x1i = a[idx0 + 1] - a[idx2];
+ x2r = a[idx1] - a[idx3 + 1];
+ x2i = a[idx1 + 1] + a[idx3];
+ x3r = a[idx1] + a[idx3 + 1];
+ x3i = a[idx1 + 1] - a[idx3];
+ y0r = wd1i * x0r - wd1r * x0i;
+ y0i = wd1i * x0i + wd1r * x0r;
+ y2r = wk1i * x2r - wk1r * x2i;
+ y2i = wk1i * x2i + wk1r * x2r;
+ a[idx0] = y0r + y2r;
+ a[idx0 + 1] = y0i + y2i;
+ a[idx1] = y0r - y2r;
+ a[idx1 + 1] = y0i - y2i;
+ y0r = wd3i * x1r + wd3r * x1i;
+ y0i = wd3i * x1i - wd3r * x1r;
+ y2r = wk3i * x3r + wk3r * x3i;
+ y2i = wk3i * x3i - wk3r * x3r;
+ a[idx2] = y0r + y2r;
+ a[idx2 + 1] = y0i + y2i;
+ a[idx3] = y0r - y2r;
+ a[idx3 + 1] = y0i - y2i;
+ }
+ wk1r = w[startw + m];
+ wk1i = w[startw + m + 1];
+ j0 = mh;
+ j1 = j0 + m;
+ j2 = j1 + m;
+ j3 = j2 + m;
+ idx0 = offa + j0;
+ idx1 = offa + j1;
+ idx2 = offa + j2;
+ idx3 = offa + j3;
+ x0r = a[idx0] - a[idx2 + 1];
+ x0i = a[idx0 + 1] + a[idx2];
+ x1r = a[idx0] + a[idx2 + 1];
+ x1i = a[idx0 + 1] - a[idx2];
+ x2r = a[idx1] - a[idx3 + 1];
+ x2i = a[idx1 + 1] + a[idx3];
+ x3r = a[idx1] + a[idx3 + 1];
+ x3i = a[idx1 + 1] - a[idx3];
+ y0r = wk1r * x0r - wk1i * x0i;
+ y0i = wk1r * x0i + wk1i * x0r;
+ y2r = wk1i * x2r - wk1r * x2i;
+ y2i = wk1i * x2i + wk1r * x2r;
+ a[idx0] = y0r + y2r;
+ a[idx0 + 1] = y0i + y2i;
+ a[idx1] = y0r - y2r;
+ a[idx1 + 1] = y0i - y2i;
+ y0r = wk1i * x1r - wk1r * x1i;
+ y0i = wk1i * x1i + wk1r * x1r;
+ y2r = wk1r * x3r - wk1i * x3i;
+ y2i = wk1r * x3i + wk1i * x3r;
+ a[idx2] = y0r - y2r;
+ a[idx2 + 1] = y0i - y2i;
+ a[idx3] = y0r + y2r;
+ a[idx3 + 1] = y0i + y2i;
+ }
+
+ private void cftfx41(int n, float[] a, int offa, int nw, float[] w) {
+ if (n == 128) {
+ cftf161(a, offa, w, nw - 8);
+ cftf162(a, offa + 32, w, nw - 32);
+ cftf161(a, offa + 64, w, nw - 8);
+ cftf161(a, offa + 96, w, nw - 8);
+ } else {
+ cftf081(a, offa, w, nw - 8);
+ cftf082(a, offa + 16, w, nw - 8);
+ cftf081(a, offa + 32, w, nw - 8);
+ cftf081(a, offa + 48, w, nw - 8);
+ }
+ }
+
+ private void cftf161(float[] a, int offa, float[] w, int startw) {
+ float wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i;
+
+ wn4r = w[startw + 1];
+ wk1r = w[startw + 2];
+ wk1i = w[startw + 3];
+
+ x0r = a[offa] + a[offa + 16];
+ x0i = a[offa + 1] + a[offa + 17];
+ x1r = a[offa] - a[offa + 16];
+ x1i = a[offa + 1] - a[offa + 17];
+ x2r = a[offa + 8] + a[offa + 24];
+ x2i = a[offa + 9] + a[offa + 25];
+ x3r = a[offa + 8] - a[offa + 24];
+ x3i = a[offa + 9] - a[offa + 25];
+ y0r = x0r + x2r;
+ y0i = x0i + x2i;
+ y4r = x0r - x2r;
+ y4i = x0i - x2i;
+ y8r = x1r - x3i;
+ y8i = x1i + x3r;
+ y12r = x1r + x3i;
+ y12i = x1i - x3r;
+ x0r = a[offa + 2] + a[offa + 18];
+ x0i = a[offa + 3] + a[offa + 19];
+ x1r = a[offa + 2] - a[offa + 18];
+ x1i = a[offa + 3] - a[offa + 19];
+ x2r = a[offa + 10] + a[offa + 26];
+ x2i = a[offa + 11] + a[offa + 27];
+ x3r = a[offa + 10] - a[offa + 26];
+ x3i = a[offa + 11] - a[offa + 27];
+ y1r = x0r + x2r;
+ y1i = x0i + x2i;
+ y5r = x0r - x2r;
+ y5i = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ y9r = wk1r * x0r - wk1i * x0i;
+ y9i = wk1r * x0i + wk1i * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ y13r = wk1i * x0r - wk1r * x0i;
+ y13i = wk1i * x0i + wk1r * x0r;
+ x0r = a[offa + 4] + a[offa + 20];
+ x0i = a[offa + 5] + a[offa + 21];
+ x1r = a[offa + 4] - a[offa + 20];
+ x1i = a[offa + 5] - a[offa + 21];
+ x2r = a[offa + 12] + a[offa + 28];
+ x2i = a[offa + 13] + a[offa + 29];
+ x3r = a[offa + 12] - a[offa + 28];
+ x3i = a[offa + 13] - a[offa + 29];
+ y2r = x0r + x2r;
+ y2i = x0i + x2i;
+ y6r = x0r - x2r;
+ y6i = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ y10r = wn4r * (x0r - x0i);
+ y10i = wn4r * (x0i + x0r);
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ y14r = wn4r * (x0r + x0i);
+ y14i = wn4r * (x0i - x0r);
+ x0r = a[offa + 6] + a[offa + 22];
+ x0i = a[offa + 7] + a[offa + 23];
+ x1r = a[offa + 6] - a[offa + 22];
+ x1i = a[offa + 7] - a[offa + 23];
+ x2r = a[offa + 14] + a[offa + 30];
+ x2i = a[offa + 15] + a[offa + 31];
+ x3r = a[offa + 14] - a[offa + 30];
+ x3i = a[offa + 15] - a[offa + 31];
+ y3r = x0r + x2r;
+ y3i = x0i + x2i;
+ y7r = x0r - x2r;
+ y7i = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ y11r = wk1i * x0r - wk1r * x0i;
+ y11i = wk1i * x0i + wk1r * x0r;
+ x0r = x1r + x3i;
+ x0i = x1i - x3r;
+ y15r = wk1r * x0r - wk1i * x0i;
+ y15i = wk1r * x0i + wk1i * x0r;
+ x0r = y12r - y14r;
+ x0i = y12i - y14i;
+ x1r = y12r + y14r;
+ x1i = y12i + y14i;
+ x2r = y13r - y15r;
+ x2i = y13i - y15i;
+ x3r = y13r + y15r;
+ x3i = y13i + y15i;
+ a[offa + 24] = x0r + x2r;
+ a[offa + 25] = x0i + x2i;
+ a[offa + 26] = x0r - x2r;
+ a[offa + 27] = x0i - x2i;
+ a[offa + 28] = x1r - x3i;
+ a[offa + 29] = x1i + x3r;
+ a[offa + 30] = x1r + x3i;
+ a[offa + 31] = x1i - x3r;
+ x0r = y8r + y10r;
+ x0i = y8i + y10i;
+ x1r = y8r - y10r;
+ x1i = y8i - y10i;
+ x2r = y9r + y11r;
+ x2i = y9i + y11i;
+ x3r = y9r - y11r;
+ x3i = y9i - y11i;
+ a[offa + 16] = x0r + x2r;
+ a[offa + 17] = x0i + x2i;
+ a[offa + 18] = x0r - x2r;
+ a[offa + 19] = x0i - x2i;
+ a[offa + 20] = x1r - x3i;
+ a[offa + 21] = x1i + x3r;
+ a[offa + 22] = x1r + x3i;
+ a[offa + 23] = x1i - x3r;
+ x0r = y5r - y7i;
+ x0i = y5i + y7r;
+ x2r = wn4r * (x0r - x0i);
+ x2i = wn4r * (x0i + x0r);
+ x0r = y5r + y7i;
+ x0i = y5i - y7r;
+ x3r = wn4r * (x0r - x0i);
+ x3i = wn4r * (x0i + x0r);
+ x0r = y4r - y6i;
+ x0i = y4i + y6r;
+ x1r = y4r + y6i;
+ x1i = y4i - y6r;
+ a[offa + 8] = x0r + x2r;
+ a[offa + 9] = x0i + x2i;
+ a[offa + 10] = x0r - x2r;
+ a[offa + 11] = x0i - x2i;
+ a[offa + 12] = x1r - x3i;
+ a[offa + 13] = x1i + x3r;
+ a[offa + 14] = x1r + x3i;
+ a[offa + 15] = x1i - x3r;
+ x0r = y0r + y2r;
+ x0i = y0i + y2i;
+ x1r = y0r - y2r;
+ x1i = y0i - y2i;
+ x2r = y1r + y3r;
+ x2i = y1i + y3i;
+ x3r = y1r - y3r;
+ x3i = y1i - y3i;
+ a[offa] = x0r + x2r;
+ a[offa + 1] = x0i + x2i;
+ a[offa + 2] = x0r - x2r;
+ a[offa + 3] = x0i - x2i;
+ a[offa + 4] = x1r - x3i;
+ a[offa + 5] = x1i + x3r;
+ a[offa + 6] = x1r + x3i;
+ a[offa + 7] = x1i - x3r;
+ }
+
+ private void cftf162(float[] a, int offa, float[] w, int startw) {
+ float wn4r, wk1r, wk1i, wk2r, wk2i, wk3r, wk3i, x0r, x0i, x1r, x1i, x2r, x2i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i, y8r, y8i, y9r, y9i, y10r, y10i, y11r, y11i, y12r, y12i, y13r, y13i, y14r, y14i, y15r, y15i;
+
+ wn4r = w[startw + 1];
+ wk1r = w[startw + 4];
+ wk1i = w[startw + 5];
+ wk3r = w[startw + 6];
+ wk3i = -w[startw + 7];
+ wk2r = w[startw + 8];
+ wk2i = w[startw + 9];
+ x1r = a[offa] - a[offa + 17];
+ x1i = a[offa + 1] + a[offa + 16];
+ x0r = a[offa + 8] - a[offa + 25];
+ x0i = a[offa + 9] + a[offa + 24];
+ x2r = wn4r * (x0r - x0i);
+ x2i = wn4r * (x0i + x0r);
+ y0r = x1r + x2r;
+ y0i = x1i + x2i;
+ y4r = x1r - x2r;
+ y4i = x1i - x2i;
+ x1r = a[offa] + a[offa + 17];
+ x1i = a[offa + 1] - a[offa + 16];
+ x0r = a[offa + 8] + a[offa + 25];
+ x0i = a[offa + 9] - a[offa + 24];
+ x2r = wn4r * (x0r - x0i);
+ x2i = wn4r * (x0i + x0r);
+ y8r = x1r - x2i;
+ y8i = x1i + x2r;
+ y12r = x1r + x2i;
+ y12i = x1i - x2r;
+ x0r = a[offa + 2] - a[offa + 19];
+ x0i = a[offa + 3] + a[offa + 18];
+ x1r = wk1r * x0r - wk1i * x0i;
+ x1i = wk1r * x0i + wk1i * x0r;
+ x0r = a[offa + 10] - a[offa + 27];
+ x0i = a[offa + 11] + a[offa + 26];
+ x2r = wk3i * x0r - wk3r * x0i;
+ x2i = wk3i * x0i + wk3r * x0r;
+ y1r = x1r + x2r;
+ y1i = x1i + x2i;
+ y5r = x1r - x2r;
+ y5i = x1i - x2i;
+ x0r = a[offa + 2] + a[offa + 19];
+ x0i = a[offa + 3] - a[offa + 18];
+ x1r = wk3r * x0r - wk3i * x0i;
+ x1i = wk3r * x0i + wk3i * x0r;
+ x0r = a[offa + 10] + a[offa + 27];
+ x0i = a[offa + 11] - a[offa + 26];
+ x2r = wk1r * x0r + wk1i * x0i;
+ x2i = wk1r * x0i - wk1i * x0r;
+ y9r = x1r - x2r;
+ y9i = x1i - x2i;
+ y13r = x1r + x2r;
+ y13i = x1i + x2i;
+ x0r = a[offa + 4] - a[offa + 21];
+ x0i = a[offa + 5] + a[offa + 20];
+ x1r = wk2r * x0r - wk2i * x0i;
+ x1i = wk2r * x0i + wk2i * x0r;
+ x0r = a[offa + 12] - a[offa + 29];
+ x0i = a[offa + 13] + a[offa + 28];
+ x2r = wk2i * x0r - wk2r * x0i;
+ x2i = wk2i * x0i + wk2r * x0r;
+ y2r = x1r + x2r;
+ y2i = x1i + x2i;
+ y6r = x1r - x2r;
+ y6i = x1i - x2i;
+ x0r = a[offa + 4] + a[offa + 21];
+ x0i = a[offa + 5] - a[offa + 20];
+ x1r = wk2i * x0r - wk2r * x0i;
+ x1i = wk2i * x0i + wk2r * x0r;
+ x0r = a[offa + 12] + a[offa + 29];
+ x0i = a[offa + 13] - a[offa + 28];
+ x2r = wk2r * x0r - wk2i * x0i;
+ x2i = wk2r * x0i + wk2i * x0r;
+ y10r = x1r - x2r;
+ y10i = x1i - x2i;
+ y14r = x1r + x2r;
+ y14i = x1i + x2i;
+ x0r = a[offa + 6] - a[offa + 23];
+ x0i = a[offa + 7] + a[offa + 22];
+ x1r = wk3r * x0r - wk3i * x0i;
+ x1i = wk3r * x0i + wk3i * x0r;
+ x0r = a[offa + 14] - a[offa + 31];
+ x0i = a[offa + 15] + a[offa + 30];
+ x2r = wk1i * x0r - wk1r * x0i;
+ x2i = wk1i * x0i + wk1r * x0r;
+ y3r = x1r + x2r;
+ y3i = x1i + x2i;
+ y7r = x1r - x2r;
+ y7i = x1i - x2i;
+ x0r = a[offa + 6] + a[offa + 23];
+ x0i = a[offa + 7] - a[offa + 22];
+ x1r = wk1i * x0r + wk1r * x0i;
+ x1i = wk1i * x0i - wk1r * x0r;
+ x0r = a[offa + 14] + a[offa + 31];
+ x0i = a[offa + 15] - a[offa + 30];
+ x2r = wk3i * x0r - wk3r * x0i;
+ x2i = wk3i * x0i + wk3r * x0r;
+ y11r = x1r + x2r;
+ y11i = x1i + x2i;
+ y15r = x1r - x2r;
+ y15i = x1i - x2i;
+ x1r = y0r + y2r;
+ x1i = y0i + y2i;
+ x2r = y1r + y3r;
+ x2i = y1i + y3i;
+ a[offa] = x1r + x2r;
+ a[offa + 1] = x1i + x2i;
+ a[offa + 2] = x1r - x2r;
+ a[offa + 3] = x1i - x2i;
+ x1r = y0r - y2r;
+ x1i = y0i - y2i;
+ x2r = y1r - y3r;
+ x2i = y1i - y3i;
+ a[offa + 4] = x1r - x2i;
+ a[offa + 5] = x1i + x2r;
+ a[offa + 6] = x1r + x2i;
+ a[offa + 7] = x1i - x2r;
+ x1r = y4r - y6i;
+ x1i = y4i + y6r;
+ x0r = y5r - y7i;
+ x0i = y5i + y7r;
+ x2r = wn4r * (x0r - x0i);
+ x2i = wn4r * (x0i + x0r);
+ a[offa + 8] = x1r + x2r;
+ a[offa + 9] = x1i + x2i;
+ a[offa + 10] = x1r - x2r;
+ a[offa + 11] = x1i - x2i;
+ x1r = y4r + y6i;
+ x1i = y4i - y6r;
+ x0r = y5r + y7i;
+ x0i = y5i - y7r;
+ x2r = wn4r * (x0r - x0i);
+ x2i = wn4r * (x0i + x0r);
+ a[offa + 12] = x1r - x2i;
+ a[offa + 13] = x1i + x2r;
+ a[offa + 14] = x1r + x2i;
+ a[offa + 15] = x1i - x2r;
+ x1r = y8r + y10r;
+ x1i = y8i + y10i;
+ x2r = y9r - y11r;
+ x2i = y9i - y11i;
+ a[offa + 16] = x1r + x2r;
+ a[offa + 17] = x1i + x2i;
+ a[offa + 18] = x1r - x2r;
+ a[offa + 19] = x1i - x2i;
+ x1r = y8r - y10r;
+ x1i = y8i - y10i;
+ x2r = y9r + y11r;
+ x2i = y9i + y11i;
+ a[offa + 20] = x1r - x2i;
+ a[offa + 21] = x1i + x2r;
+ a[offa + 22] = x1r + x2i;
+ a[offa + 23] = x1i - x2r;
+ x1r = y12r - y14i;
+ x1i = y12i + y14r;
+ x0r = y13r + y15i;
+ x0i = y13i - y15r;
+ x2r = wn4r * (x0r - x0i);
+ x2i = wn4r * (x0i + x0r);
+ a[offa + 24] = x1r + x2r;
+ a[offa + 25] = x1i + x2i;
+ a[offa + 26] = x1r - x2r;
+ a[offa + 27] = x1i - x2i;
+ x1r = y12r + y14i;
+ x1i = y12i - y14r;
+ x0r = y13r - y15i;
+ x0i = y13i + y15r;
+ x2r = wn4r * (x0r - x0i);
+ x2i = wn4r * (x0i + x0r);
+ a[offa + 28] = x1r - x2i;
+ a[offa + 29] = x1i + x2r;
+ a[offa + 30] = x1r + x2i;
+ a[offa + 31] = x1i - x2r;
+ }
+
+ private void cftf081(float[] a, int offa, float[] w, int startw) {
+ float wn4r, x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i;
+
+ wn4r = w[startw + 1];
+ x0r = a[offa] + a[offa + 8];
+ x0i = a[offa + 1] + a[offa + 9];
+ x1r = a[offa] - a[offa + 8];
+ x1i = a[offa + 1] - a[offa + 9];
+ x2r = a[offa + 4] + a[offa + 12];
+ x2i = a[offa + 5] + a[offa + 13];
+ x3r = a[offa + 4] - a[offa + 12];
+ x3i = a[offa + 5] - a[offa + 13];
+ y0r = x0r + x2r;
+ y0i = x0i + x2i;
+ y2r = x0r - x2r;
+ y2i = x0i - x2i;
+ y1r = x1r - x3i;
+ y1i = x1i + x3r;
+ y3r = x1r + x3i;
+ y3i = x1i - x3r;
+ x0r = a[offa + 2] + a[offa + 10];
+ x0i = a[offa + 3] + a[offa + 11];
+ x1r = a[offa + 2] - a[offa + 10];
+ x1i = a[offa + 3] - a[offa + 11];
+ x2r = a[offa + 6] + a[offa + 14];
+ x2i = a[offa + 7] + a[offa + 15];
+ x3r = a[offa + 6] - a[offa + 14];
+ x3i = a[offa + 7] - a[offa + 15];
+ y4r = x0r + x2r;
+ y4i = x0i + x2i;
+ y6r = x0r - x2r;
+ y6i = x0i - x2i;
+ x0r = x1r - x3i;
+ x0i = x1i + x3r;
+ x2r = x1r + x3i;
+ x2i = x1i - x3r;
+ y5r = wn4r * (x0r - x0i);
+ y5i = wn4r * (x0r + x0i);
+ y7r = wn4r * (x2r - x2i);
+ y7i = wn4r * (x2r + x2i);
+ a[offa + 8] = y1r + y5r;
+ a[offa + 9] = y1i + y5i;
+ a[offa + 10] = y1r - y5r;
+ a[offa + 11] = y1i - y5i;
+ a[offa + 12] = y3r - y7i;
+ a[offa + 13] = y3i + y7r;
+ a[offa + 14] = y3r + y7i;
+ a[offa + 15] = y3i - y7r;
+ a[offa] = y0r + y4r;
+ a[offa + 1] = y0i + y4i;
+ a[offa + 2] = y0r - y4r;
+ a[offa + 3] = y0i - y4i;
+ a[offa + 4] = y2r - y6i;
+ a[offa + 5] = y2i + y6r;
+ a[offa + 6] = y2r + y6i;
+ a[offa + 7] = y2i - y6r;
+ }
+
+ private void cftf082(float[] a, int offa, float[] w, int startw) {
+ float wn4r, wk1r, wk1i, x0r, x0i, x1r, x1i, y0r, y0i, y1r, y1i, y2r, y2i, y3r, y3i, y4r, y4i, y5r, y5i, y6r, y6i, y7r, y7i;
+
+ wn4r = w[startw + 1];
+ wk1r = w[startw + 2];
+ wk1i = w[startw + 3];
+ y0r = a[offa] - a[offa + 9];
+ y0i = a[offa + 1] + a[offa + 8];
+ y1r = a[offa] + a[offa + 9];
+ y1i = a[offa + 1] - a[offa + 8];
+ x0r = a[offa + 4] - a[offa + 13];
+ x0i = a[offa + 5] + a[offa + 12];
+ y2r = wn4r * (x0r - x0i);
+ y2i = wn4r * (x0i + x0r);
+ x0r = a[offa + 4] + a[offa + 13];
+ x0i = a[offa + 5] - a[offa + 12];
+ y3r = wn4r * (x0r - x0i);
+ y3i = wn4r * (x0i + x0r);
+ x0r = a[offa + 2] - a[offa + 11];
+ x0i = a[offa + 3] + a[offa + 10];
+ y4r = wk1r * x0r - wk1i * x0i;
+ y4i = wk1r * x0i + wk1i * x0r;
+ x0r = a[offa + 2] + a[offa + 11];
+ x0i = a[offa + 3] - a[offa + 10];
+ y5r = wk1i * x0r - wk1r * x0i;
+ y5i = wk1i * x0i + wk1r * x0r;
+ x0r = a[offa + 6] - a[offa + 15];
+ x0i = a[offa + 7] + a[offa + 14];
+ y6r = wk1i * x0r - wk1r * x0i;
+ y6i = wk1i * x0i + wk1r * x0r;
+ x0r = a[offa + 6] + a[offa + 15];
+ x0i = a[offa + 7] - a[offa + 14];
+ y7r = wk1r * x0r - wk1i * x0i;
+ y7i = wk1r * x0i + wk1i * x0r;
+ x0r = y0r + y2r;
+ x0i = y0i + y2i;
+ x1r = y4r + y6r;
+ x1i = y4i + y6i;
+ a[offa] = x0r + x1r;
+ a[offa + 1] = x0i + x1i;
+ a[offa + 2] = x0r - x1r;
+ a[offa + 3] = x0i - x1i;
+ x0r = y0r - y2r;
+ x0i = y0i - y2i;
+ x1r = y4r - y6r;
+ x1i = y4i - y6i;
+ a[offa + 4] = x0r - x1i;
+ a[offa + 5] = x0i + x1r;
+ a[offa + 6] = x0r + x1i;
+ a[offa + 7] = x0i - x1r;
+ x0r = y1r - y3i;
+ x0i = y1i + y3r;
+ x1r = y5r - y7r;
+ x1i = y5i - y7i;
+ a[offa + 8] = x0r + x1r;
+ a[offa + 9] = x0i + x1i;
+ a[offa + 10] = x0r - x1r;
+ a[offa + 11] = x0i - x1i;
+ x0r = y1r + y3i;
+ x0i = y1i - y3r;
+ x1r = y5r + y7r;
+ x1i = y5i + y7i;
+ a[offa + 12] = x0r - x1i;
+ a[offa + 13] = x0i + x1r;
+ a[offa + 14] = x0r + x1i;
+ a[offa + 15] = x0i - x1r;
+ }
+
+ private void cftf040(float[] a, int offa) {
+ float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+
+ x0r = a[offa] + a[offa + 4];
+ x0i = a[offa + 1] + a[offa + 5];
+ x1r = a[offa] - a[offa + 4];
+ x1i = a[offa + 1] - a[offa + 5];
+ x2r = a[offa + 2] + a[offa + 6];
+ x2i = a[offa + 3] + a[offa + 7];
+ x3r = a[offa + 2] - a[offa + 6];
+ x3i = a[offa + 3] - a[offa + 7];
+ a[offa] = x0r + x2r;
+ a[offa + 1] = x0i + x2i;
+ a[offa + 2] = x1r - x3i;
+ a[offa + 3] = x1i + x3r;
+ a[offa + 4] = x0r - x2r;
+ a[offa + 5] = x0i - x2i;
+ a[offa + 6] = x1r + x3i;
+ a[offa + 7] = x1i - x3r;
+ }
+
+ private void cftb040(float[] a, int offa) {
+ float x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i;
+
+ x0r = a[offa] + a[offa + 4];
+ x0i = a[offa + 1] + a[offa + 5];
+ x1r = a[offa] - a[offa + 4];
+ x1i = a[offa + 1] - a[offa + 5];
+ x2r = a[offa + 2] + a[offa + 6];
+ x2i = a[offa + 3] + a[offa + 7];
+ x3r = a[offa + 2] - a[offa + 6];
+ x3i = a[offa + 3] - a[offa + 7];
+ a[offa] = x0r + x2r;
+ a[offa + 1] = x0i + x2i;
+ a[offa + 2] = x1r + x3i;
+ a[offa + 3] = x1i - x3r;
+ a[offa + 4] = x0r - x2r;
+ a[offa + 5] = x0i - x2i;
+ a[offa + 6] = x1r - x3i;
+ a[offa + 7] = x1i + x3r;
+ }
+
+ private void cftx020(float[] a, int offa) {
+ float x0r, x0i;
+ x0r = a[offa] - a[offa + 2];
+ x0i = -a[offa + 1] + a[offa + 3];
+ a[offa] += a[offa + 2];
+ a[offa + 1] += a[offa + 3];
+ a[offa + 2] = x0r;
+ a[offa + 3] = x0i;
+ }
+
+ private void cftxb020(float[] a, int offa) {
+ float x0r, x0i;
+
+ x0r = a[offa] - a[offa + 2];
+ x0i = a[offa + 1] - a[offa + 3];
+ a[offa] += a[offa + 2];
+ a[offa + 1] += a[offa + 3];
+ a[offa + 2] = x0r;
+ a[offa + 3] = x0i;
+ }
+
+ private void cftxc020(float[] a, int offa) {
+ float x0r, x0i;
+ x0r = a[offa] - a[offa + 2];
+ x0i = a[offa + 1] + a[offa + 3];
+ a[offa] += a[offa + 2];
+ a[offa + 1] -= a[offa + 3];
+ a[offa + 2] = x0r;
+ a[offa + 3] = x0i;
+ }
+
+ private void rftfsub(int n, float[] a, int offa, int nc, float[] c, int startc) {
+ int k, kk, ks, m;
+ float wkr, wki, xr, xi, yr, yi;
+ int idx1, idx2;
+
+ m = n >> 1;
+ ks = 2 * nc / m;
+ kk = 0;
+ for (int j = 2; j < m; j += 2) {
+ k = n - j;
+ kk += ks;
+ wkr = (float)(0.5 - c[startc + nc - kk]);
+ wki = c[startc + kk];
+ idx1 = offa + j;
+ idx2 = offa + k;
+ xr = a[idx1] - a[idx2];
+ xi = a[idx1 + 1] + a[idx2 + 1];
+ yr = wkr * xr - wki * xi;
+ yi = wkr * xi + wki * xr;
+ a[idx1] -= yr;
+ a[idx1 + 1] = yi - a[idx1 + 1];
+ a[idx2] += yr;
+ a[idx2 + 1] = yi - a[idx2 + 1];
+ }
+ a[offa + m + 1] = -a[offa + m + 1];
+ }
+
+ private void rftbsub(int n, float[] a, int offa, int nc, float[] c, int startc) {
+ int k, kk, ks, m;
+ float wkr, wki, xr, xi, yr, yi;
+ int idx1, idx2;
+
+ m = n >> 1;
+ ks = 2 * nc / m;
+ kk = 0;
+ for (int j = 2; j < m; j += 2) {
+ k = n - j;
+ kk += ks;
+ wkr = (float)(0.5 - c[startc + nc - kk]);
+ wki = c[startc + kk];
+ idx1 = offa + j;
+ idx2 = offa + k;
+ xr = a[idx1] - a[idx2];
+ xi = a[idx1 + 1] + a[idx2 + 1];
+ yr = wkr * xr - wki * xi;
+ yi = wkr * xi + wki * xr;
+ a[idx1] -= yr;
+ a[idx1 + 1] -= yi;
+ a[idx2] += yr;
+ a[idx2 + 1] -= yi;
+ }
+ }
+
+ private void scale(final float m, final float[] a, int offa, boolean complex) {
+ final float norm = (float)(1.0 / m);
+ int n2;
+ if (complex) {
+ n2 = 2 * n;
+ } else {
+ n2 = n;
+ }
+ int nthreads = ConcurrencyUtils.getNumberOfThreads();
+ if ((nthreads > 1) && (n2 >= ConcurrencyUtils.getThreadsBeginN_1D_FFT_2Threads())) {
+ final int k = n2 / nthreads;
+ Future>[] futures = new Future[nthreads];
+ for (int i = 0; i < nthreads; i++) {
+ final int firstIdx = offa + i * k;
+ final int lastIdx = (i == (nthreads - 1)) ? offa + n2 : firstIdx + k;
+ futures[i] = ConcurrencyUtils.submit(new Runnable() {
+
+ public void run() {
+ for (int i = firstIdx; i < lastIdx; i++) {
+ a[i] *= norm;
+ }
+ }
+ });
+ }
+ ConcurrencyUtils.waitForCompletion(futures);
+ } else {
+ for (int i = offa; i < offa + n2; i++) {
+ a[i] *= norm;
+ }
+
+ }
+ }
+}
diff --git a/app/src/main/java/be/tarsos/dsp/util/fft/GaussWindow.java b/app/src/main/java/be/tarsos/dsp/util/fft/GaussWindow.java
new file mode 100644
index 0000000..efcde8a
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/util/fft/GaussWindow.java
@@ -0,0 +1,77 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+/*
+ * Copyright (c) 2007 - 2008 by Damien Di Fede window()
function with an appropriate WindowFunction, such as
+ * HammingWindow()
, the sample buffers passed to the object for
+ * analysis will be shaped by the current window before being transformed. The
+ * result of using a window is to reduce the leakage in the spectrum somewhat.
+ * WindowFunction
handles work associated with various window
+ * functions such as the Hamming window. To create your own window function you
+ * must extend WindowFunction
and implement the
+ * {@link #value(int, int) value} method which defines the shape of the window
+ * at a given offset. WindowFunction
will call this method to apply
+ * the window to a sample buffer. The number passed to the method is an offset
+ * within the length of the window curve.
+ *
+ * @author Damien Di Fede
+ * @author Corban Brook
+ *
+ */
+public abstract class WindowFunction {
+
+ /** The float value of 2*PI. Provided as a convenience for subclasses. */
+ protected static final float TWO_PI = (float) (2 * Math.PI);
+ protected int length;
+
+
+ /**
+ * Construct a new window.
+ */
+ protected WindowFunction() {
+ }
+
+ /**
+ * Apply the window function to a sample buffer.
+ *
+ * @param samples
+ * a sample buffer
+ */
+ public void apply(float[] samples) {
+ this.length = samples.length;
+
+ for (int n = 0; n < samples.length; n++) {
+ samples[n] *= value(samples.length, n);
+ }
+ }
+
+ /**
+ * Generates the curve of the window function.
+ *
+ * @param length
+ * the length of the window
+ * @return the shape of the window function
+ */
+ public float[] generateCurve(int length) {
+ float[] samples = new float[length];
+ for (int n = 0; n < length; n++) {
+ samples[n] = value(length, n);
+ }
+ return samples;
+ }
+
+ /**
+ * The value of the window function
+ * @param length with the lengt of the window (in samples)
+ * @param index at index
+ * @return The value of the window function at the requested index.
+ */
+ protected abstract float value(int length, int index);
+}
diff --git a/app/src/main/java/be/tarsos/dsp/util/fft/package-info.java b/app/src/main/java/be/tarsos/dsp/util/fft/package-info.java
new file mode 100644
index 0000000..d8e4ab9
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/util/fft/package-info.java
@@ -0,0 +1,28 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+/**
+ * Utilities needed for FFT.
+ */
+package be.tarsos.dsp.util.fft;
diff --git a/app/src/main/java/be/tarsos/dsp/util/package-info.java b/app/src/main/java/be/tarsos/dsp/util/package-info.java
new file mode 100644
index 0000000..adf2e8d
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/util/package-info.java
@@ -0,0 +1,28 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+
+/**
+ * Utility classes to handle sampled sound.
+ */
+package be.tarsos.dsp.util;
diff --git a/app/src/main/java/be/tarsos/dsp/wavelet/HaarWaveletCoder.java b/app/src/main/java/be/tarsos/dsp/wavelet/HaarWaveletCoder.java
new file mode 100644
index 0000000..2492827
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/wavelet/HaarWaveletCoder.java
@@ -0,0 +1,83 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.wavelet;
+
+import java.util.Arrays;
+
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.AudioProcessor;
+
+public class HaarWaveletCoder implements AudioProcessor{
+
+ private final HaarWaveletTransform transform;
+
+ private int compression;
+
+ public HaarWaveletCoder(){
+ this(16);
+ }
+
+ public HaarWaveletCoder(int compression){
+ transform = new HaarWaveletTransform();
+ this.compression = compression;
+ }
+
+
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+
+ float[] audioBuffer = audioEvent.getFloatBuffer();
+ float[] sortBuffer = new float[audioBuffer.length];
+ transform.transform(audioEvent.getFloatBuffer());
+
+ for (int i = 0; i < sortBuffer.length; i++) {
+ sortBuffer[i] = Math.abs(audioBuffer[i]);
+ }
+ Arrays.sort(sortBuffer);
+
+ double threshold = sortBuffer[compression];
+
+ for (int i = 0; i < audioBuffer.length; i++) {
+ if (Math.abs(audioBuffer[i]) <= threshold) {
+ audioBuffer[i] = 0;
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public void processingFinished() {
+
+ }
+
+ public void setCompression(int compression){
+ this.compression = compression;
+ }
+
+ public int getCompression(){
+ return this.compression;
+ }
+
+}
diff --git a/app/src/main/java/be/tarsos/dsp/wavelet/HaarWaveletDecoder.java b/app/src/main/java/be/tarsos/dsp/wavelet/HaarWaveletDecoder.java
new file mode 100644
index 0000000..789c3ad
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/wavelet/HaarWaveletDecoder.java
@@ -0,0 +1,49 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.wavelet;
+
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.AudioProcessor;
+
+public class HaarWaveletDecoder implements AudioProcessor{
+
+ private final HaarWaveletTransform transform;
+
+ public HaarWaveletDecoder(){
+ transform = new HaarWaveletTransform();
+ }
+
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+ float[] audioBuffer = audioEvent.getFloatBuffer();
+ transform.inverseTransform(audioBuffer);
+ return true;
+ }
+
+ @Override
+ public void processingFinished() {
+
+ }
+
+}
diff --git a/app/src/main/java/be/tarsos/dsp/wavelet/HaarWaveletFileReader.java b/app/src/main/java/be/tarsos/dsp/wavelet/HaarWaveletFileReader.java
new file mode 100644
index 0000000..e1766e9
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/wavelet/HaarWaveletFileReader.java
@@ -0,0 +1,99 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.wavelet;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.AudioProcessor;
+
+public class HaarWaveletFileReader implements AudioProcessor {
+
+ private final int compression;
+ private FileInputStream rawInputStream;
+
+ public HaarWaveletFileReader(String fileName, int compression){
+ this.compression = compression;
+ try {
+ this.rawInputStream = new FileInputStream(fileName);
+ } catch (FileNotFoundException e) {
+ this.rawInputStream = null;
+ }
+ }
+
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+
+ float[] audioBuffer = new float[32];
+
+ byte[] byteBuffer = new byte[(32-compression)*2];
+ int placesWithZero = 0;
+ try {
+ rawInputStream.read(byteBuffer);
+ placesWithZero += rawInputStream.read();
+ placesWithZero += (rawInputStream.read()<<8);
+ placesWithZero += (rawInputStream.read()<<16);
+ placesWithZero += (rawInputStream.read()<<24);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ int byteBufferIndex = 0;
+ for(int i = 0 ; i < audioBuffer.length ; i++){
+ if((placesWithZero & (1< 0;
+ } catch (IOException e) {
+
+ e.printStackTrace();
+ }
+
+ return more;
+ }
+
+ @Override
+ public void processingFinished() {
+ try {
+ rawInputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+}
diff --git a/app/src/main/java/be/tarsos/dsp/wavelet/HaarWaveletFileWriter.java b/app/src/main/java/be/tarsos/dsp/wavelet/HaarWaveletFileWriter.java
new file mode 100644
index 0000000..b3d18f6
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/wavelet/HaarWaveletFileWriter.java
@@ -0,0 +1,107 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.wavelet;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.AudioProcessor;
+
+public class HaarWaveletFileWriter implements AudioProcessor {
+
+
+ private final int compression;
+ private FileOutputStream rawOutputStream;
+
+ public HaarWaveletFileWriter(String fileName, int compression){
+ this.compression = compression;
+ try {
+ this.rawOutputStream = new FileOutputStream(fileName);
+ } catch (FileNotFoundException e) {
+ this.rawOutputStream = null;
+ }
+ }
+
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+ float[] audioBuffer = audioEvent.getFloatBuffer();
+
+ int placesWithZero = 0;
+ int zeroCounter = 0;
+ for(int i = 0 ; i < audioBuffer.length ; i++){
+ if(audioBuffer[i]==0 && zeroCounter < compression){
+ zeroCounter++;
+ placesWithZero = placesWithZero | (1<>> 8);
+ }
+ }
+
+ try {
+ rawOutputStream.write(byteBuffer);
+ rawOutputStream.write((byte) placesWithZero);
+ rawOutputStream.write((byte) (placesWithZero>>>8));
+ rawOutputStream.write((byte) (placesWithZero>>>16));
+ rawOutputStream.write((byte) (placesWithZero>>>24));
+
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+
+ return true;
+ }
+
+ @Override
+ public void processingFinished() {
+ try {
+ rawOutputStream.close();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ }
+
+}
diff --git a/app/src/main/java/be/tarsos/dsp/wavelet/HaarWaveletTransform.java b/app/src/main/java/be/tarsos/dsp/wavelet/HaarWaveletTransform.java
new file mode 100644
index 0000000..b71d034
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/wavelet/HaarWaveletTransform.java
@@ -0,0 +1,140 @@
+/*
+* _______ _____ _____ _____
+* |__ __| | __ \ / ____| __ \
+* | | __ _ _ __ ___ ___ ___| | | | (___ | |__) |
+* | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/
+* | | (_| | | \__ \ (_) \__ \ |__| |____) | |
+* |_|\__,_|_| |___/\___/|___/_____/|_____/|_|
+*
+* -------------------------------------------------------------
+*
+* TarsosDSP is developed by Joren Six at IPEM, University Ghent
+*
+* -------------------------------------------------------------
+*
+* Info: http://0110.be/tag/TarsosDSP
+* Github: https://github.com/JorenSix/TarsosDSP
+* Releases: http://0110.be/releases/TarsosDSP/
+*
+* TarsosDSP includes modified source code by various authors,
+* for credits and info, see README.
+*
+*/
+
+package be.tarsos.dsp.wavelet;
+
+public class HaarWaveletTransform {
+
+ private final boolean preserveEnergy;
+ private final float sqrtTwo = (float) Math.sqrt(2.0);
+
+ public HaarWaveletTransform(boolean preserveEnergy){
+ this.preserveEnergy = preserveEnergy;
+ }
+
+ public HaarWaveletTransform(){
+ this(false);
+ }
+
+ /**
+ * Does an in-place HaarWavelet wavelet transform. The
+ * length of data needs to be a power of two.
+ * It is based on the algorithm found in "Wavelets Made Easy" by Yves Nivergelt, page 24.
+ * @param s The data to transform.
+ */
+ public void transform(float[] s){
+ int m = s.length;
+ assert isPowerOfTwo(m);
+ int n = log2(m);
+ int j = 2;
+ int i = 1;
+ for(int l = 0 ; l < n ; l++ ){
+ m = m/2;
+ for(int k=0; k < m;k++){
+ float a = (s[j*k]+s[j*k + i])/2.0f;
+ float c = (s[j*k]-s[j*k + i])/2.0f;
+ if(preserveEnergy){
+ a = a/sqrtTwo;
+ c = c/sqrtTwo;
+ }
+ s[j*k] = a;
+ s[j*k+i] = c;
+ }
+ i = j;
+ j = j * 2;
+ }
+ }
+
+ /**
+ * Does an in-place inverse HaarWavelet Wavelet Transform. The data needs to be a power of two.
+ * It is based on the algorithm found in "Wavelets Made Easy" by Yves Nivergelt, page 29.
+ * @param data The data to transform.
+ */
+ public void inverseTransform(float[] data){
+ int m = data.length;
+ assert isPowerOfTwo(m);
+ int n = log2(m);
+ int i = pow2(n-1);
+ int j = 2 * i;
+ m = 1;
+ for(int l = n ; l >= 1; l--){
+ for(int k = 0; k < m ; k++){
+ float a = data[j*k]+data[j*k+i];
+ float a1 = data[j*k]-data[j*k+i];
+ if(preserveEnergy){
+ a = a*sqrtTwo;
+ a1 = a1*sqrtTwo;
+ }
+ data[j*k] = a;
+ data[j*k+i] = a1;
+ }
+ j = i;
+ i = i /2;
+ m = 2*m;
+ }
+ }
+
+
+
+ /**
+ * Checks if the number is a power of two. For performance it uses bit shift
+ * operators. e.g. 4 in binary format is
+ * "0000 0000 0000 0000 0000 0000 0000 0100"; and -4 is
+ * "1111 1111 1111 1111 1111 1111 1111 1100"; and 4 & -4 will be
+ * "0000 0000 0000 0000 0000 0000 0000 0100";
+ *
+ * @param number
+ * The number to check.
+ * @return True if the number is a power of two, false otherwise.
+ */
+ public static boolean isPowerOfTwo(int number) {
+ if (number <= 0) {
+ throw new IllegalArgumentException("number: " + number);
+ }
+ return (number & -number) == number;
+ }
+
+ /**
+ * A quick and simple way to calculate log2 of integers.
+ *
+ * @param bits
+ * the integer
+ * @return log2(bits)
+ */
+ public static int log2(int bits) {
+ if (bits == 0) {
+ return 0;
+ }
+ return 31 - Integer.numberOfLeadingZeros(bits);
+ }
+
+ /**
+ * A quick way to calculate the power of two (2^power), by using bit shifts.
+ * @param power The power.
+ * @return 2^power
+ */
+ public static int pow2(int power) {
+ return 1< This software was written and is copyrighted by Ian Kaplan, Bear
+ * Products International, www.bearcave.com, 2001.
+ *
+ * iank@bearcave.com
+ *
+ *
+ * @author Ian Kaplan
+ */
+public class HaarWavelet extends LiftingSchemeBaseWavelet {
+
+ /**
+ * HaarWavelet predict step
+ */
+ protected void predict(float[] vec, int N, int direction) {
+ int half = N >> 1;
+
+ for (int i = 0; i < half; i++) {
+ float predictVal = vec[i];
+ int j = i + half;
+
+ if (direction == forward) {
+ vec[j] = vec[j] - predictVal;
+ } else if (direction == inverse) {
+ vec[j] = vec[j] + predictVal;
+ } else {
+ System.out.println("HaarWavelet::predict: bad direction value");
+ }
+ }
+ }
+
+ /**
+ * Transform forward
+ * @param vec the vector to update.
+ */
+ public void forwardTransOne(float[] vec) {
+ final int N = vec.length;
+
+ split(vec, N);
+ predict(vec, N, forward);
+ update(vec, N, forward);
+
+ } // forwardTrans
+
+ /**
+ *
+ * d j+1, i = odd j+1, i = odd j, i - even j, i
+ * a j+1, i = even j, i = (even j, i + odd j, i )/2
+ *
+ *
+ * a j+1, i = even j, i = even j, i + (odd j, i /2)
+ *
+ */
+ protected void update(float[] vec, int N, int direction) {
+ int half = N >> 1;
+
+ for (int i = 0; i < half; i++) {
+ int j = i + half;
+ float updateVal = vec[j] / 2.0f;
+
+ if (direction == forward) {
+ vec[i] = vec[i] + updateVal;
+ } else if (direction == inverse) {
+ vec[i] = vec[i] - updateVal;
+ } else {
+ System.out.println("update: bad direction value");
+ }
+ }
+ }
+
+} // HaarWavelet
diff --git a/app/src/main/java/be/tarsos/dsp/wavelet/lift/HaarWithPolynomialInterpolationWavelet.java b/app/src/main/java/be/tarsos/dsp/wavelet/lift/HaarWithPolynomialInterpolationWavelet.java
new file mode 100644
index 0000000..5e89a77
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/wavelet/lift/HaarWithPolynomialInterpolationWavelet.java
@@ -0,0 +1,229 @@
+package be.tarsos.dsp.wavelet.lift;
+
+/**
+ *
+ *
+ * http://www.bearcave.com/misl/misl_tech/wavelets/lifting/index.html
+ *
+ *
+ *
+ * Copyright and Use
+ *
+ * This software was written and is copyrighted by Ian Kaplan, Bear
+ * Products International, www.bearcave.com, 2001.
+ *
+ * iank@bearcave.com
+ *
+ *
+ * @author Ian Kaplan
+ */
+public class HaarWithPolynomialInterpolationWavelet extends HaarWavelet {
+ final static int numPts = 4;
+ private final PolynomialInterpolation fourPt;
+
+ /**
+ * HaarWithPolynomialInterpolationWavelet class constructor
+ */
+ public HaarWithPolynomialInterpolationWavelet() {
+ fourPt = new PolynomialInterpolation();
+ }
+
+ /**
+ *
+ *
+ * http://www.bearcave.com/misl/misl_tech/wavelets/lifting/index.html
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ *
+ * Copyright and Use
+ *
+ * This software was written and is copyrighted by Ian Kaplan, Bear
+ * Products International, www.bearcave.com, 2001.
+ *
+ * iank@bearcave.com
+ *
+ *
+ * @author Ian Kaplan
+ */
+public abstract class LiftingSchemeBaseWavelet {
+
+ /** "enumeration" for forward wavelet transform */
+ protected final int forward = 1;
+ /** "enumeration" for inverse wavelet transform */
+ protected final int inverse = 2;
+
+ /**
+ * Split the vec into even and odd elements, where the even elements
+ * are in the first half of the vector and the odd elements are in the
+ * second half.
+ */
+ protected void split(float[] vec, int N) {
+
+ int start = 1;
+ int end = N - 1;
+
+ while (start < end) {
+ for (int i = start; i < end; i = i + 2) {
+ float tmp = vec[i];
+ vec[i] = vec[i + 1];
+ vec[i + 1] = tmp;
+ }
+ start = start + 1;
+ end = end - 1;
+ }
+ }
+
+ /**
+ * Merge the odd elements from the second half of the N element region in
+ * the array with the even elements in the first half of the N element
+ * region. The result will be the combination of the odd and even elements
+ * in a region of length N.
+ */
+ protected void merge(float[] vec, int N) {
+ int half = N >> 1;
+ int start = half - 1;
+ int end = half;
+
+ while (start > 0) {
+ for (int i = start; i < end; i = i + 2) {
+ float tmp = vec[i];
+ vec[i] = vec[i + 1];
+ vec[i + 1] = tmp;
+ }
+ start = start - 1;
+ end = end + 1;
+ }
+ }
+
+ /**
+ * Predict step, to be defined by the subclass
+ *
+ * @param vec
+ * input array
+ * @param N
+ * size of region to act on (from 0..N-1)
+ * @param direction
+ * forward or inverse transform
+ */
+ protected abstract void predict(float[] vec, int N, int direction);
+
+ /**
+ * Update step, to be defined by the subclass
+ *
+ * @param vec
+ * input array
+ * @param N
+ * size of region to act on (from 0..N-1)
+ * @param direction
+ * forward or inverse transform
+ */
+ protected abstract void update(float[] vec, int N, int direction);
+
+ /**
+ * This software was written and is copyrighted by Ian Kaplan, Bear
+ * Products International, www.bearcave.com, 2001.
+ *
+ * iank@bearcave.com
+ *
+ *
+ * @author Ian Kaplan
+ */
+public class LineWavelet extends LiftingSchemeBaseWavelet {
+
+ /**
+ *
+ * x 1 = 0
+ * x 2 = 1
+ *
+ *
+ * x 3 = 2
+ *
+ *
+ * . y 2 - y 1
+ * (y - y 1 ) = -------- (x - x 1 )
+ * . x 2 - x 1
+ *
+ *
+ * . y 2 - y 1
+ * y = -------- (x - x 1 ) + y 1
+ * . x 2 - x 1
+ *
+ *
+ * . y 2 - y 1
+ * y = -------- (x - 0) + y 1
+ * . 1 - 0
+ *
+ *
+ * y = (y 2 - y 1 )*x + y 1
+ *
+ *
+ * y = 2*y 2 - 2*y 1 + y 1
+ *
+ *
+ * y = 2*y 2 - y 1
+ *
+ */
+ private float new_y(float y1, float y2) {
+ float y = 2 * y2 - y1;
+ return y;
+ }
+
+ /**
+ *
+ * a 0 , a 1 , a 1 , a 3 , b 0 , b 1 , b 2 , b 2 ,
+ *
+ *
+ * b j+1,i = b j,i - (a j,i + a j,i+1 )/2
+ *
+ *
+ * even i+1,j = even i,j op (odd i+1,k-1 + odd i+1,k )/4
+ *
+ *
+ * (2 * odd k )/4
+ *
+ *
+ * odd k /2
+ *
+ */
+ protected void update(float[] vec, int N, int direction) {
+ int half = N >> 1;
+
+ for (int i = 0; i < half; i++) {
+ int j = i + half;
+ float val;
+
+ if (i == 0) {
+ val = vec[j] / 2.0f;
+ } else {
+ val = (vec[j - 1] + vec[j]) / 4.0f;
+ }
+ if (direction == forward) {
+ vec[i] = vec[i] + val;
+ } else if (direction == inverse) {
+ vec[i] = vec[i] - val;
+ } else {
+ System.out.println("update: bad direction value");
+ }
+ } // for
+ }
+
+} // LineWavelet
diff --git a/app/src/main/java/be/tarsos/dsp/wavelet/lift/PolynomialInterpolation.java b/app/src/main/java/be/tarsos/dsp/wavelet/lift/PolynomialInterpolation.java
new file mode 100644
index 0000000..1a7f3f1
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/wavelet/lift/PolynomialInterpolation.java
@@ -0,0 +1,189 @@
+package be.tarsos.dsp.wavelet.lift;
+
+/**
+*
+* @author Ian Kaplan
+*/
+class PolynomialInterpolation {
+ /** number of polynomial interpolation ponts */
+ private final static int numPts = 4;
+
+ /** Table for 4-point interpolation coefficients */
+ private final float[][] fourPointTable;
+
+ /** Table for 2-point interpolation coefficients */
+ private final float[][] twoPointTable;
+
+ /**
+ *
+ *
+ * http://www.bearcave.com/misl/misl_tech/wavelets/lifting/index.html
+ *
+ *
+ *
+ * Copyright and Use
+ *
+ * This software was written and is copyrighted by Ian Kaplan, Bear
+ * Products International, www.bearcave.com, 2001.
+ *
+ * iank@bearcave.com
+ *
+ *
+ * @author Ian Kaplan
+ */
+public class PolynomialWavelets extends LiftingSchemeBaseWavelet {
+ final static int numPts = 4;
+ private final PolynomialInterpolation fourPt;
+
+ /**
+ * PolynomialWavelets class constructor
+ */
+ public PolynomialWavelets() {
+ fourPt = new PolynomialInterpolation();
+ }
+
+ /**
+ *
+ *
+ * http://www.bearcave.com/misl/misl_tech/wavelets/lifting/index.html
+ *
+ *
+ *
+ *
+ *
+ * Not yet ready to be supported, so
+ */
+public class WaveHeader {
+
+ // follows WAVE format in http://ccrma.stanford.edu/courses/422/projects/WaveFormat
+
+ private static final int HEADER_LENGTH = 44;
+
+ /** Indicates PCM format. */
+ public static final short FORMAT_PCM = 1;
+ /** Indicates ALAW format. */
+ public static final short FORMAT_ALAW = 6;
+ /** Indicates ULAW format. */
+ public static final short FORMAT_ULAW = 7;
+
+ private short mFormat;
+ private short mNumChannels;
+ private int mSampleRate;
+ private short mBitsPerSample;
+ private int mNumBytes;
+
+ /**
+ * Construct a WaveHeader, with all fields defaulting to zero.
+ */
+ public WaveHeader() {
+ }
+
+ /**
+ * Construct a WaveHeader, with fields initialized.
+ * @param format format of audio data,
+ * one of {@link #FORMAT_PCM}, {@link #FORMAT_ULAW}, or {@link #FORMAT_ALAW}.
+ * @param numChannels 1 for mono, 2 for stereo.
+ * @param sampleRate typically 8000, 11025, 16000, 22050, or 44100 hz.
+ * @param bitsPerSample usually 16 for PCM, 8 for ULAW or 8 for ALAW.
+ * @param numBytes size of audio data after this header, in bytes.
+ */
+ public WaveHeader(short format, short numChannels, int sampleRate, short bitsPerSample, int numBytes) {
+ mFormat = format;
+ mSampleRate = sampleRate;
+ mNumChannels = numChannels;
+ mBitsPerSample = bitsPerSample;
+ mNumBytes = numBytes;
+ }
+
+ /**
+ * Get the format field.
+ * @return format field,
+ * one of {@link #FORMAT_PCM}, {@link #FORMAT_ULAW}, or {@link #FORMAT_ALAW}.
+ */
+ public short getFormat() {
+ return mFormat;
+ }
+
+ /**
+ * Set the format field.
+ * @param format
+ * one of {@link #FORMAT_PCM}, {@link #FORMAT_ULAW}, or {@link #FORMAT_ALAW}.
+ * @return reference to this WaveHeader instance.
+ */
+ public WaveHeader setFormat(short format) {
+ mFormat = format;
+ return this;
+ }
+
+ /**
+ * Get the number of channels.
+ * @return number of channels, 1 for mono, 2 for stereo.
+ */
+ public short getNumChannels() {
+ return mNumChannels;
+ }
+
+ /**
+ * Set the number of channels.
+ * @param numChannels 1 for mono, 2 for stereo.
+ * @return reference to this WaveHeader instance.
+ */
+ public WaveHeader setNumChannels(short numChannels) {
+ mNumChannels = numChannels;
+ return this;
+ }
+
+ /**
+ * Get the sample rate.
+ * @return sample rate, typically 8000, 11025, 16000, 22050, or 44100 hz.
+ */
+ public int getSampleRate() {
+ return mSampleRate;
+ }
+
+ /**
+ * Set the sample rate.
+ * @param sampleRate sample rate, typically 8000, 11025, 16000, 22050, or 44100 hz.
+ * @return reference to this WaveHeader instance.
+ */
+ public WaveHeader setSampleRate(int sampleRate) {
+ mSampleRate = sampleRate;
+ return this;
+ }
+
+ /**
+ * Get the number of bits per sample.
+ * @return number of bits per sample,
+ * usually 16 for PCM, 8 for ULAW or 8 for ALAW.
+ */
+ public short getBitsPerSample() {
+ return mBitsPerSample;
+ }
+
+ /**
+ * Set the number of bits per sample.
+ * @param bitsPerSample number of bits per sample,
+ * usually 16 for PCM, 8 for ULAW or 8 for ALAW.
+ * @return reference to this WaveHeader instance.
+ */
+ public WaveHeader setBitsPerSample(short bitsPerSample) {
+ mBitsPerSample = bitsPerSample;
+ return this;
+ }
+
+ /**
+ * Get the size of audio data after this header, in bytes.
+ * @return size of audio data after this header, in bytes.
+ */
+ public int getNumBytes() {
+ return mNumBytes;
+ }
+
+ /**
+ * Set the size of audio data after this header, in bytes.
+ * @param numBytes size of audio data after this header, in bytes.
+ * @return reference to this WaveHeader instance.
+ */
+ public WaveHeader setNumBytes(int numBytes) {
+ mNumBytes = numBytes;
+ return this;
+ }
+
+ /**
+ * Read and initialize a WaveHeader.
+ * @param in {@link java.io.InputStream} to read from.
+ * @return number of bytes consumed.
+ * @throws IOException
+ */
+ public int read(InputStream in) throws IOException {
+ /* RIFF header */
+ readId(in, "RIFF");
+
+ readId(in, "WAVE");
+
+ /* fmt chunk */
+ readId(in, "fmt ");
+ if (16 != readInt(in)) throw new IOException("fmt chunk length not 16");
+ mFormat = readShort(in);
+ mNumChannels = readShort(in);
+ mSampleRate = readInt(in);
+ int byteRate = readInt(in);
+ short blockAlign = readShort(in);
+ mBitsPerSample = readShort(in);
+ if (byteRate != mNumChannels * mSampleRate * mBitsPerSample / 8) {
+ throw new IOException("fmt.ByteRate field inconsistent");
+ }
+ if (blockAlign != mNumChannels * mBitsPerSample / 8) {
+ throw new IOException("fmt.BlockAlign field inconsistent");
+ }
+
+ /* data chunk */
+ readId(in, "data");
+ mNumBytes = readInt(in);
+
+ return HEADER_LENGTH;
+ }
+
+ private static void readId(InputStream in, String id) throws IOException {
+ for (int i = 0; i < id.length(); i++) {
+ if (id.charAt(i) != in.read()) throw new IOException( id + " tag not present");
+ }
+ }
+
+ private static int readInt(InputStream in) throws IOException {
+ return in.read() | (in.read() << 8) | (in.read() << 16) | (in.read() << 24);
+ }
+
+ private static short readShort(InputStream in) throws IOException {
+ return (short)(in.read() | (in.read() << 8));
+ }
+
+ /**
+ * Write a WAVE file header.
+ * @param out {@link java.io.OutputStream} to receive the header.
+ * @return number of bytes written.
+ * @throws IOException
+ */
+ public int write(OutputStream out) throws IOException {
+ /* RIFF header */
+ writeId(out, "RIFF");
+ writeInt(out, 36 + mNumBytes);
+ writeId(out, "WAVE");
+
+ /* fmt chunk */
+ writeId(out, "fmt ");
+ writeInt(out, 16);
+ writeShort(out, mFormat);
+ writeShort(out, mNumChannels);
+ writeInt(out, mSampleRate);
+ writeInt(out, mNumChannels * mSampleRate * mBitsPerSample / 8);
+ writeShort(out, (short)(mNumChannels * mBitsPerSample / 8));
+ writeShort(out, mBitsPerSample);
+
+ /* data chunk */
+ writeId(out, "data");
+ writeInt(out, mNumBytes);
+
+ return HEADER_LENGTH;
+ }
+
+ private static void writeId(OutputStream out, String id) throws IOException {
+ for (int i = 0; i < id.length(); i++) out.write(id.charAt(i));
+ }
+
+ private static void writeInt(OutputStream out, int val) throws IOException {
+ out.write(val >> 0);
+ out.write(val >> 8);
+ out.write(val >> 16);
+ out.write(val >> 24);
+ }
+
+ private static void writeShort(OutputStream out, short val) throws IOException {
+ out.write(val >> 0);
+ out.write(val >> 8);
+ }
+
+ @Override
+ public String toString() {
+ return String.format(
+ "WaveHeader format=%d numChannels=%d sampleRate=%d bitsPerSample=%d numBytes=%d",
+ mFormat, mNumChannels, mSampleRate, mBitsPerSample, mNumBytes);
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/be/tarsos/dsp/writer/WriterProcessor.java b/app/src/main/java/be/tarsos/dsp/writer/WriterProcessor.java
new file mode 100644
index 0000000..4e43637
--- /dev/null
+++ b/app/src/main/java/be/tarsos/dsp/writer/WriterProcessor.java
@@ -0,0 +1,65 @@
+package be.tarsos.dsp.writer;
+
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+import be.tarsos.dsp.AudioEvent;
+import be.tarsos.dsp.AudioProcessor;
+import be.tarsos.dsp.io.TarsosDSPAudioFormat;
+
+/**
+ * This class writes the ongoing sound to an output specified by the programmer
+ *
+ */
+public class WriterProcessor implements AudioProcessor {
+ RandomAccessFile output;
+ TarsosDSPAudioFormat audioFormat;
+ private int audioLen=0;
+ private static final int HEADER_LENGTH=44;//byte
+
+ /**
+ *
+ * @param audioFormat which this processor is attached to
+ * @param output randomaccessfile of the output file
+ */
+ public WriterProcessor(TarsosDSPAudioFormat audioFormat,RandomAccessFile output){
+ this.output=output;
+ this.audioFormat=audioFormat;
+ try {
+ output.write(new byte[HEADER_LENGTH]);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ @Override
+ public boolean process(AudioEvent audioEvent) {
+ try {
+ audioLen+=audioEvent.getByteBuffer().length;
+ //write audio to the output
+ output.write(audioEvent.getByteBuffer());
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return true;
+ }
+
+ @Override
+ public void processingFinished() {
+ //write header and data to the result output
+ WaveHeader waveHeader=new WaveHeader(WaveHeader.FORMAT_PCM,
+ (short)audioFormat.getChannels(),
+ (int)audioFormat.getSampleRate(),(short)16,audioLen);//16 is for pcm, Read WaveHeader class for more details
+ ByteArrayOutputStream header=new ByteArrayOutputStream();
+ try {
+ waveHeader.write(header);
+ output.seek(0);
+ output.write(header.toByteArray());
+ output.close();
+ }catch (IOException e){
+ e.printStackTrace();
+ }
+
+ }
+}
diff --git a/app/src/main/java/com/ztftrue/music/ErrorTipActivity.kt b/app/src/main/java/com/ztftrue/music/ErrorTipActivity.kt
new file mode 100644
index 0000000..a4225a0
--- /dev/null
+++ b/app/src/main/java/com/ztftrue/music/ErrorTipActivity.kt
@@ -0,0 +1,156 @@
+package com.ztftrue.music
+
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.util.Log
+import androidx.activity.ComponentActivity
+import androidx.activity.compose.BackHandler
+import androidx.activity.compose.setContent
+import androidx.activity.viewModels
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.material3.Button
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.dp
+import androidx.media3.common.util.UnstableApi
+import com.ztftrue.music.ui.theme.MusicPitchTheme
+
+
+class ErrorTipActivity : ComponentActivity() {
+ private val musicViewModel: MusicViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContent {
+ MusicPitchTheme(musicViewModel) {
+ ErrorCollectorView(this@ErrorTipActivity)
+ }
+ }
+ }
+
+ @androidx.annotation.OptIn(UnstableApi::class)
+ @Composable
+ fun ErrorCollectorView(
+ activity: ErrorTipActivity
+ ) {
+ var errorMessage by remember { mutableStateOf("") }
+ LaunchedEffect(Unit) {
+ try {
+ val error: Throwable = intent.getSerializableExtra("error") as Throwable
+ val sb = StringBuilder("")
+ for (element in error.stackTrace) {
+ sb.append(element.toString())
+ sb.append("\n")
+ }
+ errorMessage = sb.toString()
+ } catch (e: Exception) {
+ Log.e("ERROR", e.toString())
+ }
+ }
+ BackHandler(enabled = true) {
+ finish()
+ }
+ Surface(
+ modifier = Modifier
+ .fillMaxSize()
+ .semantics { contentDescription = "MainView" },
+ color = MaterialTheme.colorScheme.background
+ ) {
+
+ Scaffold(modifier = Modifier,
+ topBar = {
+ Column {
+ Text(
+ text = stringResource(R.string.error_tip),
+ color = MaterialTheme.colorScheme.onBackground
+ )
+ HorizontalDivider(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(1.dp)
+ .background(color = MaterialTheme.colorScheme.onBackground)
+ )
+ }
+ }, bottomBar = {
+ Row {
+ Button(
+ onClick = {
+ val intent = Intent(Intent.ACTION_SEND)
+ intent.setType("text/plain")
+ intent.putExtra(Intent.EXTRA_SUBJECT, "Crash report")
+ intent.putExtra(Intent.EXTRA_TEXT, errorMessage)
+ startActivity(
+ Intent.createChooser(
+ intent,
+ "Copy"
+ )
+ )
+ activity.startActivity(intent)
+ }
+ ) {
+ Text(
+ text = stringResource(R.string.feedback)
+ )
+ }
+ Button(
+ onClick = {
+ val intent = Intent(Intent.ACTION_SENDTO).apply {
+ data =
+ Uri.parse("mailto:") // only email apps should handle this
+ putExtra(Intent.EXTRA_EMAIL, arrayOf("ztftrue@gmail.com"))
+ putExtra(Intent.EXTRA_SUBJECT, "Crash report")
+ putExtra(Intent.EXTRA_TEXT, errorMessage)
+ }
+ activity.startActivity(intent)
+ }
+ ) {
+ Text(
+ text = stringResource(R.string.send_email)
+ )
+ }
+ }
+
+ }, content = {
+ LazyColumn(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(it)
+ ) {
+ item(1) {
+ Text(
+ text = stringResource(R.string.sorry_some_error_happens_you_can_feedback_it_with_this_message),
+ color = MaterialTheme.colorScheme.onBackground
+ )
+ Text(
+ text = errorMessage,
+ color = MaterialTheme.colorScheme.onBackground
+ )
+ }
+ }
+ })
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/ztftrue/music/MainActivity.kt b/app/src/main/java/com/ztftrue/music/MainActivity.kt
index 36b76c6..ddf735f 100644
--- a/app/src/main/java/com/ztftrue/music/MainActivity.kt
+++ b/app/src/main/java/com/ztftrue/music/MainActivity.kt
@@ -20,7 +20,6 @@ import android.support.v4.media.MediaMetadataCompat
import android.support.v4.media.session.MediaControllerCompat
import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat
-import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.ActivityResultLauncher
@@ -37,6 +36,7 @@ import androidx.compose.material3.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.sp
import androidx.core.app.ActivityCompat
import androidx.core.splashscreen.SplashScreen
@@ -276,13 +276,13 @@ class MainActivity : ComponentActivity() {
ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode == RESULT_OK) {
- val treeUri = result.data?.data;
+ val treeUri = result.data?.data
if (treeUri != null) {
contentResolver.takePersistableUriPermission(
treeUri,
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
)
- CoroutineScope(Dispatchers.IO).launch{
+ CoroutineScope(Dispatchers.IO).launch {
musicViewModel.getDb(this@MainActivity).StorageFolderDao().insert(
StorageFolder(null, treeUri.toString())
)
@@ -352,7 +352,7 @@ class MainActivity : ComponentActivity() {
verticalArrangement = Arrangement.Center
) {
Text(
- text = "Can't find any audio file\n I need permission\nClick here to open settings",
+ text = stringResource(R.string.can_t_find_any_audio_file_i_need_permission_click_here_to_open_settings),
fontSize = 20.sp,
color = Color.Red
)
@@ -493,7 +493,7 @@ class MainActivity : ComponentActivity() {
// before switch to another music, must clear lyrics
musicViewModel.currentCaptionList.clear()
val index = it.getInt("index")
- if (index >= 0 && musicViewModel.musicQueue.size > index) {
+ if (index >= 0 && musicViewModel.musicQueue.size > index && index != musicViewModel.currentPlayQueueIndex.intValue) {
musicViewModel.currentMusicCover.value = null
musicViewModel.currentPlay.value =
musicViewModel.musicQueue[index]
@@ -504,7 +504,6 @@ class MainActivity : ComponentActivity() {
this@MainActivity,
musicViewModel.musicQueue[index]
)
-
}
} else if (it.getInt("type") == EVENT_MEDIA_METADATA_Change) {
val cover = it.getByteArray("cover")
@@ -602,7 +601,6 @@ class MainActivity : ComponentActivity() {
musicViewModel.musicQueue.addAll(it)
}
resultData.getParcelableArrayList