/**
* Copyright 2007 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.sinusoidal;
import marytts.util.math.MathUtils;
import marytts.util.signal.SignalProcUtils;
/**
* A sinusoidal track is a collection of matched (amplitude,frequency,phase) triplets which represents a "relatively" stationary
* time-frequency partition of a signal
*
* @author Oytun Türk
*/
public class SinusoidalTrack {
public float[] amps; // Amplitudes of the sinusoids
public float[] freqs; // Frequencies of the sinusoids
public float[] phases; // Phases of the sinusoids in radians
public int[] frameIndices; // Frame indices of sinusoids
public float[] times; // Times of the sinusoids in seconds
public float[] maxFreqOfVoicings; // Voicing probabilities of track components
public int[] states; // State of the track for a given index (one of the flags below, by default: ACTIVE
public static int ACTIVE = 0; // The track has been turned on in a previous frame and has not been turned off until now
public static int TURNED_ON = 1; // The track is turned on at the current time instant
public static int TURNED_OFF = 2; // The track is turned off until next turning on event
public int currentIndex;
public int totalSins;
// These two parameters are used for keeping temporary information
// on new sinusoid candidates to be appended to the current track during track generation
public Sinusoid newCandidate;
public int newCandidateInd;
//
// These are for checking some statistics and debugging only, not required for actual analysis/synthesis
public float avgFreqInHz;
public float minFreqInHz;
public float maxFreqInHz;
public float avgAmpLinear;
public float minAmpLinear;
public float maxAmpLinear;
public float avgPhaseInDegrees;
public float minPhaseInDegrees;
public float maxPhaseInDegrees;
public float minTimeInSeconds;
public float maxTimeInSeconds;
public void getStatistics(boolean isFreqRadian, boolean isPhaseRadian, int fs, int trackIndex) {
avgFreqInHz = 0.0f;
minFreqInHz = -1.0f;
maxFreqInHz = -1.0f;
avgAmpLinear = 0.0f;
minAmpLinear = -1.0f;
maxAmpLinear = -1.0f;
avgPhaseInDegrees = 0.0f;
minPhaseInDegrees = -1.0f;
maxPhaseInDegrees = -1.0f;
minTimeInSeconds = -1.0f;
maxTimeInSeconds = -1.0f;
if (totalSins > 0) {
int i;
for (i = 0; i < totalSins; i++) {
avgFreqInHz += freqs[i];
avgAmpLinear += amps[i];
avgPhaseInDegrees += phases[i];
}
avgFreqInHz /= totalSins;
avgAmpLinear /= totalSins;
avgPhaseInDegrees /= totalSins;
if (isFreqRadian)
avgFreqInHz = SignalProcUtils.radian2hz(avgFreqInHz, fs);
if (isPhaseRadian)
avgPhaseInDegrees = MathUtils.radian2degrees(avgPhaseInDegrees);
minFreqInHz = MathUtils.getMin(freqs);
if (isFreqRadian)
minFreqInHz = SignalProcUtils.radian2hz(minFreqInHz, fs);
maxFreqInHz = MathUtils.getMax(freqs);
if (isFreqRadian)
maxFreqInHz = SignalProcUtils.radian2hz(maxFreqInHz, fs);
minAmpLinear = MathUtils.getMin(amps);
maxAmpLinear = MathUtils.getMax(amps);
minPhaseInDegrees = MathUtils.getMin(phases);
if (isPhaseRadian)
minPhaseInDegrees = MathUtils.radian2degrees(minPhaseInDegrees);
maxPhaseInDegrees = MathUtils.getMax(phases);
if (isPhaseRadian)
maxPhaseInDegrees = MathUtils.radian2degrees(maxPhaseInDegrees);
minTimeInSeconds = MathUtils.getMin(times);
maxTimeInSeconds = MathUtils.getMax(times);
char chTab = 9;
String str = "ind=" + String.valueOf(trackIndex) + " avgFreq=" + String.valueOf(avgFreqInHz) + " Hz." + chTab;
str += "minFreq=" + String.valueOf(minFreqInHz) + " Hz." + chTab;
str += "maxFreq=" + String.valueOf(maxFreqInHz) + " Hz." + chTab;
str += "avgAmpl=" + String.valueOf(avgAmpLinear) + chTab;
str += "minAmpl=" + String.valueOf(minAmpLinear) + chTab;
str += "maxAmpl=" + String.valueOf(maxAmpLinear) + chTab;
str += "avgPhas=" + String.valueOf(avgPhaseInDegrees) + "°" + chTab;
str += "minPhas=" + String.valueOf(minPhaseInDegrees) + "°" + chTab;
str += "maxPhas=" + String.valueOf(maxPhaseInDegrees) + "°" + chTab;
str += "minTime=" + String.valueOf(minTimeInSeconds) + "s." + chTab;
str += "maxTime=" + String.valueOf(maxTimeInSeconds) + "s.";
System.out.println(str);
}
}
//
public SinusoidalTrack(int len) {
initialize(len);
}
// Create a track from a single sinusoid
public SinusoidalTrack(float time, Sinusoid sin, float maxFreqOfVoicing, int state) {
add(time, sin.amp, sin.freq, sin.phase, sin.frameIndex, maxFreqOfVoicing, state);
}
// Create a track from a track
public SinusoidalTrack(SinusoidalTrack trk) {
initialize(trk.totalSins);
copy(trk);
}
public void initialize(int len) {
if (len > 0) {
totalSins = len;
times = new float[totalSins];
amps = new float[totalSins];
freqs = new float[totalSins];
phases = new float[totalSins];
frameIndices = new int[totalSins];
maxFreqOfVoicings = new float[totalSins];
states = new int[totalSins];
} else {
totalSins = 0;
times = null;
amps = null;
freqs = null;
phases = null;
frameIndices = null;
maxFreqOfVoicings = null;
states = null;
}
currentIndex = -1;
newCandidate = null;
newCandidateInd = -1;
}
// Copy part of the existing track parameters in srcTrack into the current track
// starting from startSinIndex and ending at endSinIndex
// including startSinIndex and endSinIndex
public void copy(SinusoidalTrack srcTrack, int startSinIndex, int endSinIndex) {
if (startSinIndex < 0)
startSinIndex = 0;
if (endSinIndex < 0)
endSinIndex = 0;
if (endSinIndex > srcTrack.totalSins - 1)
endSinIndex = srcTrack.totalSins - 1;
if (startSinIndex > endSinIndex)
startSinIndex = endSinIndex;
if (totalSins < endSinIndex - startSinIndex + 1)
initialize(endSinIndex - startSinIndex + 1);
if (totalSins > 0) {
System.arraycopy(srcTrack.times, startSinIndex, this.times, 0, endSinIndex - startSinIndex + 1);
System.arraycopy(srcTrack.amps, startSinIndex, this.amps, 0, endSinIndex - startSinIndex + 1);
System.arraycopy(srcTrack.freqs, startSinIndex, this.freqs, 0, endSinIndex - startSinIndex + 1);
System.arraycopy(srcTrack.phases, startSinIndex, this.phases, 0, endSinIndex - startSinIndex + 1);
System.arraycopy(srcTrack.frameIndices, startSinIndex, this.frameIndices, 0, endSinIndex - startSinIndex + 1);
System.arraycopy(srcTrack.maxFreqOfVoicings, startSinIndex, this.maxFreqOfVoicings, 0, endSinIndex - startSinIndex
+ 1);
System.arraycopy(srcTrack.states, startSinIndex, this.states, 0, endSinIndex - startSinIndex + 1);
currentIndex = endSinIndex - startSinIndex;
}
}
// Copy an existing track (srcTrack) into the current track
public void copy(SinusoidalTrack srcTrack) {
copy(srcTrack, 0, srcTrack.totalSins - 1);
}
public void add(float time, Sinusoid newSin, float pVoicing, int state) {
add(time, newSin.amp, newSin.freq, newSin.phase, newSin.frameIndex, pVoicing, state);
}
// Add a new sinusoid to the track
public void add(float time, float amp, float freq, float phase, int frameIndex, float maxFreqOfVoicing, int state) {
if (currentIndex + 1 >= totalSins) // Expand the current track to twice its length and then add
{
SinusoidalTrack tmpTrack = new SinusoidalTrack(totalSins);
if (totalSins > 0) {
tmpTrack.copy(this);
initialize(tmpTrack.totalSins + 1);
this.copy(tmpTrack);
} else
initialize(1);
}
currentIndex++;
times[currentIndex] = time;
amps[currentIndex] = amp;
freqs[currentIndex] = freq;
phases[currentIndex] = phase;
frameIndices[currentIndex] = frameIndex;
maxFreqOfVoicings[currentIndex] = maxFreqOfVoicing;
states[currentIndex] = state;
}
// Update parameters of <index>th sinusoid in track
public void update(int index, int time, float amp, float freq, float phase, int frameIndex, float sysAmp,
float maxFreqOfVoicing, int state) {
if (index < totalSins) {
times[index] = time;
amps[index] = amp;
freqs[index] = freq;
phases[index] = phase;
frameIndices[index] = frameIndex;
maxFreqOfVoicings[index] = maxFreqOfVoicing;
states[index] = state;
}
}
public void resetCandidate() {
newCandidate = null;
newCandidateInd = -1;
}
// Check turning on instants and if it is misplaced, correct its location
public void correctTrack() {
for (int i = 0; i < totalSins; i++) {
if (states[i] == TURNED_OFF) {
if (i > 0 && (times[i] - times[i - 1]) > 0.040f)
times[i] = times[i - 1] + TrackGenerator.ZERO_AMP_SHIFT_IN_SECONDS;
}
if (states[i] == TURNED_ON) {
if (i < totalSins - 1 && (times[i + 1] - times[i]) > 0.040f)
times[i] = times[i + 1] - TrackGenerator.ZERO_AMP_SHIFT_IN_SECONDS;
}
}
}
}