All Downloads are FREE. Search and download functionalities are using the official Maven repository.

be.tarsos.dsp.PitchShifter Maven / Gradle / Ivy

There is a newer version: 2.4-1
Show newest version
package be.tarsos.dsp;


import be.tarsos.dsp.util.fft.FFT;

/**
 * This is a translation of code by Stephan M. Bernsee. See the following explanation on this code:
 * Pitch shifting using the STFT.
 * 
 * @author Joren Six
 * @author Stephan M. Bernsee
 */
public class PitchShifter implements AudioProcessor{
	
	private final FFT fft;
	private final int size;
	private final float[] currentMagnitudes;
	private final float[] currentPhase;
	private final float[] currentFrequencies;
	private final float[] outputAccumulator;
	private final float[] summedPhase;
	
	private  float[] previousPhase;
	
	private double pitchShiftRatio = 0;

	private final double sampleRate;
	
	private long osamp;
	
	private double excpt;
	
	public PitchShifter(double factor, double sampleRate, int size, int overlap){

		
		pitchShiftRatio = factor;
		this.size = size;
		this.sampleRate = sampleRate;
		//this.d = d;
		
		osamp=size/(size-overlap);
		
		this.excpt = 2.*Math.PI*(double)(size-overlap)/(double)size;
		
		fft = new FFT(size);
		
		currentMagnitudes = new float[size/2];
		currentFrequencies = new float[size/2];
		currentPhase = new float[size/2];
		
		previousPhase = new float[size/2];
		summedPhase = new float[size/2];
		outputAccumulator = new float[size*2];
	}
	
	public void setPitchShiftFactor(float newPitchShiftFactor){
		this.pitchShiftRatio = newPitchShiftFactor;
	}

	@Override
	public boolean process(AudioEvent audioEvent) {
		//see http://downloads.dspdimension.com/smbPitchShift.cpp
			
		/* ***************** ANALYSIS ******************* */		
		float[] fftData = audioEvent.getFloatBuffer().clone();
		
		for(int i = 0 ; i= 0) 
				qpd += qpd&1;
			else 
				qpd -= qpd&1;
			tmp -= Math.PI*(double)qpd;

			/* get deviation from bin frequency from the +/- Pi interval */
			tmp = osamp*tmp/(2.*Math.PI);

			/* compute the k-th partials' true frequency */
			tmp = (double)i*freqPerBin + tmp*freqPerBin;			

			/* store magnitude and true frequency in analysis arrays */
			currentFrequencies[i] = (float) tmp;
		}
		
		/* ***************** PROCESSING ******************* */
		/* this does the actual pitch shifting */
		float[] newMagnitudes = new float[size/2];
		float[] newFrequencies = new float[size/2];
		
		for(int i = 0 ; i < size/2 ; i++){
			int index = (int)(i * pitchShiftRatio);
			if(index < size/2){
				newMagnitudes[index] += currentMagnitudes[i];
				newFrequencies[index] = (float) (currentFrequencies[i]*pitchShiftRatio);
			}
		}
		
		///Synthesis****
		float[] newFFTData = new float[size];
		
		for(int i =0 ; i < size/2 ; i++){
			
			float magn = newMagnitudes[i];
			double tmp = newFrequencies[i];

			/* subtract bin mid frequency */
			tmp -= (double)i*freqPerBin;

			/* get bin deviation from freq deviation */
			tmp /= freqPerBin;

			/* take osamp into account */
			tmp = 2.*Math.PI*tmp/osamp;

			/* add the overlap phase advance back in */
			tmp += (double)i*excpt;

			/* accumulate delta phase to get bin phase */
			summedPhase[i] += tmp;
			float phase = summedPhase[i];

			/* get real and imag part and re-interleave */
			newFFTData[2*i] = (float) (magn * Math.cos(phase));
			newFFTData[2*i+1] = (float) (magn* Math.sin(phase));
		}
		
		/* zero negative frequencies */
		for (int i = size/2+2;  i < size; i++){ 
			newFFTData[i] = 0.f;
		}
		
		fft.backwardsTransform(newFFTData);
		for(int i = 0 ; i < newFFTData.length ; i ++){
			float window = (float) (-.5*Math.cos(2.*Math.PI*(double)i/(double)size)+.5);
			//outputAccumulator[i] += 2000*window*newFFTData[i]/(float) (size*osamp);
			outputAccumulator[i] += window*newFFTData[i]/(float) osamp;
			if(outputAccumulator[i] > 1.0 ||  outputAccumulator[i] < -1.0 ){
				System.err.println("Clipping!");
			}
		}
		
		int stepSize = (int) (size/osamp);
		
		
		
		//Arrays.fill(audioBuffer, 0);
		System.arraycopy(outputAccumulator, stepSize, outputAccumulator, 0, size);
		
		float[] audioBuffer = new float[audioEvent.getFloatBuffer().length];
		audioEvent.setFloatBuffer(audioBuffer);
		System.arraycopy(outputAccumulator, 0, audioBuffer,size-stepSize, stepSize);
		
		return true;
	}

	@Override
	public void processingFinished() {
		
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy