public void testStretch2() { double[] signal = FFTTest.getSampleSignal(16000); int samplingRate = 8000; double rateFactor = 0.5; NaiveVocoder nv = new NaiveVocoder(new BufferedDoubleDataSource(signal), samplingRate, rateFactor); double[] result = nv.getAllData(); double meanSignalEnergy = MathUtils.mean(MathUtils.multiply(signal, signal)); double meanResultEnergy = MathUtils.mean(MathUtils.multiply(result, result)); double percentDifference = Math.abs(meanSignalEnergy - meanResultEnergy) / meanSignalEnergy * 100; assertTrue( "Stretching changed signal energy by " + percentDifference + "%", percentDifference < 6); }
// st: Sinusoidal tracks // absMaxDesired: Desired absolute maximum of the output public double[] synthesize(SinusoidalTracks st, boolean isSilentSynthesis) { int n; // discrete time index int i, j; int nStart, nEnd, pStart, pEnd; float t; // continuous time float t2; // continuous time squared float t3; // continuous time cubed float tFinal = st.getOriginalDuration(); int nFinal = (int) (Math.floor(tFinal * st.fs + 0.5)); double[] y = new double[nFinal + 1]; Arrays.fill(y, 0.0); float currentAmp; float currentTheta; double alpha, beta; int M; float T; // Number of samples between consecutive frames (equals to pitch period in pitch // synchronous analysis/synthesis) float T2; // T squared float T3; // T cubed double oneOverTwoPi = 1.0 / MathUtils.TWOPI; double term1, term2; float currentTime; // For debugging purposes for (i = 0; i < st.totalTracks; i++) { for (j = 0; j < st.tracks[i].totalSins - 1; j++) { if (st.tracks[i].states[j] != SinusoidalTrack.TURNED_OFF) { pStart = (int) Math.floor(st.tracks[i].times[j] * st.fs + 0.5); pEnd = (int) Math.floor(st.tracks[i].times[j + 1] * st.fs + 0.5); nStart = Math.max(0, pStart); nEnd = Math.max(0, pEnd); nStart = Math.min(y.length - 1, nStart); nEnd = Math.min(y.length - 1, nEnd); // currentTime = 0.5f*(nEnd+nStart)/st.fs; // System.out.println("currentTime=" + String.valueOf(currentTime)); for (n = nStart; n < nEnd; n++) { if (false) // Direct synthesis { currentAmp = st.tracks[i].amps[j]; currentTheta = (n - nStart) * st.tracks[i].freqs[j] + st.tracks[i].phases[j]; y[n] += currentAmp * Math.cos(currentTheta); } else // Synthesis with interpolation { // Amplitude interpolation currentAmp = st.tracks[i].amps[j] + (st.tracks[i].amps[j + 1] - st.tracks[i].amps[j]) * ((float) n - pStart) / (pEnd - pStart + 1); T = (pEnd - pStart); if (n == nStart && st.tracks[i].states[j] == SinusoidalTrack.TURNED_ON) // Turning on a track { // Quatieri currentTheta = st.tracks[i].phases[j + 1] - T * st.tracks[i].freqs[j + 1]; currentAmp = 0.0f; } else if (n == nStart && st.tracks[i].states[j] == SinusoidalTrack.TURNED_OFF && j > 0) // Turning off a track { // Quatieri currentTheta = st.tracks[i].phases[j - 1] + T * st.tracks[i].freqs[j - 1]; currentAmp = 0.0f; } else // Cubic phase interpolation { // Quatieri M = (int) (Math.floor( oneOverTwoPi * ((st.tracks[i].phases[j] + T * st.tracks[i].freqs[j] - st.tracks[i].phases[j + 1]) + (st.tracks[i].freqs[j + 1] - st.tracks[i].freqs[j]) * 0.5 * T) + 0.5)); term1 = st.tracks[i].phases[j + 1] - st.tracks[i].phases[j] - T * st.tracks[i].freqs[j] + M * MathUtils.TWOPI; term2 = st.tracks[i].freqs[j + 1] - st.tracks[i].freqs[j]; T2 = T * T; T3 = T * T2; alpha = 3.0 * term1 / T2 - term2 / T; beta = -2 * term1 / T3 + term2 / T2; t = ((float) n - nStart); t2 = t * t; t3 = t * t2; // Quatieri currentTheta = (float) (st.tracks[i].phases[j] + st.tracks[i].freqs[j] * t + alpha * t2 + beta * t3); } // Synthesis y[n] += currentAmp * Math.cos(currentTheta); } // System.out.println(String.valueOf(currentTheta)); } } } if (!isSilentSynthesis) System.out.println( "Synthesized track " + String.valueOf(i + 1) + " of " + String.valueOf(st.totalTracks)); } y = MathUtils.multiply(y, st.absMaxOriginal / MathUtils.getAbsMax(y)); return y; }