/*
*
      
_______
                       
_____
   
_____ _____
  

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

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

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

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

*
                                                         

* -------------------------------------------------------------
*
* 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.
*
 

*/

/*
 
*
      
_______
                       
_____
   
_____ _____
  

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

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

 
*
                                                         

 
* -----------------------------------------------------------
 
*
 
*
  
TarsosDSP is developed by Joren Six at
 
*
  
The School of Arts,
 
*
  
University College Ghent,
 
*
  
Hoogpoort 64, 9000 Ghent - Belgium
 
*
  

 
* -----------------------------------------------------------
 
*
 
*
  
Info: http://tarsos.0110.be/tag/TarsosDSP
 
*
  
Github: https://github.com/JorenSix/TarsosDSP
 
*
  
Releases: http://tarsos.0110.be/releases/TarsosDSP/
 
*
  

 
*
  
TarsosDSP includes modified source code by various authors,
 
*
  
for credits and info, see README.
 
*
 
*/


package be.tarsos.dsp.effects;

import be.tarsos.dsp.AudioEvent;
import be.tarsos.dsp.AudioProcessor;

/**
 
* <p>
 
* Adds a flanger effect to a signal. The implementation is done with a delay
 
* buffer and an LFO in the form of a sine wave. It is probably the most
 
* straightforward flanger implementation possible.
 
* </p>
 
*
 
* @author Joren Six
 
*/

public class FlangerEffect implements AudioProcessor {

	
/**
	 
* A simple delay buffer, it holds a number of samples determined by the
	 
* maxFlangerLength and the sample rate.
	 
*/

	
private float[] flangerBuffer;

	
/**
	 
* The position in the delay buffer to store the current sample.
	 
*/

	
private int writePosition;

	
/**
	 
* Determines the factor of original signal that remains in the final mix.
	 
* Dry should always equal 1-wet).
	 
*/

	
private float dry;
	
/**
	 
* Determines the factor of flanged signal that is mixed in the final mix.
	 
* Wet should always equal 1-dry.
	 
*/

	
private float wet;
	
/**
	 
* The frequency for the LFO (sine).
	 
*/
	
private double lfoFrequency;

	
/**
	 
* The sample rate is neede to calculate the length of the delay buffer.
	 
*/

	
private double sampleRate;

	
/**
	 
* @param maxFlangerLength
	 
*
            
in seconds
	 
* @param wet
	 
*
            
The 'wetness' of the flanging effect. A value between 0 and 1.
	 
*
            
Zero meaning no flanging effect in the resulting signal, one
	 
*
            
means total flanging effect and no original signal left. The
	 
*
            
dryness of the signal is determined by dry = "1-wet".
	 
* @param sampleRate
	 
*
            
the sample rate in Hz.
	 
* @param lfoFrequency
	 
*
            
in Hertz
	 
*/

	
public FlangerEffect(double maxFlangerLength, double wet,
			
double sampleRate, double lfoFrequency) {
		
flangerBuffer = new float[(int) (sampleRate * maxFlangerLength)];
		
this.sampleRate = sampleRate;
		
this.lfoFrequency = lfoFrequency;
		
this.wet = (float) wet;
		
this.dry = (float) (1 - wet);
	
}

	
@Override
	
public boolean process(AudioEvent audioEvent) {
		
float[] audioFloatBuffer = audioEvent.getFloatBuffer();
		
int overlap = audioEvent.getOverlap();

		
// Divide f by two, to counter rectifier below, which effectively
		
// doubles the frequency
		
double twoPIf = 2 * Math.PI * lfoFrequency / 2.0;
		
double time = audioEvent.getTimeStamp();
		
double timeStep = 1.0 / sampleRate;

		
for (int i = overlap; i < audioFloatBuffer.length; i++) {

			
// Calculate the LFO delay value with a sine wave:
			
//fix by hans bickel
			
double lfoValue = (flangerBuffer.length - 1) * Math.sin(twoPIf * time);
			
// add a time step, each iteration
			
time += timeStep;

			
// Make the delay a positive integer
			
int delay = (int) (Math.round(Math.abs(lfoValue)));
			

			
// store the current sample in the delay buffer;
			
if (writePosition >= flangerBuffer.length) {
				
writePosition = 0;
			
}
			
flangerBuffer[writePosition] = audioFloatBuffer[i];

			
// find out the position to read the delayed sample:
			
int readPosition = writePosition - delay;
			
if (readPosition < 0) {
				
readPosition += flangerBuffer.length;
			
}

			
//increment the write position
			
writePosition++;

			
// Output is the input summed with the value at the delayed flanger
			
// buffer
			
audioFloatBuffer[i] = dry * audioFloatBuffer[i] + wet * flangerBuffer[readPosition];
		
}
		
return true;
	
}

	
@Override
	
public void processingFinished() {
	
}

	
/**
	 
* Set the new length of the delay LineWavelet.
	 
*
 

	 
* @param flangerLength
	 
*
            
The new length of the delay LineWavelet, in seconds.
	 
*/

	
public void setFlangerLength(double flangerLength) {
		
flangerBuffer = new float[(int) (sampleRate * flangerLength)];
	
}

	
/**
	 
* Sets the frequency of the LFO (sine wave), in Hertz.
	 
*
 

	 
* @param lfoFrequency
	 
*
            
The new LFO frequency in Hertz.
	 
*/

	
public void setLFOFrequency(double lfoFrequency) {
		
this.lfoFrequency = lfoFrequency;
	
}

	
/**
	 
* Sets the wetness and dryness of the effect. Should be a value between
	 
* zero and one (inclusive), the dryness is determined by 1-wet.
	 
*
 

	 
* @param wet
	 
*
            
A value between zero and one (inclusive) that determines the
	 
*
            
wet and dryness of the resulting mix.
	 
*/

	
public void setWet(double wet) {
		
this.wet = (float) wet;
		
this.dry = (float) (1 - wet);
	
}

	
/**
	 
* Sets the wetness and wetness of the effect. Should be a value between
	 
* zero and one (inclusive), the wetness is determined by 1-dry.
	 
*
 

	 
* @param dry
	 
*
            
A value between zero and one (inclusive) that determines the
	 
*
            
wet and dryness of the resulting mix.
	 
*/

	
public void setDry(double dry) {
		
this.dry = (float) dry;
		
this.wet = (float) (1 - dry);
	
}
}