PitchShifter.java (5009B)
1 package be.tarsos.dsp; 2 3 4 import be.tarsos.dsp.util.fft.FFT; 5 6 /** 7 * This is a translation of code by Stephan M. Bernsee. See the following explanation on this code: 8 * <a href="http://www.dspdimension.com/admin/pitch-shifting-using-the-ft/">Pitch shifting using the STFT</a>. 9 * 10 * @author Joren Six 11 * @author Stephan M. Bernsee 12 */ 13 public class PitchShifter implements AudioProcessor{ 14 15 private final FFT fft; 16 private final int size; 17 private final float[] currentMagnitudes; 18 private final float[] currentPhase; 19 private final float[] currentFrequencies; 20 private final float[] outputAccumulator; 21 private final float[] summedPhase; 22 23 private float[] previousPhase; 24 25 private double pitchShiftRatio = 0; 26 27 private final double sampleRate; 28 29 private long osamp; 30 31 private double excpt; 32 33 public PitchShifter(double factor, double sampleRate, int size, int overlap){ 34 35 36 pitchShiftRatio = factor; 37 this.size = size; 38 this.sampleRate = sampleRate; 39 //this.d = d; 40 41 osamp=size/(size-overlap); 42 43 this.excpt = 2.*Math.PI*(double)(size-overlap)/(double)size; 44 45 fft = new FFT(size); 46 47 currentMagnitudes = new float[size/2]; 48 currentFrequencies = new float[size/2]; 49 currentPhase = new float[size/2]; 50 51 previousPhase = new float[size/2]; 52 summedPhase = new float[size/2]; 53 outputAccumulator = new float[size*2]; 54 } 55 56 public void setPitchShiftFactor(float newPitchShiftFactor){ 57 this.pitchShiftRatio = newPitchShiftFactor; 58 } 59 60 @Override 61 public boolean process(AudioEvent audioEvent) { 62 //see http://downloads.dspdimension.com/smbPitchShift.cpp 63 64 /* ***************** ANALYSIS ******************* */ 65 float[] fftData = audioEvent.getFloatBuffer().clone(); 66 67 for(int i = 0 ; i<size ; i++){ 68 float window = (float) (-.5*Math.cos(2.*Math.PI*(double)i/(double)size)+.5); 69 fftData[i] = window * fftData[i]; 70 } 71 //Fourier transform the audio 72 fft.forwardTransform(fftData); 73 //Calculate the magnitudes and phase information. 74 fft.powerAndPhaseFromFFT(fftData, currentMagnitudes, currentPhase); 75 76 float freqPerBin = (float) (sampleRate/(float)size); // distance in Hz between FFT bins 77 78 for(int i = 0 ; i < size/2 ; i++){ 79 80 float phase = currentPhase[i]; 81 82 /* compute phase difference */ 83 double tmp = phase - previousPhase[i]; 84 previousPhase[i] = phase; 85 86 /* subtract expected phase difference */ 87 tmp -= (double)i*excpt; 88 89 /* map delta phase into +/- Pi interval */ 90 long qpd = (long) (tmp/Math.PI); 91 if (qpd >= 0) 92 qpd += qpd&1; 93 else 94 qpd -= qpd&1; 95 tmp -= Math.PI*(double)qpd; 96 97 /* get deviation from bin frequency from the +/- Pi interval */ 98 tmp = osamp*tmp/(2.*Math.PI); 99 100 /* compute the k-th partials' true frequency */ 101 tmp = (double)i*freqPerBin + tmp*freqPerBin; 102 103 /* store magnitude and true frequency in analysis arrays */ 104 currentFrequencies[i] = (float) tmp; 105 } 106 107 /* ***************** PROCESSING ******************* */ 108 /* this does the actual pitch shifting */ 109 float[] newMagnitudes = new float[size/2]; 110 float[] newFrequencies = new float[size/2]; 111 112 for(int i = 0 ; i < size/2 ; i++){ 113 int index = (int)(i * pitchShiftRatio); 114 if(index < size/2){ 115 newMagnitudes[index] += currentMagnitudes[i]; 116 newFrequencies[index] = (float) (currentFrequencies[i]*pitchShiftRatio); 117 } 118 } 119 120 ///Synthesis**** 121 float[] newFFTData = new float[size]; 122 123 for(int i =0 ; i < size/2 ; i++){ 124 125 float magn = newMagnitudes[i]; 126 double tmp = newFrequencies[i]; 127 128 /* subtract bin mid frequency */ 129 tmp -= (double)i*freqPerBin; 130 131 /* get bin deviation from freq deviation */ 132 tmp /= freqPerBin; 133 134 /* take osamp into account */ 135 tmp = 2.*Math.PI*tmp/osamp; 136 137 /* add the overlap phase advance back in */ 138 tmp += (double)i*excpt; 139 140 /* accumulate delta phase to get bin phase */ 141 summedPhase[i] += tmp; 142 float phase = summedPhase[i]; 143 144 /* get real and imag part and re-interleave */ 145 newFFTData[2*i] = (float) (magn * Math.cos(phase)); 146 newFFTData[2*i+1] = (float) (magn* Math.sin(phase)); 147 } 148 149 /* zero negative frequencies */ 150 for (int i = size/2+2; i < size; i++){ 151 newFFTData[i] = 0.f; 152 } 153 154 fft.backwardsTransform(newFFTData); 155 for(int i = 0 ; i < newFFTData.length ; i ++){ 156 float window = (float) (-.5*Math.cos(2.*Math.PI*(double)i/(double)size)+.5); 157 //outputAccumulator[i] += 2000*window*newFFTData[i]/(float) (size*osamp); 158 outputAccumulator[i] += window*newFFTData[i]/(float) osamp; 159 if(outputAccumulator[i] > 1.0 || outputAccumulator[i] < -1.0 ){ 160 System.err.println("Clipping!"); 161 } 162 } 163 164 int stepSize = (int) (size/osamp); 165 166 167 168 //Arrays.fill(audioBuffer, 0); 169 System.arraycopy(outputAccumulator, stepSize, outputAccumulator, 0, size); 170 171 float[] audioBuffer = new float[audioEvent.getFloatBuffer().length]; 172 audioEvent.setFloatBuffer(audioBuffer); 173 System.arraycopy(outputAccumulator, 0, audioBuffer,size-stepSize, stepSize); 174 175 return true; 176 } 177 178 @Override 179 public void processingFinished() { 180 181 } 182 }