/** * Copyright 2004-2006 DFKI GmbH. * All Rights Reserved. Use is subject to license terms. * * This file is part of MARY TTS. * * MARY TTS is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ package marytts.signalproc.analysis; import java.util.Arrays; import marytts.util.math.ComplexArray; import marytts.util.math.FFT; import marytts.util.math.FFTMixedRadix; import marytts.util.math.Hilbert; import marytts.util.math.MathUtils; import marytts.util.signal.SignalProcUtils; /** * * @author Marc Schröder, oytun.turk * * Computes real and complex cepstrum directly from speech spectrum Note that this is different than LP cepstrum which is * implemented in CepstrumLPCAnalyser * */ public class CepstrumSpeechAnalyser { public static double[] realCepstrum(double[] frame) { return realCepstrum(frame, frame.length); } public static double[] realCepstrum(double[] frame, int fftSize) { return complexCepstrum(frame, fftSize).real; } public static ComplexArray complexCepstrum(double[] frame, int fftSize) { double[] real = new double[fftSize]; double[] imag = new double[fftSize]; Arrays.fill(real, 0.0); Arrays.fill(imag, 0.0); System.arraycopy(frame, 0, real, 0, Math.min(frame.length, fftSize)); if (MathUtils.isPowerOfTwo(fftSize)) FFT.transform(real, imag, false); else FFTMixedRadix.fftComplex(real, imag); // Now real + j*imag is the complex spectrum MathUtils.toPolarCoordinates(real, imag); // now real = abs(X), imag = phi real = MathUtils.log(real); // Now real + j*imag is the complex cepstrum if (MathUtils.isPowerOfTwo(fftSize)) { FFT.transform(real, imag, true); return new ComplexArray(real, imag); } else return FFTMixedRadix.ifft(real, imag); } public static double[] filterLowPass(double[] realCepstrum, int cutoffIndex) { double[] filtered = new double[realCepstrum.length]; filtered[0] = realCepstrum[0]; filtered[cutoffIndex] = realCepstrum[cutoffIndex]; for (int i = 1; i < cutoffIndex; i++) { filtered[i] = 2 * realCepstrum[i]; } return filtered; } // Estimates the phase response (in Radians) of the vocal tract transfer function // using the minimum phase assumption using real cepstrum based spectral smoothing public static double[] systemPhaseResponse(double[] x, int fftSize, int lifterOrder) { double[] systemAmpsInNeper = cepstralSmoothedSpectrumInNeper(x, fftSize, lifterOrder); return minimumPhaseResponseInRadians(systemAmpsInNeper); } // Estimates the phase response (in Radians) of the vocal tract transfer function // using the minimum phase assumption using real cepstrum based spectral smoothing public static double[] systemPhaseResponse(double[] x, int fs) { double[] systemAmpsInNeper = cepstralSmoothedSpectrumInNeper(x, fs); return minimumPhaseResponseInRadians(systemAmpsInNeper); } // Returns the phase response(in radians) of a minimum phase system given the system amplitudes in dB public static double[] minimumPhaseResponseInRadians(double[] systemAmpsInNeper) { ComplexArray phaseResponse = minimumPhaseResponse(systemAmpsInNeper); // Perform in-place conversion from complex values to radians for (int w = 0; w < phaseResponse.real.length; w++) phaseResponse.real[w] = Math.atan2(phaseResponse.imag[w], phaseResponse.real[w]); return phaseResponse.real; } // Returns the phase response of a minimum phase system given the system amplitudes in dB public static ComplexArray minimumPhaseResponse(double[] systemAmpsInNeper) { int w; ComplexArray phaseResponse = Hilbert.transform(systemAmpsInNeper); for (w = 0; w < phaseResponse.real.length; w++) { phaseResponse.real[w] *= -1.0; phaseResponse.imag[w] *= -1.0; } return phaseResponse; } // Returns the cepstral smoothed amplitude spectrum in dB public static double[] cepstralSmoothedSpectrumInNeper(double[] x, int fs) { int lifterOrder = SignalProcUtils.getLifterOrder(fs); int fftSize = SignalProcUtils.getDFTSize(fs); return cepstralSmoothedSpectrumInNeper(x, fftSize, lifterOrder); } public static double[] cepstralSmoothedSpectrumInNeper(double[] x, int fftSize, int lifterOrder) { double[] rceps = realCepstrum(x, fftSize); double[] w = new double[rceps.length]; int i; for (i = 0; i < lifterOrder; i++) w[i] = 1.0; for (i = lifterOrder; i < w.length; i++) w[i] = 0.0; for (i = 0; i < w.length; i++) rceps[i] *= w[i]; // Inverse cepstrum step ComplexArray y = FFTMixedRadix.fftReal(rceps, rceps.length); return y.real; // } }