plectrum

Plectrum: instrument tuner for Android
Log | Files | Refs | README | LICENSE

ConstantQ.java (12653B)


      1 /*
      2 *      _______                       _____   _____ _____  
      3 *     |__   __|                     |  __ \ / ____|  __ \ 
      4 *        | | __ _ _ __ ___  ___  ___| |  | | (___ | |__) |
      5 *        | |/ _` | '__/ __|/ _ \/ __| |  | |\___ \|  ___/ 
      6 *        | | (_| | |  \__ \ (_) \__ \ |__| |____) | |     
      7 *        |_|\__,_|_|  |___/\___/|___/_____/|_____/|_|     
      8 *                                                         
      9 * -------------------------------------------------------------
     10 *
     11 * TarsosDSP is developed by Joren Six at IPEM, University Ghent
     12 *  
     13 * -------------------------------------------------------------
     14 *
     15 *  Info: http://0110.be/tag/TarsosDSP
     16 *  Github: https://github.com/JorenSix/TarsosDSP
     17 *  Releases: http://0110.be/releases/TarsosDSP/
     18 *  
     19 *  TarsosDSP includes modified source code by various authors,
     20 *  for credits and info, see README.
     21 * 
     22 */
     23 
     24 /*
     25 *      _______                       _____   _____ _____  
     26 *     |__   __|                     |  __ \ / ____|  __ \ 
     27 *        | | __ _ _ __ ___  ___  ___| |  | | (___ | |__) |
     28 *        | |/ _` | '__/ __|/ _ \/ __| |  | |\___ \|  ___/ 
     29 *        | | (_| | |  \__ \ (_) \__ \ |__| |____) | |     
     30 *        |_|\__,_|_|  |___/\___/|___/_____/|_____/|_|     
     31 *                                                         
     32 * -----------------------------------------------------------
     33 *
     34 *  TarsosDSP is developed by Joren Six at 
     35 *  The Royal Academy of Fine Arts & Royal Conservatory,
     36 *  University College Ghent,
     37 *  Hoogpoort 64, 9000 Ghent - Belgium
     38 *  
     39 *  http://tarsos.0110.be/tag/TarsosDSP
     40 *  https://github.com/JorenSix/TarsosDSP
     41 *  http://tarsos.0110.be/releases/TarsosDSP/
     42 * 
     43 */
     44 /* 
     45  * Copyright (c) 2006, Karl Helgason
     46  * 
     47  * 2007/1/8 modified by p.j.leonard
     48  * 
     49  * All rights reserved.
     50  * 
     51  * Redistribution and use in source and binary forms, with or without
     52  * modification, are permitted provided that the following conditions
     53  * are met:
     54  * 
     55  *    1. Redistributions of source code must retain the above copyright
     56  *       notice, this list of conditions and the following disclaimer.
     57  *    2. Redistributions in binary form must reproduce the above
     58  *       copyright notice, this list of conditions and the following
     59  *       disclaimer in the documentation and/or other materials
     60  *       provided with the distribution.
     61  *    3. The name of the author may not be used to endorse or promote
     62  *       products derived from this software without specific prior
     63  *       written permission.
     64  * 
     65  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     66  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     67  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     68  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
     69  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     70  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
     71  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     72  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
     73  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     74  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
     75  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     76  */
     77 
     78 package be.tarsos.dsp;
     79 
     80 import be.tarsos.dsp.util.fft.FFT;
     81 
     82 /**
     83  * Implementation of the Constant Q Transform.<br> References:
     84  * <p>
     85  * Judith C. Brown, <a
     86  * href="http://www.wellesley.edu/Physics/brown/pubs/cq1stPaper.pdf">
     87  * Calculation of a constant Q spectral transform</a>, J. Acoust. Soc. Am.,
     88  * 89(1): 425-434, 1991.
     89  * </p>
     90  * <p>
     91  * Judith C. Brown and Miller S. Puckette, <a
     92  * href="http://www.wellesley.edu/Physics/brown/pubs/effalgV92P2698-P2701.pdf"
     93  * >An efficient algorithm for the calculation of a constant Q transform</a>, J.
     94  * Acoust. Soc. Am., Vol. 92, No. 5, November 1992
     95  * </p>
     96  * <p>
     97  * Benjamin Blankertz, <a href=
     98  * "http://wwwmath1.uni-muenster.de/logik/org/staff/blankertz/constQ/constQ.pdf"
     99  * >The Constant Q Transform</a>
    100  * </p>
    101  * 
    102  * 
    103  * @author Joren Six
    104  * @author Karl Helgason
    105  * @author P.J Leonard
    106  */
    107 public class ConstantQ implements AudioProcessor {
    108 
    109 
    110 	/**
    111 	 * The minimum frequency, in Hertz. The Constant-Q factors are calculated
    112 	 * starting from this frequency.
    113 	 */
    114 	private final float minimumFrequency ;
    115 
    116 	/**
    117 	 * The maximum frequency in Hertz.
    118 	 */
    119 	private final float maximumFreqency;
    120 
    121 	/**
    122 	 * The length of the underlying FFT.
    123 	 */
    124 	private int fftLength;
    125 
    126 	/**
    127 	 * Lists the start of each frequency bin, in Hertz. 
    128 	 */
    129 	private final float[] frequencies;
    130 
    131 	private final float[][] qKernel;
    132 
    133 	private final int[][] qKernel_indexes;
    134 	
    135 	/**
    136 	 * The array with constant q coefficients. If you for
    137 	 * example are interested in coefficients between 256 and 1024 Hz
    138 	 * (2^8 and 2^10 Hz) and you requested 12 bins per octave, you
    139 	 * will need 12 bins/octave * 2 octaves * 2 entries/bin = 48
    140 	 * places in the output buffer. The coefficient needs two entries
    141 	 * in the output buffer since they are complex numbers.
    142 	 */
    143 	private final float[] coefficients;
    144 	
    145 	/**
    146 	 * The output buffer with constant q magnitudes. If you for example are
    147 	 * interested in coefficients between 256 and 1024 Hz (2^8 and 2^10 Hz) and
    148 	 * you requested 12 bins per octave, you will need 12 bins/octave * 2
    149 	 * octaves = 24 places in the output buffer.
    150 	 */
    151 	private final float[] magnitudes;
    152 	
    153 	/**
    154 	 * The number of bins per octave.
    155 	 */
    156 	private int binsPerOctave;
    157 
    158 	/**
    159 	 * The underlying FFT object.
    160 	 */
    161 	private FFT fft;
    162 
    163 
    164 	public ConstantQ(float sampleRate, float minFreq, float maxFreq,float binsPerOctave) {
    165 		this(sampleRate,minFreq,maxFreq,binsPerOctave,0.001f,1.0f);
    166 	}
    167 
    168 	
    169 	public ConstantQ(float sampleRate, float minFreq, float maxFreq,float binsPerOctave, float threshold,float spread) {
    170 		this.minimumFrequency = minFreq;
    171 		this.maximumFreqency = maxFreq;
    172 		this.binsPerOctave = (int) binsPerOctave;
    173 		
    174 		// Calculate Constant Q		
    175 		double q = 1.0 / (Math.pow(2, 1.0 / binsPerOctave) - 1.0) / spread;
    176 
    177 		// Calculate number of output bins
    178 		int numberOfBins = (int) Math.ceil(binsPerOctave * Math.log(maximumFreqency / minimumFrequency) / Math.log(2));
    179 		
    180 		// Initialize the coefficients array (complex number so 2 x number of bins)
    181 		coefficients = new float[numberOfBins*2];
    182 		
    183 		// Initialize the magnitudes array
    184 		magnitudes = new float[numberOfBins];
    185 
    186 		
    187 		// Calculate the minimum length of the FFT to support the minimum
    188 		// frequency
    189 		float calc_fftlen = (float) Math.ceil(q * sampleRate / minimumFrequency);
    190 		
    191 		// No need to use power of 2 FFT length.
    192 		fftLength = (int) calc_fftlen; 
    193 		
    194 		//System.out.println(fftLength);
    195 		//The FFT length needs to be a power of two for performance reasons:
    196 		fftLength = (int) Math.pow(2, Math.ceil(Math.log(calc_fftlen) / Math.log(2)));
    197 		
    198 		// Create FFT object
    199 		fft = new FFT(fftLength);
    200 		qKernel = new float[numberOfBins][];
    201 		qKernel_indexes = new int[numberOfBins][];
    202 		frequencies = new float[numberOfBins];
    203 
    204 		// Calculate Constant Q kernels
    205 		 float[] temp = new float[fftLength*2];
    206 		 float[] ctemp = new float[fftLength*2];
    207 		 int[] cindexes = new int[fftLength];
    208 		    for (int i = 0; i < numberOfBins; i++) {
    209 		    	float[] sKernel = temp;
    210 		    	// Calculate the frequency of current bin
    211 		    	frequencies[i] = (float) (minimumFrequency * Math.pow(2, i/binsPerOctave ));
    212 		    	
    213 		    	// Calculate length of window
    214 		    	int len = (int)Math.min(Math.ceil( q * sampleRate / frequencies[i]), fftLength);
    215 		    	
    216 		    	for (int j = 0; j < len; j++) {
    217 		    		
    218 		    		double window = -.5*Math.cos(2.*Math.PI*(double)j/(double)len)+.5;; // Hanning Window
    219 		    		// double window = -.46*Math.cos(2.*Math.PI*(double)j/(double)len)+.54; // Hamming Window
    220 
    221 		    		window /= len;
    222 		    		
    223 		    		// Calculate kernel
    224 		    	    double x = 2*Math.PI * q * (double)j/(double)len;
    225 		    		sKernel[j*2] = (float) (window * Math.cos(x));
    226 		    		sKernel[j*2+1] = (float) (window * Math.sin(x));	
    227 				}
    228 		    	for (int j = len*2; j < fftLength*2; j++) {
    229 		    		sKernel[j] = 0;
    230 		    	}
    231 
    232 			// Perform FFT on kernel
    233 			fft.complexForwardTransform(sKernel);
    234 
    235 			// Remove all zeros from kernel to improve performance
    236 			float[] cKernel = ctemp;
    237 
    238 			int k = 0;
    239 			for (int j = 0, j2 = sKernel.length - 2; j < sKernel.length/2; j+=2,j2-=2)
    240 	    	{
    241 	    		double absval = Math.sqrt(sKernel[j]*sKernel[j] + sKernel[j+1]*sKernel[j+1]);
    242 	    	    absval += Math.sqrt(sKernel[j2]*sKernel[j2] + sKernel[j2+1]*sKernel[j2+1]);	    	    
    243 	    		if(absval > threshold)
    244 	    		{
    245 	    			cindexes[k] = j;
    246 	    			cKernel[2*k] = sKernel[j] + sKernel[j2];
    247 	    			cKernel[2*k + 1] = sKernel[j + 1] + sKernel[j2 + 1];
    248 	    			k++;
    249 	    		}	    		
    250 	    	}
    251 
    252 			sKernel = new float[k * 2];
    253 			int[] indexes = new int[k];
    254 
    255 			for (int j = 0; j < k * 2; j++)
    256 				sKernel[j] = cKernel[j];
    257 			for (int j = 0; j < k; j++)
    258 				indexes[j] = cindexes[j];
    259 
    260 			// Normalize fft output
    261 			for (int j = 0; j < sKernel.length; j++)
    262 				sKernel[j] /= fftLength;
    263 
    264 			// Perform complex conjugate on sKernel
    265 			for (int j = 1; j < sKernel.length; j += 2)
    266 				sKernel[j] = -sKernel[j];
    267 			
    268 			for (int j = 0; j < sKernel.length; j ++)
    269 				sKernel[j] = -sKernel[j];
    270 
    271 			qKernel_indexes[i] = indexes;
    272 			qKernel[i] = sKernel;
    273 		}
    274 	}
    275 
    276 	/**
    277 	 * Take an input buffer with audio and calculate the constant Q
    278 	 * coefficients.
    279 	 * 
    280 	 * @param inputBuffer
    281 	 *            The input buffer with audio.
    282 	 * 
    283 	 *            
    284 	 */
    285 	public void calculate(float[] inputBuffer) {
    286 		fft.forwardTransform(inputBuffer);
    287 		for (int i = 0; i < qKernel.length; i++) {
    288 			float[] kernel = qKernel[i];
    289 			int[] indexes = qKernel_indexes[i];
    290 			float t_r = 0;
    291 			float t_i = 0;
    292 			for (int j = 0, l = 0; j < kernel.length; j += 2, l++) {
    293 				int jj = indexes[l];
    294 				float b_r = inputBuffer[jj];
    295 				float b_i = inputBuffer[jj + 1];
    296 				float k_r = kernel[j];
    297 				float k_i = kernel[j + 1];
    298 				// COMPLEX: T += B * K
    299 				t_r += b_r * k_r - b_i * k_i;
    300 				t_i += b_r * k_i + b_i * k_r;
    301 			}
    302 			coefficients[i * 2] = t_r;
    303 			coefficients[i * 2 + 1] = t_i;
    304 		}
    305 	}
    306 	
    307 	/**
    308 	 * Take an input buffer with audio and calculate the constant Q magnitudes.
    309 	 * @param inputBuffer The input buffer with audio.
    310 	 */
    311 	public void calculateMagintudes(float[] inputBuffer) {
    312 		calculate(inputBuffer);
    313 		for(int i = 0 ; i < magnitudes.length ; i++){
    314 			magnitudes[i] = (float) Math.sqrt(coefficients[i*2] * coefficients[i*2] + coefficients[i*2+1] * coefficients[i*2+1]); 
    315 		}
    316 	}
    317 	
    318 
    319 	@Override
    320 	public boolean process(AudioEvent audioEvent) {
    321 		float[] audioBuffer = audioEvent.getFloatBuffer().clone();
    322 		if(audioBuffer.length != getFFTlength()){
    323 			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));
    324 		}
    325 		calculateMagintudes(audioBuffer);
    326 		return true;
    327 	}
    328 
    329 	@Override
    330 	public void processingFinished() {
    331 		// Do nothing.
    332 	}
    333 	
    334 	//----GETTERS
    335 
    336 	/**
    337 	 * @return The list of starting frequencies for each band. In Hertz.
    338 	 */
    339 	public float[] getFreqencies() {
    340 		return frequencies;
    341 	}
    342 	
    343 	/**
    344 	 * Returns the Constant Q magnitudes calculated for the previous audio
    345 	 * buffer. Beware: the array is reused for performance reasons. If your need
    346 	 * to cache your results, please copy the array.
    347 	 * @return The output buffer with constant q magnitudes. If you for example are
    348 	 * interested in coefficients between 256 and 1024 Hz (2^8 and 2^10 Hz) and
    349 	 * you requested 12 bins per octave, you will need 12 bins/octave * 2
    350 	 * octaves = 24 places in the output buffer.
    351 	 */
    352 	public float[] getMagnitudes() {
    353 		return magnitudes;
    354 	}
    355 
    356 	
    357 	/**
    358 	 * Return the Constant Q coefficients calculated for the previous audio
    359 	 * buffer. Beware: the array is reused for performance reasons. If your need
    360 	 * to cache your results, please copy the array.
    361 	 * 
    362 	 * @return The array with constant q coefficients. If you for example are
    363 	 *         interested in coefficients between 256 and 1024 Hz (2^8 and 2^10
    364 	 *         Hz) and you requested 12 bins per octave, you will need 12
    365 	 *         bins/octave * 2 octaves * 2 entries/bin = 48 places in the output
    366 	 *         buffer. The coefficient needs two entries in the output buffer
    367 	 *         since they are complex numbers.
    368 	 */
    369 	public float[] getCoefficients() {
    370 		return coefficients;
    371 	}
    372 
    373 	/**
    374 	 * @return The number of coefficients, output bands.
    375 	 */
    376 	public int getNumberOfOutputBands() {
    377 		return frequencies.length;
    378 	}
    379 
    380 	/**
    381 	 * @return The required length the FFT.
    382 	 */
    383 	public int getFFTlength() {
    384 		return fftLength;
    385 	}
    386 	
    387 	/**
    388 	 * @return the number of bins every octave.
    389 	 */
    390 	public int getBinsPerOctave(){
    391 		return binsPerOctave;
    392 	}
    393 }