PitchResyntheziser.java (4504B)
1 /* 2 * _______ _____ _____ _____ 3 * |__ __| | __ \ / ____| __ \ 4 * | | __ _ _ __ ___ ___ ___| | | | (___ | |__) | 5 * | |/ _` | '__/ __|/ _ \/ __| | | |\___ \| ___/ 6 * | | (_| | | \__ \ (_) \__ \ |__| |____) | | 7 * |_|\__,_|_| |___/\___/|___/_____/|_____/|_| 8 * 9 * ------------------------------------------------------------- 10 * 11 * TarsosDSP is developed by Joren Six at IPEM, University Ghent 12 * 13 * ------------------------------------------------------------- 14 * 15 * Info: http://0110.be/tag/TarsosDSP 16 * Github: https://github.com/JorenSix/TarsosDSP 17 * Releases: http://0110.be/releases/TarsosDSP/ 18 * 19 * TarsosDSP includes modified source code by various authors, 20 * for credits and info, see README. 21 * 22 */ 23 24 package be.tarsos.dsp.synthesis; 25 26 import java.util.Arrays; 27 28 import be.tarsos.dsp.AudioEvent; 29 import be.tarsos.dsp.EnvelopeFollower; 30 import be.tarsos.dsp.pitch.PitchDetectionHandler; 31 import be.tarsos.dsp.pitch.PitchDetectionResult; 32 33 /** 34 * This pitch detection handler replaces the audio buffer in the pipeline with a 35 * synthesized wave. It either follows the envelope of the original signal or 36 * not. Use it wisely. The following demonstrates how it can be used. 37 * 38 * <pre> 39 * <code> 40 * PitchEstimationAlgorithm algo = PitchEstimationAlgorithm.FFT_YIN; 41 * PitchResyntheziser prs = new PitchResyntheziser(samplerate); 42 * AudioDispatcher dispatcher = AudioDispatcher.fromFile(new File("in.wav"),1024, 0); 43 * //Handle pitch detection 44 * dispatcher.addAudioProcessor(new PitchProcessor(algo, samplerate, size, prs)); 45 * //Write the synthesized pitch to an output file. 46 * dispatcher.addAudioProcessor(new WaveformWriter(format, "out.wav"));// 47 * dispatcher.run(); 48 * </code> 49 * </pre> 50 * 51 * @author Joren Six 52 */ 53 public class PitchResyntheziser implements PitchDetectionHandler { 54 55 private double phase = 0; 56 private double phaseFirst = 0; 57 private double phaseSecond = 0; 58 private double prevFrequency = 0; 59 private float samplerate; 60 private final EnvelopeFollower envelopeFollower; 61 private boolean usePureSine; 62 private boolean followEnvelope; 63 private final double[] previousFrequencies; 64 private int previousFrequencyIndex; 65 66 public PitchResyntheziser(float samplerate){ 67 this(samplerate,true,false); 68 } 69 70 public PitchResyntheziser(float samplerate,boolean followEnvelope,boolean pureSine){ 71 this(samplerate,followEnvelope,pureSine,5); 72 } 73 74 public PitchResyntheziser(float samplerate,boolean followEnvelope,boolean pureSine,int filterSize){ 75 envelopeFollower = new EnvelopeFollower(samplerate,0.005,0.01); 76 this.followEnvelope=followEnvelope; 77 this.usePureSine = pureSine; 78 this.samplerate = samplerate; 79 previousFrequencies = new double[filterSize]; 80 previousFrequencyIndex = 0; 81 } 82 83 @Override 84 public void handlePitch(PitchDetectionResult pitchDetectionResult, 85 AudioEvent audioEvent) { 86 double frequency = pitchDetectionResult.getPitch(); 87 88 if(frequency==-1){ 89 frequency=prevFrequency; 90 }else{ 91 if(previousFrequencies.length!=0){ 92 //median filter 93 //store and adjust pointer 94 previousFrequencies[previousFrequencyIndex] = frequency; 95 previousFrequencyIndex++; 96 previousFrequencyIndex %= previousFrequencies.length; 97 //sort to get median frequency 98 double[] frequenciesCopy = previousFrequencies.clone(); 99 Arrays.sort(frequenciesCopy); 100 //use the median as frequency 101 frequency = frequenciesCopy[frequenciesCopy.length/2]; 102 } 103 104 prevFrequency = frequency; 105 } 106 107 108 109 final double twoPiF = 2 * Math.PI * frequency; 110 float[] audioBuffer = audioEvent.getFloatBuffer(); 111 float[] envelope = null; 112 if(followEnvelope){ 113 envelope = audioBuffer.clone(); 114 envelopeFollower.calculateEnvelope(envelope); 115 } 116 117 for (int sample = 0; sample < audioBuffer.length; sample++) { 118 double time = sample / samplerate; 119 double wave = Math.sin(twoPiF * time + phase); 120 if(!usePureSine){ 121 wave += 0.05 * Math.sin(twoPiF * 4 * time + phaseFirst); 122 wave += 0.01 * Math.sin(twoPiF * 8 * time + phaseSecond); 123 } 124 audioBuffer[sample] = (float) wave; 125 if(followEnvelope){ 126 audioBuffer[sample] = audioBuffer[sample] * envelope[sample]; 127 } 128 } 129 130 double timefactor = twoPiF * audioBuffer.length / samplerate; 131 phase = timefactor + phase; 132 if(!usePureSine){ 133 phaseFirst = 4 * timefactor + phaseFirst; 134 phaseSecond = 8 * timefactor + phaseSecond; 135 } 136 } 137 }