plectrum

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

PeakPicker.java (4772B)


      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 package be.tarsos.dsp.util;
     25 
     26 import java.util.Arrays;
     27 
     28 /**
     29  * Implements a moving mean adaptive threshold peak picker.
     30  * 
     31  * The implementation is a translation of peakpicker.c from Aubio, Copyright (C)
     32  * 2003-2009 Paul Brossier <piem@aubio.org>
     33  * 
     34  * @author Joren Six
     35  * @author Paul Brossiers
     36  */
     37 public class PeakPicker {
     38 	/** thresh: offset threshold [0.033 or 0.01] */
     39 	private double threshold;
     40 	/** win_post: median filter window length (causal part) [8] */
     41 	private int win_post;
     42 	/** pre: median filter window (anti-causal part) [post-1] */
     43 	private int win_pre;
     44 	
     45 	/** biquad low pass filter */
     46 	private BiQuadFilter biquad;
     47 	
     48 	/** original onsets */
     49 	private float[] onset_keep;
     50 	/** modified onsets */
     51 	private float[] onset_proc;
     52 	/** peak picked window [3] */
     53 	private float[] onset_peek;
     54 	/** scratch pad for biquad and median */
     55 	private float[] scratch;
     56 	
     57 	private float lastPeekValue;
     58 	
     59 	/**
     60 	 * Initializes a new moving mean adaptive threshold peak picker.
     61 	 * 
     62 	 * @param threshold
     63 	 *            The threshold defines when a peak is selected. It should be
     64 	 *            between zero and one, 0.3 is a reasonable value. If too many
     65 	 *            peaks are detected go to 0.5 - 0.8.
     66 	 */
     67 	public PeakPicker(double threshold) {
     68 		/* Low-pass filter cutoff [0.34, 1] */		
     69 		biquad = new BiQuadFilter(0.1600,0.3200,0.1600,-0.5949,0.2348);
     70 		this.threshold = threshold;
     71 		win_post = 5;
     72 		win_pre = 1;
     73 		
     74 		onset_keep = new float[win_post + win_pre +1];
     75 		onset_proc =  new float[win_post + win_pre +1];
     76 		scratch =  new float[win_post + win_pre +1];
     77 		onset_peek = new float[3];		
     78 	}
     79 	
     80 	/**
     81 	 * Sets a new threshold.
     82 	 * 
     83 	 * @param threshold
     84 	 *            The threshold defines when a peak is selected. It should be
     85 	 *            between zero and one, 0.3 is a reasonable value. If too many
     86 	 *            peaks are detected go to 0.5 - 0.8.
     87 	 */
     88 	public void setThreshold(double threshold) {
     89 		this.threshold = threshold;
     90 	}
     91 	
     92 	/**
     93 	 * Modified version for real time, moving mean adaptive threshold this
     94 	 * method is slightly more permissive than the off-LineWavelet one, and yields to
     95 	 * an increase of false positives.
     96 	 * 
     97 	 * @param onset
     98 	 *            The new onset value.
     99 	 * @return True if a peak is detected, false otherwise.
    100 	 **/
    101 	public boolean pickPeak(float onset) {
    102 		float mean = 0.f;
    103 		float median = 0.f;
    104 		
    105 		int length = win_post + win_pre + 1;
    106 		
    107 		
    108 		/* store onset in onset_keep */
    109 		/* shift all elements but last, then write last */
    110 		/* for (i=0;i<channels;i++) { */
    111 		for(int j=0;j<length-1;j++) {
    112 			onset_keep[j] = onset_keep[j+1];
    113 			onset_proc[j] = onset_keep[j];
    114 		}
    115 		onset_keep[length-1] = onset;
    116 		onset_proc[length-1] = onset;
    117 		
    118 		/* filter onset_proc */
    119 		/** \bug filtfilt calculated post+pre times, should be only once !? */
    120 		biquad.doFiltering(onset_proc,scratch);
    121 
    122 		/* calculate mean and median for onset_proc */
    123 		
    124 		/* copy to scratch */
    125 		float sum = 0.0f;
    126 		for (int j = 0; j < length; j++){
    127 			scratch[j] = onset_proc[j];
    128 			sum += scratch[j];
    129 		}
    130 		Arrays.sort(scratch);
    131 		median = scratch[scratch.length/2];
    132 		mean = sum/Float.valueOf(length);
    133 				
    134 		/* shift peek array */
    135 		for (int j=0;j<3-1;j++){
    136 			onset_peek[j] = onset_peek[j+1];
    137 		}
    138 		/* calculate new peek value */
    139 		onset_peek[2] = (float) (onset_proc[win_post] - median - mean * threshold);
    140 		
    141 		boolean isPeak = isPeak(1);
    142 		lastPeekValue = onset;
    143 	
    144 		return isPeak;
    145 	}
    146 	
    147 	/**
    148 	 * 
    149 	 * @return The value of the last detected peak, or zero. 
    150 	 */
    151 	public float getLastPeekValue() {
    152 		return lastPeekValue;
    153 	}
    154 	
    155 	/**
    156 	 * Returns true if the onset is a peak.
    157 	 * 
    158 	 * @param index
    159 	 *            the index in onset_peak to check.
    160 	 * @return True if the onset is a peak, false otherwise.
    161 	 */
    162 	private boolean isPeak(int index) {
    163 		return (	onset_peek[index] > onset_peek[index - 1] &&
    164 					onset_peek[index] > onset_peek[index + 1] && 
    165 					onset_peek[index] > 0.);
    166 	}
    167 }