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 }