/*
*
      
_______
                       
_____
   
_____ _____
  

*
     
|__
   
__|
                     
|
  
__ \ / ____|__ \
 

*
        
| | __ _ _ __ ___
  
______| || | (___ | |__) |
*
        
| |/ _` | '__/ __|/ _ \/ __| |
  
| |\___ \|___/
 

*
        
| | (_| | |
  
\__ \ (_) \__ \ |__| |____) | |
     

*
        
|_|\__,_|_|
  
|___/\___/|___/_____/|_____/|_|
     

*
                                                         

* -------------------------------------------------------------
*
* 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.example.dissonance;

import java.util.ArrayList;
import java.util.List;

import javax.sound.sampled.UnsupportedAudioFileException;

import be.tarsos.dsp.AudioDispatcher;
import be.tarsos.dsp.AudioEvent;
import be.tarsos.dsp.AudioProcessor;
import be.tarsos.dsp.SpectralPeakProcessor;
import be.tarsos.dsp.io.PipedAudioStream;
import be.tarsos.dsp.io.TarsosDSPAudioInputStream;

public class SpectralPeakFollowerExample {
	

	
private final int sampleRate = 44100;
	
private final int fftsize = 32768/2;
	
private final int overlap = fftsize/2;//50% overlap
	
//private final int noiseFloorMedianFilterLenth = fftsize/117;//35
	
//private final float noiseFloorFactor = 1.5f;
	
private final String fileName;
	
//private final int numberOfSpectralPeaks;
	

	
/**
	 
* Peaks are only allowed between the highest peak minus 2400 and plus 7200 cents (8 octaves in total)
	 
*/

	
//private final int minimumCentsBelowHighest = 2400;//2 octaves
	
//private final int maximumCentsAboveHighest = 7200;//6 octaves

	

	

	
private final List<float[]> magnitudesList = new ArrayList<float[]>();
	
private final List<float[]> frequencyEstimatesList = new ArrayList<float[]>();
	

	

	
public SpectralPeakFollowerExample(String fileName,int numberOfSpectralPeaks){
		
this.fileName = fileName;
		
//this.numberOfSpectralPeaks = numberOfSpectralPeaks;
		

	
}
	

	
private void extractPeakListList() throws UnsupportedAudioFileException{
		
PipedAudioStream f = new PipedAudioStream(fileName);
		
TarsosDSPAudioInputStream stream = f.getMonoStream(sampleRate,0);
		

		
final SpectralPeakProcessor spectralPeakFollower = new SpectralPeakProcessor(fftsize, overlap, sampleRate);
		
AudioDispatcher dispatcher = new AudioDispatcher(stream, fftsize, overlap);
		
dispatcher.addAudioProcessor(spectralPeakFollower);
		
dispatcher.addAudioProcessor(new AudioProcessor() {
			
@Override
			
public void processingFinished() {
			
}
			

			
@Override
			
public boolean process(AudioEvent audioEvent) {
				
magnitudesList.add(spectralPeakFollower.getMagnitudes());
				
frequencyEstimatesList.add(spectralPeakFollower.getFrequencyEstimates());
				
return true;
			
}
		
});
		
dispatcher.run();
	
}
	

	
private void processPeakListList(){
		
/*
		
KernelDensityEstimate kde = new KernelDensityEstimate(new KernelDensityEstimate.GaussianKernel(30), minimumCentsBelowHighest + maximumCentsAboveHighest);
		
for(List<SpectralPeak> spectralPeakList : peakListList){
			
for(SpectralPeak spectralPeak : spectralPeakList){
				
//avoids peaks more than 2 octaves below the highest peak
				
if(
						
spectralPeak.getRelativeFrequencyInCents() + minimumCentsBelowHighest >= 0
						
&&
						
spectralPeak.getRelativeFrequencyInCents() <= maximumCentsAboveHighest
 

				
){
					
//String point = String.format("%.2f;%.2f", spectralPeak.getRelativeFrequencyInCents(),spectralPeak.getMagnitude());
					
for(int i = 0 ; i < Math.log(spectralPeak.getMagnitude()* 100) ; i++){
						
kde.add(spectralPeak.getRelativeFrequencyInCents() + minimumCentsBelowHighest);
					
}
				
}
			
}
		
}
		

		
kde.normalize(100.0);
		
double[] estimate = kde.getEstimate();
		

		
//all peaks are within the largest 5 percent of the estimate
		
double noiseThreshold = SpectralPeakFollower.percentile(estimate.clone(),0.95);
						

		
List<Double> peakValues = new ArrayList<Double>();
		
for(int i
  
= 1 ; i < estimate.length - 1 ;i++){
			
boolean largerThanPrev = estimate[i] > estimate[i-1];
			
boolean largerThanNext = estimate[i] > estimate[i+1];
			
boolean largerThanNoise = estimate[i] >= noiseThreshold;
			
if(largerThanNoise && largerThanNext && largerThanPrev){
				
peakValues.add(estimate[i]);
			
}
		
}
		

		
double threshold = noiseThreshold;
		
if(peakValues.size()>numberOfSpectralPeaks){
			
Collections.sort(peakValues);
			
threshold = peakValues.get(peakValues.size()-numberOfSpectralPeaks);
		
}
		

		

		
System.out.println("Cent Amplitude ratio");
		
for(int i
  
= 1 ; i < estimate.length - 1 ;i++){
			
boolean largerThanPrev = estimate[i] > estimate[i-1];
			
boolean largerThanNext = estimate[i] > estimate[i+1];
			
boolean largerThanThreshold = estimate[i] >= threshold;
			
if(largerThanThreshold && largerThanNext && largerThanPrev){
				
int cents = i - minimumCentsBelowHighest;
				
double ratio = 0;
				
if(cents != 0){
					
ratio = PitchConverter.centToRatio(cents);
				
}
				
System.out.println(String.format("%d %.2f %.2f",cents, estimate[i],ratio));
			
}
		
}
		
*/

	
}
	

	
public static void main(String... args) throws UnsupportedAudioFileException {
		

		
String fileName = args[0];
		

		
final int numberOfSpectralPeaks;
		
if(args.length == 2){
			
numberOfSpectralPeaks = Integer.parseInt(args[1]);
		
}else{
			
numberOfSpectralPeaks = 10;
		
}
		

		
SpectralPeakFollowerExample spfe = new SpectralPeakFollowerExample(fileName,numberOfSpectralPeaks);
		
spfe.extractPeakListList();
		
spfe.processPeakListList();
	
}
}