/*
*
      
_______
                       
_____
   
_____ _____
  

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

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

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

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

*
                                                         

* -------------------------------------------------------------
*
* 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.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class SensoryDissonanceCurve {
	

	
private final double range;
	
private final double increment = 0.005;
	

	
private final List<SensoryDissonanceResult> curve;
	

	
private final double dstar = 0.24;
	
private final double s1= 0.0207;
	
private final double s2=18.96;
	
private final double c1=5;
	
private final double c2=-5;
	
private final double a1 = -3.51;
	
private final double a2 = -5.75;
	

	
public SensoryDissonanceCurve(){
		
this(2.3);
	
}
	

	
/**
	 
* Calcultate the dissonance curve until this factor. 2.3 means
 

	 
* one octave and one sixth of an octave.
 

	 
* @param range
	 
*/

	
private SensoryDissonanceCurve(double range){
		
this.range = range;
		
this.curve = new ArrayList<SensoryDissonanceResult>();
	
}
	

	

	
public List<SensoryDissonanceResult> calculate(List<Double> frequencies,List<Double> amplitudes){
		
curve.clear();
		
curve.add(new SensoryDissonanceResult(1.0, 0.0, frequencies.get(0)));
		
for(double alpha = 1 + increment; alpha<=range; alpha += increment ){
			
List<Double> f = new ArrayList<Double>(frequencies);
			
List<Double> a = new ArrayList<Double>(amplitudes);
			
for(Double frequency:frequencies){
				
f.add(alpha*frequency);
			
}
			
for(Double amplitude:amplitudes){
				
a.add(amplitude);
			
}
			
double d = dissonanceMeasure(f, a);
			

			
curve.add(new SensoryDissonanceResult(alpha, d, frequencies.get(0)));
		
}
		
return curve;
	
}
	

	
public List<SensoryDissonanceResult> valleys(List<SensoryDissonanceResult> results){
		
List<SensoryDissonanceResult> valleys = new ArrayList<SensoryDissonanceResult>();
		
for(int i = 1 ; i < results.size()-1; i++){
			
SensoryDissonanceResult prevResult = results.get(i-1);
			
SensoryDissonanceResult currentResult = results.get(i);
			
SensoryDissonanceResult nextResult = results.get(i+1);
			
if(currentResult.dissonanceValue < nextResult.dissonanceValue && currentResult.dissonanceValue < prevResult.dissonanceValue){
				
double rightSlope =
  
(nextResult.dissonanceValue - currentResult.dissonanceValue)/increment;
				
double leftSlope =
  
(prevResult.dissonanceValue - currentResult.dissonanceValue)/increment;
				

				
if(rightSlope > 0.15 && leftSlope > 0.15){
					
valleys.add(currentResult);
				
}
			
}
		
}
		
return valleys;
	
}
	

	
public double dissonanceMeasure(final List<Double> frequencies , List<Double> amplitudes){
		

		
int n = frequencies.size();
		
double d = 0;
		

		
Collections.sort(frequencies);
		

		
//create a list with indexes
		
final Integer[] indexes = new Integer[frequencies.size()];
		
for(int i = 0;i<frequencies.size() ; i++){
			
indexes[i] = i;
		
}
		
//sort the indexes according to ascending frequency
		
Arrays.sort(indexes, new Comparator<Integer>() {
			
@Override
			
public int compare(final Integer o1, final Integer o2) {
				
return Double.compare(frequencies.get(o1), frequencies.get(o2));
			
}
		
});
		

		
List<Double> sortedFrequencies = new ArrayList<Double>(frequencies.size());
		
List<Double> sortedAmplitudes = new ArrayList<Double>(frequencies.size());
		

		
//create sorted frequency and amplitude lists
		
for(int i = 0;i<frequencies.size() ; i++){
			
sortedFrequencies.add(frequencies.get(indexes[i]));
			
sortedAmplitudes.add(amplitudes.get(indexes[i]));
		
}
		

		
for(int i = 2;i<n ; i++){
			
List<Double> fmin = new ArrayList<Double>(sortedFrequencies.subList(0, n - i + 1));
			
for(int j = 0 ; j < fmin.size() ; j++){
				
fmin.set(j, dstar / (s1*fmin.get(j)+s2));
			
}
			

			
List<Double> fdiff = new ArrayList<Double>();
			
for(int j = i-1 ; j < sortedFrequencies.size(); j++){
				
fdiff.add(sortedFrequencies.get(j) - sortedFrequencies.get(j-i+1));
			
}
			

			
List<Double> a = new ArrayList<Double>();
			
for(int j = i - 1 ; j < sortedFrequencies.size() ; j++){
				
a.add(sortedAmplitudes.get(j));
			
}
			

			
for(int j = 0 ; j < a.size() ; j++){
				
double element= c1 * Math.exp(a1*fmin.get(j)*fdiff.get(j));
				
double other = c2 * Math.exp(a2*fmin.get(j)*fdiff.get(j));
				
d += a.get(j)* (element + other);
			
}
			

		
}
		
return d;
	
}
	

	

	
public static void main(String...strings){
		

		
List<Double> frequencies = new ArrayList<Double>();
		
frequencies.add(440.0*1);
		
frequencies.add(440.0*2);
		
frequencies.add(440.0*3);
		
frequencies.add(440.0*4);
		
frequencies.add(440.0*5);
		
//frequencies.add(440.0*6);
		
//frequencies.add(440.0*7);
		
//frequencies.add(440.0*8);
		

		
List<Double> amplitudes = new ArrayList<Double>();
		
amplitudes.add(Math.pow(0.88, 0));
		
amplitudes.add(Math.pow(0.88, 1));
		
amplitudes.add(Math.pow(0.88, 2));
		
amplitudes.add(Math.pow(0.88, 3));
		
amplitudes.add(Math.pow(0.88, 4));
		
//amplitudes.add(Math.pow(0.88, 5));
		
//amplitudes.add(Math.pow(0.88, 6));
		
//amplitudes.add(Math.pow(0.88, 7));
		

		
SensoryDissonanceCurve sdc = new SensoryDissonanceCurve();
		

		
System.out.println("Expect 0.0049863 is " + sdc.dissonanceMeasure(frequencies,amplitudes));
		

		
sdc.calculate(frequencies, amplitudes);
	
}
    

}