private Peak[] getPeaks(double[] energies) {
   ArrayList<Peak> peaks = new ArrayList<Peak>();
   LEVEL[] levels = LEVEL.values();
   for (int i = 0; i < levels.length; i += 1) {
     if (detectors[i].addEnergy(energies[i])) {
       peaks.add(new Peak(levels[i], (int) energies[i]));
     }
   }
   Peak[] content = new Peak[peaks.size()];
   return peaks.toArray(content);
 }
public class Analyzer {

  public static interface OnPeakListener {
    public void onPeak(Peak[] peaks);
  }

  public static class Peak {

    private LEVEL level;
    private float value;

    public Peak(LEVEL level, float value) {
      this.level = level;
      this.value = value;
    }

    public LEVEL getLevel() {
      return level;
    }

    public float getValue() {
      return value;
    }
  }

  public static enum LEVEL {
    LOW_BASS(0),
    HIGH_BASS(1),
    LOW_MIDDLE(2),
    MEDIUM_MIDDLE(3),
    HIGH_MIDDLE(4),
    LOW_HIGH(5),
    HIGH_HIGH(6);

    private int number;

    private LEVEL(int number) {
      this.number = number;
    }

    public int getNumber() {
      return number;
    }

    public boolean isBass() {
      return this == LOW_BASS || this == HIGH_BASS;
    }

    public boolean isMiddle() {
      return this == LOW_MIDDLE || this == MEDIUM_MIDDLE || this == HIGH_MIDDLE;
    }

    public boolean isHigh() {
      return this == LOW_HIGH || this == HIGH_HIGH;
    }
  }

  private static class BeatDetector {

    private static final int MAX_HISTORY_SIZE = 200;
    private static final double MAGICK = 1.3;
    private static final int DETECTION_THRESHOLD = 5;

    private LinkedList<Double> history = new LinkedList<Double>();
    private double sum;
    private int detections;

    public boolean addEnergy(double energy) {
      history.add(energy);
      sum += energy;
      if (history.size() > MAX_HISTORY_SIZE) {
        sum -= history.poll();
      }
      double average = sum / history.size();
      if (energy > average * MAGICK) {
        detections += 1;
      }
      if (detections == DETECTION_THRESHOLD) {
        detections = 0;
        return true;
      }
      return false;
    }
  }

  private static final int[] FREQUENCIES = {10, 80, 200, 500, 2500, 500, 10000, 20000};

  private BeatDetector[] detectors = new BeatDetector[LEVEL.values().length];
  private OnPeakListener listener;
  private double[] levels;
  private double[] energies = new double[FREQUENCIES.length - 1];

  public Analyzer(OnPeakListener listener) {
    initDetectors();
    this.listener = listener;
  }

  private void initDetectors() {
    for (int i = 0; i < detectors.length; i += 1) {
      detectors[i] = new BeatDetector();
    }
  }

  private double getFrequencyLevel(byte real, byte imaginary) {
    return Math.sqrt(real * real + imaginary * imaginary);
  }

  private double[] getFrequencyLevels(byte[] data) {
    if (levels == null) {
      levels = new double[data.length / 2];
    }
    levels[0] = getFrequencyLevel(data[0], (byte) 0);
    for (int i = 1; i < levels.length - 1; i += 1) {
      levels[i] = getFrequencyLevel(data[i * 2], data[i * 2 + 1]);
    }
    levels[levels.length - 1] = getFrequencyLevel(data[1], (byte) 0);
    return levels;
  }

  private double getEnergy(double[] levels, int minFrequency, int maxFrequency) {
    double step = FREQUENCIES[FREQUENCIES.length - 1] / levels.length;
    int begin = (int) (minFrequency / step);
    int end = (int) (maxFrequency / step);
    double energy = 0;
    for (int i = begin; i < end; i += 1) {
      if (levels[i] < 1) {
        continue;
      }
      energy += Math.log(levels[i]);
    }
    return energy;
  }

  private double[] getEnergies(double[] levels) {
    for (int i = 0; i < FREQUENCIES.length - 1; i += 1) {
      energies[i] = getEnergy(levels, FREQUENCIES[i], FREQUENCIES[i + 1]);
    }
    return energies;
  }

  private Peak[] getPeaks(double[] energies) {
    ArrayList<Peak> peaks = new ArrayList<Peak>();
    LEVEL[] levels = LEVEL.values();
    for (int i = 0; i < levels.length; i += 1) {
      if (detectors[i].addEnergy(energies[i])) {
        peaks.add(new Peak(levels[i], (int) energies[i]));
      }
    }
    Peak[] content = new Peak[peaks.size()];
    return peaks.toArray(content);
  }

  public void setFftData(byte[] data) {
    double[] levels = getFrequencyLevels(data);
    double[] energies = getEnergies(levels);
    Peak[] peaks = getPeaks(energies);
    if (peaks.length != 0) {
      listener.onPeak(peaks);
    }
  }
}