1 /* FormantModeler.cpp
2  *
3  * Copyright (C) 2014-2021 David Weenink
4  *
5  * This code is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or (at
8  * your option) any later version.
9  *
10  * This code is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * ainteger with this work. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "DataModeler.h"
20 #include "FormantModeler.h"
21 #include "Formant_extensions.h"
22 #include "NUM2.h"
23 #include "NUMmachar.h"
24 #include "SVD.h"
25 #include "Strings_extensions.h"
26 #include "Sound_and_LPC_robust.h"
27 #include "Table_extensions.h"
28 
29 #include "oo_DESTROY.h"
30 #include "FormantModeler_def.h"
31 #include "oo_COPY.h"
32 #include "FormantModeler_def.h"
33 #include "oo_EQUAL.h"
34 #include "FormantModeler_def.h"
35 #include "oo_CAN_WRITE_AS_ENCODING.h"
36 #include "FormantModeler_def.h"
37 #include "oo_WRITE_TEXT.h"
38 #include "FormantModeler_def.h"
39 #include "oo_WRITE_BINARY.h"
40 #include "FormantModeler_def.h"
41 #include "oo_READ_TEXT.h"
42 #include "FormantModeler_def.h"
43 #include "oo_READ_BINARY.h"
44 #include "FormantModeler_def.h"
45 #include "oo_DESCRIPTION.h"
46 #include "FormantModeler_def.h"
47 
48 #include "enums_getText.h"
49 #include "FormantModeler_enums.h"
50 #include "enums_getValue.h"
51 #include "FormantModeler_enums.h"
52 
53 Thing_implement (FormantModeler, Function, 0);
54 
v_info()55 void structFormantModeler :: v_info () {
56 	MelderInfo_writeLine (U"Time domain:");
57 	MelderInfo_writeLine (U"   Start time: ", xmin, U" seconds");
58 	MelderInfo_writeLine (U"   End time: ", xmax, U" seconds");
59 	MelderInfo_writeLine (U"   Total duration: ", xmax - xmin, U" seconds");
60 	for (integer iformant = 1; iformant <= trackmodelers.size; iformant ++) {
61 		DataModeler ffi = trackmodelers.at [iformant];
62 		MelderInfo_writeLine (U"Formant ", iformant);
63 		ffi -> v_info();
64 	}
65 }
66 
checkTrackAutoRange(FormantModeler me,integer * fromTrack,integer * toTrack)67 static void checkTrackAutoRange (FormantModeler me, integer *fromTrack, integer *toTrack) {
68 	if (*fromTrack == 0 && *toTrack == 0) { // auto
69 		*fromTrack = 1;
70 		*toTrack = my trackmodelers.size;
71 		return;
72 	}
73 	if (*toTrack == 0)
74 		*toTrack = my trackmodelers.size; // auto
75 	Melder_require (*fromTrack <= *toTrack,
76 		U"\"FromTrack\" should not exceed \"toTrack\".");
77 	if (*toTrack > my trackmodelers.size) // questionable because 0 is an alternative
78 		*toTrack = my trackmodelers.size;
79 	Melder_require (*fromTrack >= 1 && *toTrack <= my trackmodelers.size,
80 		U"1 \\=< \"fromTrack\" \\=< \"toTrack\" \\=< ", my trackmodelers.size, U".");
81 }
82 
newINTVECasNumbers(integer size,integer number)83 static autoINTVEC newINTVECasNumbers (integer size, integer number) {
84 	autoINTVEC target = raw_INTVEC (size);
85 	for (integer i = 1; i <= size; i++)
86 		target [i] = number;
87 	return target;
88 }
89 
FormantModeler_getStandardDeviation(FormantModeler me,integer itrack)90 double FormantModeler_getStandardDeviation (FormantModeler me, integer itrack) {
91 	double sigma = undefined;
92 	if (itrack > 0 && itrack <= my trackmodelers.size) {
93 		const DataModeler ff = my trackmodelers.at [itrack];
94 		sigma = DataModeler_getDataStandardDeviation (ff);
95 	}
96 	return sigma;
97 }
98 
FormantModeler_getDataPointValue(FormantModeler me,integer itrack,integer index)99 double FormantModeler_getDataPointValue (FormantModeler me, integer itrack, integer index) {
100 	double value = undefined;
101 	if (itrack > 0 && itrack <= my trackmodelers.size) {
102 		const DataModeler ff = my trackmodelers.at [itrack];
103 		value = DataModeler_getDataPointYValue (ff, index);
104 	}
105 	return value;
106 }
107 
FormantModeler_setDataPointValue(FormantModeler me,integer itrack,integer index,double value)108 void FormantModeler_setDataPointValue (FormantModeler me, integer itrack, integer index, double value) {
109 	if (itrack > 0 && itrack <= my trackmodelers.size) {
110 		const DataModeler ff = my trackmodelers.at [itrack];
111  		DataModeler_setDataPointYValue (ff, index, value);
112 	}
113 }
114 
FormantModeler_getDataPointSigma(FormantModeler me,integer itrack,integer index)115 double FormantModeler_getDataPointSigma (FormantModeler me, integer itrack, integer index) {
116 	double sigma = undefined;
117 	if (itrack > 0 && itrack <= my trackmodelers.size) {
118 		const DataModeler ff = (DataModeler) my trackmodelers.at [itrack];
119 		sigma = DataModeler_getDataPointYSigma (ff, index);
120 	}
121 	return sigma;
122 }
123 
FormantModeler_setDataPointSigma(FormantModeler me,integer itrack,integer index,double sigma)124 void FormantModeler_setDataPointSigma (FormantModeler me, integer itrack, integer index, double sigma) {
125 	if (itrack > 0 && itrack <= my trackmodelers.size) {
126 		const DataModeler ff = my trackmodelers.at [itrack];
127  		DataModeler_setDataPointYSigma (ff, index, sigma);
128 	}
129 }
130 
FormantModeler_getDataPointStatus(FormantModeler me,integer itrack,integer index)131 kDataModelerData FormantModeler_getDataPointStatus (FormantModeler me, integer itrack, integer index) {
132 	kDataModelerData value =kDataModelerData::INVALID;
133 	if (itrack > 0 && itrack <= my trackmodelers.size) {
134 		const DataModeler ff = my trackmodelers.at [itrack];
135 		value = DataModeler_getDataPointStatus (ff, index);
136 	}
137 	return value;
138 }
139 
FormantModeler_setDataPointStatus(FormantModeler me,integer itrack,integer index,kDataModelerData status)140 void FormantModeler_setDataPointStatus (FormantModeler me, integer itrack, integer index, kDataModelerData status) {
141 	if (itrack > 0 && itrack <= my trackmodelers.size) {
142 		const DataModeler ff = my trackmodelers.at [itrack];
143 		DataModeler_setDataPointStatus (ff, index, status);
144 	}
145 }
146 
FormantModeler_setDataPointValueAndStatus(FormantModeler me,integer itrack,integer index,double value,kDataModelerData dataStatus)147 static void FormantModeler_setDataPointValueAndStatus (FormantModeler me, integer itrack, integer index, double value, kDataModelerData dataStatus) {
148 	if (itrack > 0 && itrack <= my trackmodelers.size) {
149 		const DataModeler ff = my trackmodelers.at [itrack];
150 		DataModeler_setDataPointValueAndStatus (ff, index, value, dataStatus);
151 	}
152 }
153 
FormantModeler_setParameterValueFixed(FormantModeler me,integer itrack,integer index,double value)154 void FormantModeler_setParameterValueFixed (FormantModeler me, integer itrack, integer index, double value) {
155 	if (itrack > 0 && itrack <= my trackmodelers.size) {
156 		const DataModeler ffi = my trackmodelers.at [itrack];
157 		DataModeler_setParameterValueFixed (ffi, index, value);
158 	}
159 }
160 
FormantModeler_setParametersFree(FormantModeler me,integer fromTrack,integer toTrack,integer fromIndex,integer toIndex)161 void FormantModeler_setParametersFree (FormantModeler me, integer fromTrack, integer toTrack, integer fromIndex, integer toIndex) {
162 	checkTrackAutoRange (me, & fromTrack, & toTrack);
163 
164 	for (integer itrack = fromTrack; itrack <= toTrack; itrack ++) {
165 		const DataModeler ffi = my trackmodelers.at [itrack];
166 		DataModeler_setParametersFree (ffi, fromIndex, toIndex);
167 	}
168 }
169 
FormantModeler_setDataWeighing(FormantModeler me,integer fromTrack,integer toTrack,kFormantModelerWeights weighFormants)170 void FormantModeler_setDataWeighing (FormantModeler me, integer fromTrack, integer toTrack, kFormantModelerWeights weighFormants) {
171 	checkTrackAutoRange (me, & fromTrack, & toTrack);
172 
173 	for (integer itrack = fromTrack; itrack <= toTrack; itrack ++) {
174 		const DataModeler ffi = my trackmodelers.at [itrack];
175 		kDataModelerWeights dataWeights = kDataModelerWeights::EQUAL_WEIGHTS;
176 		if (weighFormants == kFormantModelerWeights::ONE_OVER_BANDWIDTH)
177 			dataWeights = kDataModelerWeights::ONE_OVER_SIGMA;
178 		else if (weighFormants == kFormantModelerWeights::ONE_OVER_SQRTBANDWIDTH)
179 			dataWeights = kDataModelerWeights::ONE_OVER_SQRTSIGMA;
180 		else
181 			dataWeights = kDataModelerWeights::RELATIVE_;
182 		DataModeler_setDataWeighing (ffi, dataWeights);
183 	}
184 }
185 
FormantModeler_fit(FormantModeler me)186 void FormantModeler_fit (FormantModeler me) {
187 	for (integer itrack = 1; itrack <= my trackmodelers.size; itrack ++) {
188 		const DataModeler ffi = my trackmodelers.at [itrack];
189 		DataModeler_fit (ffi);
190 	}
191 }
192 
FormantModeler_drawBasisFunction(FormantModeler me,Graphics g,double tmin,double tmax,double fmin,double fmax,integer itrack,integer iterm,bool scaled,integer numberOfPoints,bool garnish)193 void FormantModeler_drawBasisFunction (FormantModeler me, Graphics g, double tmin, double tmax, double fmin, double fmax,
194  	integer itrack, integer iterm, bool scaled, integer numberOfPoints, bool garnish)
195 {
196 	Function_unidirectionalAutowindow (me, & tmin, & tmax);
197 	if (itrack < 1 || itrack > my trackmodelers.size)
198 		return;
199 	Graphics_setInner (g);
200 	const DataModeler ffi = my trackmodelers.at [itrack];
201 	DataModeler_drawBasisFunction_inside (ffi, g, tmin, tmax, fmin, fmax, iterm, scaled, numberOfPoints);
202 	Graphics_unsetInner (g);
203 	if (garnish) {
204 		Graphics_inqWindow (g, & tmin, & tmax, & fmin, & fmax);
205 		Graphics_drawInnerBox (g);
206 		Graphics_textBottom (g, true, U"Time (s)");
207 		Graphics_textLeft (g, true, ( scaled ? U"Frequency (Hz)" : U"Amplitude" ));
208 		Graphics_marksBottom (g, 2, true, true, false);
209 		Graphics_markLeft (g, fmin, true, true, false, U"");
210 		Graphics_markLeft (g, fmax, true, true, false, U"");
211 	}
212 }
213 
FormantModeler_drawingSpecifiers_x(FormantModeler me,double * xmin,double * xmax,integer * ixmin,integer * ixmax)214 static integer FormantModeler_drawingSpecifiers_x (FormantModeler me, double *xmin, double *xmax, integer *ixmin, integer *ixmax) {
215 	Melder_assert (my trackmodelers.size > 0);
216 	const DataModeler fm = my trackmodelers.at [1];
217 	return DataModeler_drawingSpecifiers_x (fm, xmin, xmax, ixmin, ixmax);
218 }
219 
FormantModeler_getCumulativeChiScores(FormantModeler me,VEC chisq)220 static void FormantModeler_getCumulativeChiScores (FormantModeler me, VEC chisq) {
221 	try {
222 		const integer numberOfDataPoints = FormantModeler_getNumberOfDataPoints (me);
223 		const integer numberOfTracks = my trackmodelers.size;
224 		for (integer itrack = 1; itrack <= numberOfTracks; itrack ++) {
225 			const DataModeler fm = my trackmodelers.at [itrack];
226 			autoVEC zscores = DataModeler_getZScores (fm);
227 			autoVEC chisqif = DataModeler_getChisqScoresFromZScores (fm, zscores.get(), true); // undefined -> average
228 			for (integer ipoint = 1; ipoint <= numberOfDataPoints; ipoint ++)
229 				chisq [ipoint] += chisqif [ipoint];
230 		}
231 	} catch (MelderError) {
232 		Melder_throw (me, U"cannot determine cumulative chi squares.");
233 	}
234 }
235 
FormantModeler_getVariancesBetweenTrackAndEstimatedTrack(FormantModeler me,integer itrack,integer estimatedTrack)236 static autoVEC FormantModeler_getVariancesBetweenTrackAndEstimatedTrack (FormantModeler me, integer itrack, integer estimatedTrack) {
237 	try {
238 		const integer numberOfDataPoints = FormantModeler_getNumberOfDataPoints (me);
239 		const integer numberOfTracks = my trackmodelers.size;
240 		autoVEC var;
241 		if (itrack < 1 || itrack > numberOfTracks || estimatedTrack < 1 || estimatedTrack > numberOfTracks)
242 			return var;
243 		var. resize (numberOfDataPoints);
244 		const DataModeler fi = my trackmodelers.at [itrack];
245 		const DataModeler fe = my trackmodelers.at [estimatedTrack];
246 		for (integer ipoint = 1; ipoint <= numberOfDataPoints; ipoint ++) {
247 			var [ipoint] = undefined;
248 			if (fi -> data [ipoint] .status != kDataModelerData::INVALID) {
249 				const double ye = fe -> f_evaluate (fe, fe -> data [ipoint] .x, fe -> parameters.get());
250 				const double diff = ye - fi -> data [ipoint] .y;
251 				var [ipoint] = diff * diff;
252 			}
253 		}
254 		return var;
255 	} catch (MelderError) {
256 		Melder_throw (me, U"Cannot get variances between track and estimate.");
257 	}
258 }
259 
FormantModeler_getSumOfVariancesBetweenShiftedAndEstimatedTracks(FormantModeler me,kFormantModelerTrackShift shiftDirection,integer * fromTrack,integer * toTrack)260 static autoVEC FormantModeler_getSumOfVariancesBetweenShiftedAndEstimatedTracks (FormantModeler me,
261 	kFormantModelerTrackShift shiftDirection, integer *fromTrack, integer *toTrack)
262 {
263 	try {
264 		const integer numberOfTracks = my trackmodelers.size;
265 		if (*fromTrack < 1 || *fromTrack > numberOfTracks || *toTrack < 1 || *toTrack > numberOfTracks || *toTrack < *fromTrack) {
266 			*toTrack = 1;
267 			*fromTrack = numberOfTracks;
268 		}
269 		integer formantTrack = *fromTrack, estimatedTrack = *fromTrack; // FormantModeler_NOSHIFT_TRACKS
270 		if (shiftDirection == kFormantModelerTrackShift::DOWN) {
271 			estimatedTrack = *fromTrack;
272 			formantTrack = *fromTrack + 1;
273 			*fromTrack = ( *fromTrack == 1 ? 2 : *fromTrack );
274 		} else if (shiftDirection == kFormantModelerTrackShift::UP) {
275 			formantTrack = *fromTrack;
276 			estimatedTrack = *fromTrack + 1;
277 			*toTrack = ( *toTrack == numberOfTracks ? numberOfTracks - 1 : *toTrack );
278 		}
279 		const integer numberOfDataPoints = FormantModeler_getNumberOfDataPoints (me);
280 		autoVEC sumOfVariances = zero_VEC (numberOfDataPoints);
281 		for (integer itrack = *fromTrack; itrack <= *toTrack; itrack ++) {
282 			autoVEC vari = FormantModeler_getVariancesBetweenTrackAndEstimatedTrack (me, formantTrack, estimatedTrack);
283 			for (integer ipoint = 1; ipoint <= numberOfDataPoints; ipoint ++) {
284 				if (isdefined (vari [ipoint]))
285 					sumOfVariances [ipoint] += vari [ipoint];
286 			}
287 			formantTrack ++;
288 			estimatedTrack ++;
289 		}
290 		return sumOfVariances;
291 	} catch (MelderError) {
292 		Melder_throw (me, U" cannot get variances.");
293 	}
294 }
295 
FormantModeler_drawVariancesOfShiftedTracks(FormantModeler me,Graphics g,double xmin,double xmax,double ymin,double ymax,kFormantModelerTrackShift shiftDirection,integer fromTrack,integer toTrack,bool garnish)296 void FormantModeler_drawVariancesOfShiftedTracks (FormantModeler me, Graphics g, double xmin, double xmax,
297 	double ymin, double ymax, kFormantModelerTrackShift shiftDirection, integer fromTrack, integer toTrack, bool garnish)
298 {
299 	try {
300 		integer ixmin, ixmax;
301 		checkTrackAutoRange (me, & fromTrack, & toTrack);
302 		Melder_require (FormantModeler_drawingSpecifiers_x (me, & xmin, & xmax, & ixmin, & ixmax) > 0,
303 			U"The are not enough data points in the drawing range.");
304 
305 		autoVEC varShifted = FormantModeler_getSumOfVariancesBetweenShiftedAndEstimatedTracks (me, shiftDirection, & fromTrack, & toTrack);
306 		autoVEC var = FormantModeler_getSumOfVariancesBetweenShiftedAndEstimatedTracks (me, kFormantModelerTrackShift::NO_, & fromTrack, & toTrack);
307 		for (integer ipoint = ixmin + 1; ipoint <= ixmax; ipoint ++) {
308 			if (isdefined (varShifted [ipoint]) && isdefined (var [ipoint]))
309 				var [ipoint] -= varShifted [ipoint];
310 		}
311 		if (ymax <= ymin)
312 			NUMextrema (var.part (ixmin, ixmax), & ymin, & ymax);
313 		if (ymin == ymax) {
314 			ymin -= 0.5;
315 			ymax += 0.5;
316 		}
317 		Graphics_setInner (g);
318 		Graphics_setWindow (g, xmin, xmax, ymin, ymax);
319 		const DataModeler thee = my trackmodelers.at [1];
320 		while (isundef (var [ixmin]) && ixmin <= ixmax)
321 			ixmin ++;
322 		double xp = thy data [ixmin] .x, yp = var [ixmin];
323 		for (integer ipoint = ixmin + 1; ipoint <= ixmax; ipoint ++) {
324 			if (isdefined (var [ipoint])) {
325 				Graphics_line (g, xp, yp, thy data [ipoint] .x, var [ipoint]);
326 				xp = thy data [ipoint] .x;
327 				yp = var [ipoint];
328 			}
329 		}
330 		Graphics_unsetInner (g);
331 		if (garnish) {
332 			Graphics_drawInnerBox (g);
333 			Graphics_marksBottom (g, 2, true, true, false);
334 			Graphics_marksLeft (g, 2, true, true, false);
335 		}
336 
337 	} catch (MelderError) {
338 		Melder_clearError ();
339 	}
340 }
341 
FormantModeler_drawCumulativeChiScores(FormantModeler me,Graphics g,double xmin,double xmax,double ymin,double ymax,bool garnish)342 void FormantModeler_drawCumulativeChiScores (FormantModeler me, Graphics g, double xmin, double xmax, double ymin, double ymax, bool garnish) {
343 	try {
344 		integer ixmin, ixmax;
345 		Melder_require (FormantModeler_drawingSpecifiers_x (me, & xmin, & xmax, & ixmin, & ixmax) > 0,
346 			U"Not enough data points in drawing range.");
347 		const integer numberOfDataPoints = FormantModeler_getNumberOfDataPoints (me);
348 		autoVEC chisq = zero_VEC (numberOfDataPoints);
349 		FormantModeler_getCumulativeChiScores (me, chisq.get());
350 		if (ymax <= ymin)
351 			NUMextrema (chisq.part (ixmin, ixmax), & ymin, & ymax);
352 		Graphics_setInner (g);
353 		Graphics_setWindow (g, xmin, xmax, ymin, ymax);
354 		DataModeler thee = my trackmodelers.at [1];
355 		for (integer ipoint = ixmin + 1; ipoint <= ixmax; ipoint ++)
356 			Graphics_line (g, thy data [ipoint - 1] .x, chisq [ipoint - 1], thy data [ipoint] .x, chisq [ipoint]);
357 		Graphics_unsetInner (g);
358 		if (garnish) {
359 			Graphics_drawInnerBox (g);
360 			Graphics_marksBottom (g, 2, true, true, false);
361 			Graphics_marksLeft (g, 2, true, true, false);
362 		}
363 	} catch (MelderError) {
364 		Melder_clearError ();
365 	}
366 }
367 
FormantModeler_drawOutliersMarked(FormantModeler me,Graphics g,double tmin,double tmax,double fmax,integer fromTrack,integer toTrack,double numberOfSigmas,conststring32 mark,double marksFontSize,MelderColour oddTracks,MelderColour evenTracks,bool garnish)368 void FormantModeler_drawOutliersMarked (FormantModeler me, Graphics g, double tmin, double tmax, double fmax, integer fromTrack, integer toTrack,
369 	double numberOfSigmas, conststring32 mark, double marksFontSize, MelderColour oddTracks, MelderColour evenTracks, bool garnish)
370 {
371 	Function_unidirectionalAutowindow (me, & tmin, & tmax);
372 	checkTrackAutoRange (me, & fromTrack, & toTrack);
373 	Graphics_setInner (g);
374 	double currectFontSize = Graphics_inqFontSize (g);
375 	for (integer itrack = fromTrack; itrack <= toTrack; itrack ++) {
376 		const DataModeler ffi = my trackmodelers.at [itrack];
377 		Graphics_setColour (g, itrack %2  == 1 ? oddTracks : evenTracks );
378 		DataModeler_drawOutliersMarked_inside (ffi, g, tmin, tmax, 0.0, fmax, numberOfSigmas, mark, marksFontSize);
379 	}
380 	Graphics_setFontSize (g, currectFontSize);
381 	Graphics_unsetInner (g);
382 	if (garnish) {
383 		Graphics_drawInnerBox (g);
384 		Graphics_textBottom (g, true, U"Time (s)");
385 		Graphics_textLeft (g, true, U"Formant frequency (Hz)");
386 		Graphics_marksBottom (g, 2, true, true, false);
387 		Graphics_marksLeftEvery (g, 1.0, 1000.0, true, true, true);
388 	}
389 }
390 
FormantModeler_normalProbabilityPlot(FormantModeler me,Graphics g,integer itrack,integer numberOfQuantiles,double numberOfSigmas,double labelSize,conststring32 label,bool garnish)391 void FormantModeler_normalProbabilityPlot (FormantModeler me, Graphics g, integer itrack,
392 	integer numberOfQuantiles, double numberOfSigmas, double labelSize, conststring32 label, bool garnish) {
393 	if (itrack > 0 || itrack <= my trackmodelers.size) {
394 		const DataModeler ff = my trackmodelers.at [itrack];
395 		DataModeler_normalProbabilityPlot (ff, g,  numberOfQuantiles, numberOfSigmas, labelSize, label, garnish);
396 	}
397 }
398 
FormantModeler_drawModel_inside(FormantModeler me,Graphics g,double tmin,double tmax,double fmax,integer fromTrack,integer toTrack,MelderColour oddTracks,MelderColour evenTracks,integer numberOfPoints)399 void FormantModeler_drawModel_inside (FormantModeler me, Graphics g, double tmin, double tmax, double fmax,
400 	integer fromTrack, integer toTrack, MelderColour oddTracks, MelderColour evenTracks, integer numberOfPoints) {
401 	checkTrackAutoRange (me, & fromTrack, & toTrack);
402 	for (integer itrack = fromTrack; itrack <= toTrack; itrack ++) {
403 		DataModeler ffi = my trackmodelers.at [itrack];
404 		Graphics_setColour (g, itrack % 2 == 1 ? oddTracks : evenTracks );
405 		DataModeler_drawModel_inside (ffi, g, tmin, tmax, 0.0, fmax, numberOfPoints);
406 	}
407 }
408 
FormantModeler_drawTracks_inside(FormantModeler me,Graphics g,double xmin,double xmax,double fmax,integer fromTrack,integer toTrack,bool useEstimatedTrack,integer numberOfParameters,MelderColour oddTracks,MelderColour evenTracks)409 void FormantModeler_drawTracks_inside (FormantModeler me, Graphics g, double xmin, double xmax, double fmax, integer fromTrack, integer toTrack, bool useEstimatedTrack, integer numberOfParameters, MelderColour oddTracks, MelderColour evenTracks) {
410 	checkTrackAutoRange (me, & fromTrack, & toTrack);
411 	for (integer itrack = fromTrack; itrack <= toTrack; itrack ++) {
412 		DataModeler ffi = my trackmodelers.at [itrack];
413 		Graphics_setColour (g, itrack % 2 == 1 ? oddTracks : evenTracks );
414 		DataModeler_drawTrack_inside (ffi, g, xmin, xmax, 0.0, fmax, useEstimatedTrack, numberOfParameters);
415 	}
416 }
417 
FormantModeler_drawTracks(FormantModeler me,Graphics g,double tmin,double tmax,double fmax,integer fromTrack,integer toTrack,bool useEstimatedTrack,integer numberOfParameters,MelderColour oddTracks,MelderColour evenTracks,bool garnish)418 void FormantModeler_drawTracks (FormantModeler me, Graphics g, double tmin, double tmax, double fmax,
419 	integer fromTrack, integer toTrack, bool useEstimatedTrack, integer numberOfParameters, MelderColour oddTracks, MelderColour evenTracks, bool garnish)
420 {
421 	Function_unidirectionalAutowindow (me, & tmin, & tmax);
422 	checkTrackAutoRange (me, & fromTrack, & toTrack);
423 	Graphics_setInner (g);
424 	FormantModeler_drawTracks_inside (me, g, tmin, tmax, fmax, fromTrack, toTrack, useEstimatedTrack, numberOfParameters, oddTracks, evenTracks);
425 	Graphics_unsetInner (g);
426 	if (garnish) {
427 		Graphics_drawInnerBox (g);
428 		Graphics_textBottom (g, true, U"Time (s)");
429 		Graphics_textLeft (g, true, U"Formant frequency (Hz)");
430 		Graphics_marksBottom (g, 2, true, true, false);
431 		Graphics_marksLeftEvery (g, 1.0, 1000.0, true, true, true);
432 	}
433 }
434 
FormantModeler_speckle_inside(FormantModeler me,Graphics g,double xmin,double xmax,double fmax,integer fromTrack,integer toTrack,bool useEstimatedTrack,integer numberOfParameters,bool errorBars,MelderColour oddTracks,MelderColour evenTracks)435 void FormantModeler_speckle_inside (FormantModeler me, Graphics g, double xmin, double xmax, double fmax,
436 	integer fromTrack, integer toTrack, bool useEstimatedTrack, integer numberOfParameters, bool errorBars, MelderColour oddTracks, MelderColour evenTracks) {
437 	checkTrackAutoRange (me, & fromTrack, & toTrack);
438 	for (integer itrack = fromTrack; itrack <= toTrack; itrack ++) {
439 		const DataModeler ffi = my trackmodelers.at [itrack];
440 		Graphics_setColour (g, itrack % 2 == 1 ? oddTracks : evenTracks);
441 		DataModeler_speckle_inside (ffi, g, xmin, xmax, 0, fmax, useEstimatedTrack, numberOfParameters, errorBars, 0.0);
442 	}
443 }
444 
FormantModeler_speckle(FormantModeler me,Graphics g,double tmin,double tmax,double fmax,integer fromTrack,integer toTrack,bool useEstimatedTrack,integer numberOfParameters,bool errorBars,MelderColour oddTracks,MelderColour evenTracks,bool garnish)445 void FormantModeler_speckle (FormantModeler me, Graphics g, double tmin, double tmax, double fmax,
446 	integer fromTrack, integer toTrack, bool useEstimatedTrack, integer numberOfParameters,
447 	bool errorBars, MelderColour oddTracks, MelderColour evenTracks, bool garnish)
448 {
449 	Function_unidirectionalAutowindow (me, & tmin, & tmax);
450 	checkTrackAutoRange (me, & fromTrack, & toTrack);
451 	Graphics_setInner (g);
452 	FormantModeler_speckle_inside (me, g, tmin, tmax, fmax, fromTrack, toTrack, useEstimatedTrack, numberOfParameters, errorBars, oddTracks, evenTracks);
453 	Graphics_unsetInner (g);
454 	if (garnish) {
455 		Graphics_drawInnerBox (g);
456 		Graphics_textBottom (g, true, U"Time (s)");
457 		Graphics_textLeft (g, true, U"Formant frequency (Hz)");
458 		Graphics_marksBottom (g, 2, true, true, false);
459 		Graphics_marksLeftEvery (g, 1.0, 1000.0, true, true, true);
460 	}
461 }
462 
FormantModeler_create(double tmin,double tmax,integer numberOfDataPoints,integer numberOfTracks,integer numberOfParameters)463 autoFormantModeler FormantModeler_create (double tmin, double tmax, integer numberOfDataPoints, integer numberOfTracks, integer numberOfParameters) {
464 	autoINTVEC npar = newINTVECasNumbers (numberOfTracks, numberOfParameters);
465 	return FormantModeler_create (tmin, tmax, numberOfDataPoints, npar.get());
466 }
467 
FormantModeler_create(double tmin,double tmax,integer numberOfDataPoints,constINTVEC const & numberOfParameters)468 autoFormantModeler FormantModeler_create (double tmin, double tmax, integer numberOfDataPoints, constINTVEC const& numberOfParameters) {
469 	try {
470 		autoFormantModeler me = Thing_new (FormantModeler);
471 		my xmin = tmin;
472 		my xmax = tmax;
473 		for (integer itrack = 1; itrack <= numberOfParameters.size; itrack ++) {
474 			autoDataModeler ff = DataModeler_create (tmin, tmax, numberOfDataPoints, numberOfParameters [itrack], kDataModelerFunction::LEGENDRE);
475 			my trackmodelers. addItem_move (ff.move());
476 		}
477 		return me;
478 	} catch (MelderError) {
479 		Melder_throw (U"FormantModeler not created.");
480 	}
481 }
482 
FormantModeler_getModelValueAtTime(FormantModeler me,integer itrack,double time)483 double FormantModeler_getModelValueAtTime (FormantModeler me, integer itrack, double time) {
484 	double f = undefined;
485 	if (itrack >= 1 && itrack <= my trackmodelers.size) {
486 		const DataModeler thee = my trackmodelers.at [itrack];
487 		f = DataModeler_getModelValueAtX (thee, time);
488 	}
489 	return f;
490 }
491 
FormantModeler_getModelValueAtIndex(FormantModeler me,integer itrack,integer index)492 double FormantModeler_getModelValueAtIndex (FormantModeler me, integer itrack, integer index) {
493 	double f = undefined;
494 	if (itrack >= 1 && itrack <= my trackmodelers.size) {
495 		const DataModeler thee = my trackmodelers.at [itrack];
496 		f = DataModeler_getModelValueAtIndex (thee, index);
497 	}
498 	return f;
499 }
500 
FormantModeler_getWeightedMean(FormantModeler me,integer itrack)501 double FormantModeler_getWeightedMean (FormantModeler me, integer itrack) {
502 	double f = undefined;
503 	if (itrack >= 1 && itrack <= my trackmodelers.size) {
504 		const DataModeler thee = my trackmodelers.at [itrack];
505 		f = DataModeler_getWeightedMean (thee);
506 	}
507 	return f;
508 }
509 
FormantModeler_getNumberOfTracks(FormantModeler me)510 integer FormantModeler_getNumberOfTracks (FormantModeler me) {
511 	return my trackmodelers.size;
512 }
513 
FormantModeler_getNumberOfParameters(FormantModeler me,integer itrack)514 integer FormantModeler_getNumberOfParameters (FormantModeler me, integer itrack) {
515 	integer numberOfParameters = 0;
516 	if (itrack > 0 && itrack <= my trackmodelers.size) {
517 		const DataModeler ff = my trackmodelers.at [itrack];
518 		numberOfParameters = ff -> numberOfParameters;
519 	}
520 	return numberOfParameters;
521 }
522 
FormantModeler_getNumberOfFixedParameters(FormantModeler me,integer itrack)523 integer FormantModeler_getNumberOfFixedParameters (FormantModeler me, integer itrack) {
524 	integer numberOfParameters = 0;
525 	if (itrack > 0 && itrack <= my trackmodelers.size) {
526 		const DataModeler ff = my trackmodelers.at [itrack];
527 		numberOfParameters = ff -> numberOfParameters;
528 		numberOfParameters -= DataModeler_getNumberOfFreeParameters (ff);
529 	}
530 	return numberOfParameters;
531 }
532 
533 
FormantModeler_getNumberOfInvalidDataPoints(FormantModeler me,integer itrack)534 integer FormantModeler_getNumberOfInvalidDataPoints (FormantModeler me, integer itrack) {
535 	integer numberOfInvalidDataPoints = 0;
536 	if (itrack > 0 && itrack <= my trackmodelers.size) {
537 		const DataModeler ff = my trackmodelers.at [itrack];
538 		numberOfInvalidDataPoints = DataModeler_getNumberOfInvalidDataPoints (ff);
539 	}
540 	return numberOfInvalidDataPoints;
541 }
542 
FormantModeler_getParameterValue(FormantModeler me,integer itrack,integer iparameter)543 double FormantModeler_getParameterValue (FormantModeler me, integer itrack, integer iparameter) {
544 	double value = undefined;
545 	if (itrack > 0 && itrack <= my trackmodelers.size) {
546 		DataModeler ff = my trackmodelers.at [itrack];
547 		value = DataModeler_getParameterValue (ff, iparameter);
548 	}
549 	return value;
550 }
551 
FormantModeler_getParameterStatus(FormantModeler me,integer itrack,integer index)552 kDataModelerParameterStatus FormantModeler_getParameterStatus (FormantModeler me, integer itrack, integer index) {
553 	kDataModelerParameterStatus status = kDataModelerParameterStatus::NOT_DEFINED;
554 	if (itrack > 0 && itrack <= my trackmodelers.size) {
555 		const DataModeler ff = my trackmodelers.at [itrack];
556 		status = DataModeler_getParameterStatus (ff, index);
557 	}
558 	return status;
559 }
560 
FormantModeler_getParameterStandardDeviation(FormantModeler me,integer itrack,integer index)561 double FormantModeler_getParameterStandardDeviation ( FormantModeler me, integer itrack, integer index) {
562 	double stdev = undefined;
563 	if (itrack > 0 && itrack <= my trackmodelers.size) {
564 		const DataModeler ff = my trackmodelers.at [itrack];
565 		stdev = DataModeler_getParameterStandardDeviation (ff, index);
566 	}
567 	return stdev;
568 }
569 
FormantModeler_getDegreesOfFreedom(FormantModeler me,integer itrack)570 double FormantModeler_getDegreesOfFreedom (FormantModeler me, integer itrack) {
571 	double dof = 0.0;
572 	if (itrack > 0 && itrack <= my trackmodelers.size) {
573 		const DataModeler ff = my trackmodelers.at [itrack];
574 		dof = DataModeler_getDegreesOfFreedom (ff);
575 	}
576 	return dof;
577 }
578 
FormantModeler_getVarianceOfParameters(FormantModeler me,integer fromTrack,integer toTrack,integer fromIndex,integer toIndex,integer * out_numberOfFreeParameters)579 double FormantModeler_getVarianceOfParameters (FormantModeler me, integer fromTrack, integer toTrack, integer fromIndex, integer toIndex, integer *out_numberOfFreeParameters) {
580 	checkTrackAutoRange (me, & fromTrack, & toTrack);
581 	double variance = 0.0;
582 	integer numberOfFreeParameters = 0;
583 	for (integer itrack = fromTrack; itrack <= toTrack; itrack ++) {
584 		const DataModeler ff = my trackmodelers.at [itrack];
585 		integer free;
586 		variance += DataModeler_getVarianceOfParameters (ff, fromIndex, toIndex, & free);
587 		numberOfFreeParameters += free;
588 	}
589 
590 	if (out_numberOfFreeParameters)
591 		*out_numberOfFreeParameters = numberOfFreeParameters;
592 	return variance;
593 }
594 
FormantModeler_getNumberOfDataPoints(FormantModeler me)595 integer FormantModeler_getNumberOfDataPoints (FormantModeler me) {
596 	Melder_assert (my trackmodelers.size > 0);
597 	const DataModeler thee = my trackmodelers.at [1];
598 	// all tracks have the same number of data points
599 	return thy numberOfDataPoints;
600 }
601 
FormantModeler_to_Table_zscores(FormantModeler me)602 autoTable FormantModeler_to_Table_zscores (FormantModeler me) {
603 	try {
604 		const integer icolt = 1, numberOfFormants = my trackmodelers.size;
605 		const integer numberOfDataPoints = FormantModeler_getNumberOfDataPoints (me);
606 		autoTable ztable = Table_createWithoutColumnNames (numberOfDataPoints, numberOfFormants + 1);
607 		Table_setColumnLabel (ztable.get(), icolt, U"time");
608 		for (integer itrack = 1; itrack <= numberOfFormants; itrack ++) {
609 			const integer icolz = itrack + 1;
610 			Table_setColumnLabel (ztable.get(), icolz, Melder_cat (U"z", itrack));
611 			DataModeler ffi = my trackmodelers.at [itrack];
612 			if (itrack == 1) {
613 				for (integer i = 1; i <= numberOfDataPoints; i ++)   // only once all tracks have same x-values
614 					Table_setNumericValue (ztable.get(), i, icolt, ffi -> data [i] .x);
615 			}
616 			autoVEC zscores = DataModeler_getZScores (ffi);
617 			for (integer i = 1; i <= numberOfDataPoints; i ++)
618 				Table_setNumericValue (ztable.get(), i, icolz, zscores [i]);
619 		}
620 		return ztable;
621 	} catch (MelderError) {
622 		Melder_throw (U"Table not created.");
623 	}
624 }
625 
FormantModeler_extractDataModeler(FormantModeler me,integer itrack)626 autoDataModeler FormantModeler_extractDataModeler (FormantModeler me, integer itrack) {
627 	try {
628 		Melder_require (itrack > 0 && itrack<= my trackmodelers.size,
629 			U"The formant should be greater than zero and smaller than or equal to ", my trackmodelers.size);
630 		const DataModeler ff = my trackmodelers.at [itrack];
631 		autoDataModeler thee = Data_copy (ff);
632 		return thee;
633 	} catch (MelderError) {
634 		Melder_throw (U"DataModeler not created.");
635 	}
636 }
637 
FormantModeler_to_Covariance_parameters(FormantModeler me,integer itrack)638 autoCovariance FormantModeler_to_Covariance_parameters (FormantModeler me, integer itrack) {
639 	try {
640 		Melder_require (itrack > 0 && itrack <= my trackmodelers.size,
641 			U"The formant should be greater than zero and smaller than or equal to ", my trackmodelers.size);
642 		const DataModeler dm = my trackmodelers.at [itrack];
643 		autoCovariance thee = Data_copy (dm -> parameterCovariances.get());
644 		return thee;
645 	} catch (MelderError) {
646 		Melder_throw (U"Covariance not created.");
647 	}
648 
649 }
650 
FormantModeler_setTolerance(FormantModeler me,double tolerance)651 void FormantModeler_setTolerance (FormantModeler me, double tolerance) {
652 	for (integer itrack = 1; itrack <= my trackmodelers.size; itrack ++) {
653 		const DataModeler ffi = my trackmodelers.at [itrack];
654 		DataModeler_setTolerance (ffi, tolerance);
655 	}
656 }
657 
FormantModeler_indexToTime(FormantModeler me,integer index)658 double FormantModeler_indexToTime (FormantModeler me, integer index) {
659 	Melder_assert (my trackmodelers.size > 0);
660 	const DataModeler thee = my trackmodelers.at [1];
661 	return ( index > 0 && index <= thy numberOfDataPoints ? thy data [index] .x : undefined );
662 }
663 
Formant_to_FormantModeler(Formant me,double tmin,double tmax,integer numberOfFormants,integer numberOfParametersPerTrack)664 autoFormantModeler Formant_to_FormantModeler (Formant me, double tmin, double tmax,
665 	integer numberOfFormants, integer numberOfParametersPerTrack) {
666 	autoINTVEC npar = newINTVECasNumbers (numberOfFormants, numberOfParametersPerTrack);
667 	return Formant_to_FormantModeler (me, tmin, tmax, npar.get());
668 }
669 
Formant_to_FormantModeler(Formant me,double tmin,double tmax,constINTVEC const & numberOfParametersPerTrack)670 autoFormantModeler Formant_to_FormantModeler (Formant me, double tmin, double tmax, constINTVEC const& numberOfParametersPerTrack) {
671 	try {
672 		integer ifmin, ifmax, posInCollection = 0;
673 		Function_unidirectionalAutowindow (me, & tmin, & tmax);
674 		const integer numberOfDataPoints = Sampled_getWindowSamples (me, tmin, tmax, & ifmin, & ifmax);
675 		autoFormantModeler thee = FormantModeler_create (tmin, tmax, numberOfDataPoints, numberOfParametersPerTrack);
676 		Thing_setName (thee.get(), my name.get());
677 		for (integer iformant = 1; iformant <= numberOfParametersPerTrack.size; iformant ++) {
678 			posInCollection ++;
679 			const DataModeler ffi = thy trackmodelers.at [posInCollection];
680 			integer idata = 0, validData = 0;
681 			for (integer iframe = ifmin; iframe <= ifmax; iframe ++) {
682 				const Formant_Frame curFrame = & my frames [iframe];
683 				ffi -> data [++ idata] .x = Sampled_indexToX (me, iframe);
684 				ffi -> data [idata] .status = kDataModelerData::INVALID;
685 				if (iformant <= curFrame -> numberOfFormants) {
686 					const double frequency = curFrame -> formant [iformant]. frequency;
687 					if (isdefined (frequency)) {
688 						const double bandwidth = curFrame -> formant [iformant]. bandwidth;
689 						ffi -> data [idata] .y = curFrame -> formant [iformant]. frequency;
690 						ffi -> data [idata] .sigmaY = bandwidth;
691 						ffi -> data [idata] .status = kDataModelerData::VALID;
692 						validData ++;
693 					}
694 				}
695 			}
696 			ffi -> weighData = kDataModelerWeights::ONE_OVER_SIGMA;
697 			ffi -> tolerance = 1e-5;
698 		}
699 		FormantModeler_fit (thee.get());
700 		return thee;
701 	} catch (MelderError) {
702 		Melder_throw (U"FormantModeler not created.");
703 	}
704 }
705 
FormantModeler_to_Formant(FormantModeler me,bool useEstimates,bool estimateUndefineds)706 autoFormant FormantModeler_to_Formant (FormantModeler me, bool useEstimates, bool estimateUndefineds) {
707 	try {
708 		const integer numberOfFormants = my trackmodelers.size;
709 		const DataModeler ff = my trackmodelers.at [1];
710 		const integer numberOfFrames = ff -> numberOfDataPoints;
711 		const double t1 = ff -> data [1] .x, dt = ff -> data [2] .x - t1;
712 		autoFormant thee = Formant_create (my xmin, my xmax, numberOfFrames, dt, t1, numberOfFormants);
713 		autoVEC sigma = raw_VEC (numberOfFormants);
714 		if (useEstimates || estimateUndefineds) {
715 			for (integer itrack = 1; itrack <= numberOfFormants; itrack ++)
716 				sigma [itrack] = FormantModeler_getStandardDeviation (me, itrack);
717 		}
718 		for (integer iframe = 1; iframe <= numberOfFrames; iframe ++) {
719 			const Formant_Frame thyFrame = & thy frames [iframe];
720 			thyFrame -> intensity = 1.0; //???
721 			thyFrame -> formant = newvectorzero <structFormant_Formant> (numberOfFormants);
722 
723 			for (integer itrack = 1; itrack <= numberOfFormants; itrack ++) {
724 				DataModeler ffi = my trackmodelers.at [itrack];
725 				double f = undefined, b = f;
726 				if (ffi -> data [iframe] .status != kDataModelerData::INVALID) {
727 					f = ( useEstimates ? DataModeler_getModelValueAtX (ffi, ffi -> data [iframe] .x) :
728 						ffi -> data [iframe] .y);
729 					b = ff -> data [iframe] .sigmaY; // copy original value
730 				} else {
731 					if (estimateUndefineds) {
732 						f = FormantModeler_getModelValueAtTime (me, itrack, ffi -> data [iframe] .x);
733 						b = sigma [itrack];
734 					}
735 				}
736 				thyFrame -> formant [itrack]. frequency = f;
737 				thyFrame -> formant [itrack]. bandwidth = b;
738 			}
739 		}
740 		return thee;
741 	} catch (MelderError) {
742 		Melder_throw (U"Cannot create Formant from FormantModeler.");
743 	}
744 }
745 
FormantModeler_getChiSquaredQ(FormantModeler me,integer fromTrack,integer toTrack,double * out_probability,double * out_ndf)746 double FormantModeler_getChiSquaredQ (FormantModeler me, integer fromTrack, integer toTrack, double *out_probability, double *out_ndf) {
747 	double ndfTotal = 0.0;
748 	checkTrackAutoRange (me, & fromTrack, & toTrack);
749 	double chisq = 0.0;
750 	integer numberOfDefined = 0;
751 	for (integer itrack = fromTrack; itrack <= toTrack; itrack ++) {
752 		const DataModeler ffi = my trackmodelers.at [itrack];
753 		double p, df;
754 		const double chisqi = DataModeler_getChiSquaredQ (ffi, & p, & df);
755 		if (isdefined (chisqi)) {
756 			chisq += df * chisqi;
757 			ndfTotal += df;
758 			numberOfDefined ++;
759 		}
760 	}
761 	if (numberOfDefined == toTrack - fromTrack + 1) {   // chisq of all tracks defined
762 		chisq /= ndfTotal;
763 		if (out_ndf)
764 			*out_ndf = ndfTotal;
765 		if (out_probability)
766 			*out_probability = NUMchiSquareQ (chisq, ndfTotal);
767 	} else
768 		chisq = undefined;
769 	return chisq;
770 }
771 
FormantModeler_getCoefficientOfDetermination(FormantModeler me,integer fromTrack,integer toTrack)772 double FormantModeler_getCoefficientOfDetermination (FormantModeler me, integer fromTrack, integer toTrack) {
773 	double rSquared = undefined;
774 	checkTrackAutoRange (me, & fromTrack, & toTrack);
775 	double ssreg = 0.0, sstot = 0.0;
776 	for (integer itrack= fromTrack; itrack <= toTrack; itrack ++) {
777 		const DataModeler ffi = my trackmodelers.at [itrack];
778 		double ssregi, sstoti;
779 		DataModeler_getCoefficientOfDetermination (ffi, & ssregi, & sstoti);
780 		sstot += sstoti;
781 		ssreg += ssregi;
782 	}
783 	rSquared = ( sstot > 0.0 ? ssreg / sstot : 1.0 );
784 	return rSquared;
785 }
786 
FormantModeler_getResidualSumOfSquares(FormantModeler me,integer itrack,integer * out_numberOfDataPoints)787 double FormantModeler_getResidualSumOfSquares (FormantModeler me, integer itrack, integer *out_numberOfDataPoints) {
788 	double rss = undefined;
789 	integer numberOfDataPoints = -1;
790 	if (itrack < 1 || itrack > my trackmodelers.size)
791 		return undefined;
792 	const DataModeler ff = my trackmodelers.at [itrack];
793 	rss = DataModeler_getResidualSumOfSquares (ff, & numberOfDataPoints);
794 	if (out_numberOfDataPoints)
795 		*out_numberOfDataPoints = numberOfDataPoints;
796 	return rss;
797 }
798 
FormantModeler_setParameterValuesToZero(FormantModeler me,integer fromTrack,integer toTrack,double numberOfSigmas)799 void FormantModeler_setParameterValuesToZero (FormantModeler me, integer fromTrack, integer toTrack, double numberOfSigmas) {
800 	checkTrackAutoRange (me, & fromTrack, & toTrack);
801 	for (integer itrack= fromTrack; itrack <= toTrack; itrack ++) {
802 		const DataModeler ffi = my trackmodelers.at [itrack];
803 		DataModeler_setParameterValuesToZero (ffi, numberOfSigmas);
804 	}
805 }
806 
807 
FormantModeler_processOutliers(FormantModeler me,double numberOfSigmas)808 autoFormantModeler FormantModeler_processOutliers (FormantModeler me, double numberOfSigmas) {
809 	try {
810 		const integer numberOfFormants = my trackmodelers.size;
811 		Melder_require (numberOfFormants > 2,
812 			U"We need at least three formants to process outliers.");
813 
814 		const integer numberOfDataPoints = FormantModeler_getNumberOfDataPoints (me);
815 		autoVEC x = raw_VEC (numberOfDataPoints); // also store x-values
816 		autoMAT z = raw_MAT (numberOfFormants, numberOfDataPoints);
817 		// maybe some of the formants had NUMundefind's.
818 
819 		// 1. calculate z-scores for each formant and sort them in descending order
820 		DataModeler ff = my trackmodelers.at [1];
821 		for (integer ipoint = 1; ipoint <= numberOfDataPoints; ipoint ++)
822 			x [ipoint] = ff -> data [ipoint] .x;
823 		for (integer itrack = 1; itrack <= numberOfFormants; itrack ++) {
824 			const DataModeler ffi = my trackmodelers.at [itrack];
825 			autoVEC zscores = DataModeler_getZScores (ffi);
826 			z.row (itrack)  <<=  zscores.get();
827 		}
828 		// 2. Do the manipulation in a copy
829 		autoFormantModeler thee = Data_copy (me);
830 		for (integer ipoint = 1; ipoint <= numberOfDataPoints; ipoint ++) {
831 			// First the easy one: first formant missing: F1' = F2; F2' = F3
832 			if (isdefined (z [1] [ipoint]) && isdefined (z [1] [ipoint]) && isdefined (z [3] [ipoint])) {
833 				if (z [1] [ipoint] > numberOfSigmas && z [2] [ipoint] > numberOfSigmas && z [3] [ipoint] > numberOfSigmas) {
834 					// all deviations have the same sign:
835 					// probably F1 is missing
836 					// try if f2 <- F1 and f3 <- F2 reduces chisq
837 					const double f2 = FormantModeler_getDataPointValue (me, 1, ipoint); // F1
838 					const double f3 = FormantModeler_getDataPointValue (me, 2, ipoint); // F2
839 					FormantModeler_setDataPointStatus (thee.get(), 1, ipoint, kDataModelerData::INVALID);
840 					FormantModeler_setDataPointValueAndStatus (thee.get(), 2, ipoint, f2, kDataModelerData::VALID);
841 					FormantModeler_setDataPointValueAndStatus (thee.get(), 3, ipoint, f3, kDataModelerData::VALID);
842 				}
843 			}
844 		}
845 		FormantModeler_fit (thee.get());
846 		return thee;
847 	} catch (MelderError) {
848 		Melder_throw (U"Cannot calculate track discontinuities");
849 	}
850 }
851 
FormantModeler_getStress(FormantModeler me,integer fromTrack,integer toTrack,integer numberOfParametersPerTrack,double power)852 double FormantModeler_getStress (FormantModeler me, integer fromTrack, integer toTrack, integer numberOfParametersPerTrack, double power) {
853 	checkTrackAutoRange (me, & fromTrack, & toTrack);
854 	integer numberOfFreeParameters;
855 	const double var = FormantModeler_getVarianceOfParameters (me, fromTrack, toTrack, 1, numberOfParametersPerTrack, & numberOfFreeParameters);
856 	double degreesOfFreedom;
857 	const double chisq = FormantModeler_getChiSquaredQ (me, fromTrack, toTrack, nullptr, & degreesOfFreedom);
858 	return ( isdefined (var) && isdefined (chisq) && numberOfFreeParameters > 0 && degreesOfFreedom >= 0.0 ?
859 		( sqrt (pow (var / numberOfFreeParameters, power) * (chisq / degreesOfFreedom))) :
860 		undefined );
861 }
862 
FormantModeler_getAverageDistanceBetweenTracks(FormantModeler me,integer track1,integer track2,int type)863 double FormantModeler_getAverageDistanceBetweenTracks (FormantModeler me, integer track1, integer track2, int type) {
864 	double diff = undefined;
865 	if (track1 == track2)
866 		return 0.0;
867 	if (track1 <= my trackmodelers.size && track2 <= my trackmodelers.size) {
868 		const DataModeler fi = my trackmodelers.at [track1];
869 		const DataModeler fj = my trackmodelers.at [track2];
870 		// fi and fj have equal number of data points
871 		integer numberOfDataPoints = 0;
872 		diff = 0.0;
873 		for (integer ipoint = 1; ipoint <= fi -> numberOfDataPoints; ipoint ++) {
874 			if (type != 0) {
875 				const double fie = fi -> f_evaluate (fi, fi -> data [ipoint] .x, fi -> parameters.get());
876 				const double fje = fj -> f_evaluate (fj, fj -> data [ipoint] .x, fj -> parameters.get());
877 				diff += fabs (fie - fje);
878 				numberOfDataPoints ++;
879 			} else if (fi -> data [ipoint] .status != kDataModelerData::INVALID && fj -> data [ipoint] .status != kDataModelerData::INVALID) {
880 				diff += fabs (fi -> data [ipoint] .y - fj -> data [ipoint] .y);
881 				numberOfDataPoints ++;
882 			}
883 		}
884 		diff /= numberOfDataPoints;
885 	}
886 	return diff;
887 }
888 
FormantModeler_getFormantsConstraintsFactor(FormantModeler me,double minF1,double maxF1,double minF2,double maxF2,double minF3)889 double FormantModeler_getFormantsConstraintsFactor (FormantModeler me, double minF1, double maxF1, double minF2, double maxF2, double minF3) {
890 	const double f1 = FormantModeler_getParameterValue (me, 1, 1); // trackmodelers -> item [1] -> parameter [1]
891 	const double minF1Factor = ( f1 > minF1 ? 1 : sqrt (minF1 - f1 + 1.0) );
892 	const double maxF1Factor = ( f1 < maxF1 ? 1 : sqrt (f1 - maxF1 + 1.0) );
893 	const double f2 = FormantModeler_getParameterValue (me, 2, 1); // trackmodelers -> item [2] -> parameter [1]
894 	const double minF2Factor = ( f2 > minF2 ? 1 : sqrt (minF2 - f2 + 1.0) );
895 	const double maxF2Factor = ( f2 < maxF2 ? 1 : sqrt (f2 - maxF2 + 1.0) );
896 	const double f3 = FormantModeler_getParameterValue (me, 3, 1); // trackmodelers -> item [3] -> parameter [1]
897 	const double minF3Factor = ( f3 > minF3 ? 1 : sqrt (minF3 - f3 + 1.0) );
898 	return minF1Factor * maxF1Factor * minF2Factor * maxF2Factor * minF3Factor;
899 }
900 
FormantModeler_reportChiSquared(FormantModeler me)901 void FormantModeler_reportChiSquared (FormantModeler me) {
902 	const integer numberOfTracks = my trackmodelers.size;
903 	double ndf = 0, probability;
904 	MelderInfo_writeLine (U"Chi squared tests for individual models of each of ", numberOfTracks, U" formant track:");
905 	MelderInfo_writeLine (( my weighFormants == kFormantModelerWeights::EQUAL_WEIGHTS ? U"Standard deviation is estimated from the data." :
906 		( my weighFormants == kFormantModelerWeights::ONE_OVER_BANDWIDTH ? U"\tBandwidths are used as estimate for local standard deviations." :
907 		( my weighFormants == kFormantModelerWeights::Q_FACTOR ? U"\t1/Q's are used as estimate for local standard deviations." :
908 		U"\tSquare root of bandwidths are used as estimate for local standard deviations." ) ) ));
909 	for (integer itrack = 1; itrack <= numberOfTracks; itrack ++) {
910 		const double chisq_f = FormantModeler_getChiSquaredQ (me, itrack, itrack, & probability, & ndf);
911 		MelderInfo_writeLine (U"Formant track ", itrack, U":");
912 		MelderInfo_writeLine (U"\tChi squared (F", itrack, U") = ", chisq_f);
913 		MelderInfo_writeLine (U"\tProbability (F", itrack, U") = ", probability);
914 		MelderInfo_writeLine (U"\tNumber of degrees of freedom (F", itrack, U") = ", ndf);
915 	}
916 	const double chisq = FormantModeler_getChiSquaredQ (me, 1, numberOfTracks, & probability, & ndf);
917 	MelderInfo_writeLine (U"Chi squared test for the complete model with ", numberOfTracks, U" formants:");
918 	MelderInfo_writeLine (U"\tChi squared = ", chisq);
919 	MelderInfo_writeLine (U"\tProbability = ", probability);
920 	MelderInfo_writeLine (U"\tNumber of degrees of freedom = ", ndf);
921 }
922 
Formants_getSmoothestInInterval(CollectionOf<structFormant> * me,double tmin,double tmax,integer numberOfFormantTracks,integer numberOfParametersPerTrack,kFormantModelerWeights weighData,bool useConstraints,double numberOfSigmas,double power,double minF1,double maxF1,double minF2,double maxF2,double minF3)923 integer Formants_getSmoothestInInterval (CollectionOf<structFormant>* me, double tmin, double tmax,
924 	integer numberOfFormantTracks, integer numberOfParametersPerTrack,
925 	kFormantModelerWeights weighData, bool useConstraints, double numberOfSigmas, double power,
926 	double minF1, double maxF1, double minF2, double maxF2, double minF3)
927 {
928 	try {
929 		const integer numberOfFormantObjects = my size;
930 		integer minNumberOfFormants = 1000000;
931 		if (numberOfFormantObjects == 1)
932 			return 1;
933 		autoINTVEC numberOfFormants = raw_INTVEC (numberOfFormantObjects);
934 		autoINTVEC invalid = zero_INTVEC (numberOfFormantObjects);
935 		double tminf = 0.0, tmaxf = 0.0;
936 		for (integer iobject = 1; iobject <= numberOfFormantObjects; iobject ++) {
937 			// Check that all Formants have the same domain
938 			const Formant fi = my at [iobject];
939 			if (tminf == tmaxf) {
940 				tminf = fi -> xmin;
941 				tmaxf = fi -> xmax;
942 			} else if (fi -> xmin != tminf || fi -> xmax != tmaxf) {
943 				Melder_throw (U"All Formant objects must have the same starting and finishing times.");
944 			}
945 			// Find the one that has least formant tracks
946 			numberOfFormants [iobject] = Formant_getMaxNumFormants (fi);
947 			if (numberOfFormants [iobject] < minNumberOfFormants)
948 				minNumberOfFormants = numberOfFormants [iobject];
949 		}
950 		if (numberOfFormantTracks == 0)  // default
951 			numberOfFormantTracks = minNumberOfFormants;
952 		if (numberOfFormantTracks > minNumberOfFormants) {
953 			// make formants with not enough tracks invalid for the competition
954 			integer numberOfInvalids = 0;
955 			for (integer iobject = 1; iobject <= numberOfFormantObjects; iobject ++) {
956 				if (numberOfFormants [iobject] < numberOfFormantTracks) {
957 					invalid [iobject] = 1;
958 					numberOfInvalids ++;
959 				}
960 			}
961 			Melder_require (numberOfInvalids < numberOfFormantObjects,
962 				U"None of the Formants has enough formant tracks. Please, lower your upper formant number.");
963 		}
964 		if (tmax <= tmin) {
965 			tmin = tminf;
966 			tmax = tmaxf;
967 		}
968 		Melder_require (tmin >= tminf && tmax <= tmaxf,
969 			U"The selected interval should be within the Formant object's domain.");
970 
971 		/* The chisq is not meaningfull as a the only test whether one model is better than the other because
972 			if we have two models 1 & 2 with the same data points (x1 [i]=x2 [i] and y1 [i]= y2 [i] but if
973 			sigma1 [i] < sigma2 [i] than chisq1 > chisq2.
974 			This is not what we want.
975 			We test therefore the variances of the parameters because if sigma1 [i] < sigma2 [i] than pvar1 < pvar2.
976 		 */
977 		double minChiVar = 1e308;
978 		integer index = 0;
979 		for (integer iobject = 1; iobject <= numberOfFormantObjects; iobject ++) {
980 			if (invalid [iobject] != 1) {
981 				const Formant fi = my at [iobject];
982 				autoFormantModeler fs = Formant_to_FormantModeler (fi, tmin, tmax, numberOfFormantTracks, numberOfParametersPerTrack);
983 				FormantModeler_setParameterValuesToZero (fs.get(), 1, numberOfFormantTracks, numberOfSigmas);
984 				const double cf = ( useConstraints ? FormantModeler_getFormantsConstraintsFactor (fs.get(), minF1, maxF1, minF2, maxF2, minF3) : 1.0 );
985 				const double chiVar = FormantModeler_getStress (fs.get(), 1, numberOfFormantTracks, numberOfParametersPerTrack, power);
986 				if (isdefined (chiVar) && cf * chiVar < minChiVar) {
987 					minChiVar = cf * chiVar;
988 					index = iobject;
989 				}
990 			}
991 		}
992 		return index;
993 	} catch (MelderError) {
994 		Melder_throw (U"No Formant object could be selected.");
995 	}
996 }
997 
Formants_extractSmoothestPart(CollectionOf<structFormant> * me,double tmin,double tmax,integer numberOfFormantTracks,integer numberOfParametersPerTrack,kFormantModelerWeights weighFormants,double numberOfSigmas,double power)998 autoFormant Formants_extractSmoothestPart (CollectionOf<structFormant>* me, double tmin, double tmax,
999 	integer numberOfFormantTracks, integer numberOfParametersPerTrack, kFormantModelerWeights weighFormants, double numberOfSigmas, double power) {
1000 	try {
1001 		const integer index = Formants_getSmoothestInInterval (me, tmin, tmax, numberOfFormantTracks, numberOfParametersPerTrack,
1002 			weighFormants, false, numberOfSigmas, power, 1.0, 1.0, 1.0, 1.0, 1.0); // last five are just fillers
1003 		const Formant bestfit = my at [index];
1004 		autoFormant thee = Formant_extractPart (bestfit, tmin, tmax);
1005 		return thee;
1006 	} catch (MelderError) {
1007 		Melder_throw (U"Smoothest Formant part could not be extracted.");
1008 	}
1009 }
1010 
Formants_extractSmoothestPart_withFormantsConstraints(CollectionOf<structFormant> * me,double tmin,double tmax,integer numberOfFormantTracks,integer numberOfParametersPerTrack,kFormantModelerWeights weighFormants,double numberOfSigmas,double power,double minF1,double maxF1,double minF2,double maxF2,double minF3)1011 autoFormant Formants_extractSmoothestPart_withFormantsConstraints (CollectionOf<structFormant>* me, double tmin, double tmax,
1012 	integer numberOfFormantTracks, integer numberOfParametersPerTrack, kFormantModelerWeights weighFormants,
1013 	double numberOfSigmas, double power, double minF1, double maxF1, double minF2, double maxF2, double minF3)
1014 {
1015 	try {
1016 		const integer index = Formants_getSmoothestInInterval (me, tmin, tmax, numberOfFormantTracks, numberOfParametersPerTrack, weighFormants, 1, numberOfSigmas, power, minF1, maxF1, minF2, maxF2, minF3);
1017 		const Formant bestfit = my at [index];
1018 		autoFormant thee = Formant_extractPart (bestfit, tmin, tmax);
1019 		return thee;
1020 	} catch (MelderError) {
1021 		Melder_throw (U"Smoothest Formant part could not be extracted.");
1022 	}
1023 }
1024 
Sound_getOptimalFormantCeiling(Sound me,double startTime,double endTime,double windowLength,double timeStep,double minFreq,double maxFreq,integer numberOfFrequencySteps,double preemphasisFrequency,integer numberOfFormantTracks,integer numberOfParametersPerTrack,kFormantModelerWeights weighFormants,double numberOfSigmas,double power)1025 double Sound_getOptimalFormantCeiling (Sound me, double startTime, double endTime,
1026 	double windowLength, double timeStep, double minFreq, double maxFreq, integer numberOfFrequencySteps,
1027 	double preemphasisFrequency, integer numberOfFormantTracks, integer numberOfParametersPerTrack, kFormantModelerWeights weighFormants,
1028 	double numberOfSigmas, double power)
1029 {
1030 	double optimalCeiling;
1031 	autoFormant thee = Sound_to_Formant_interval (me, startTime, endTime, windowLength, timeStep, minFreq, maxFreq,  numberOfFrequencySteps, preemphasisFrequency, numberOfFormantTracks, numberOfParametersPerTrack, weighFormants,  numberOfSigmas, power, false, 0.0, 5000.0, 0.0, 5000.0, 0.0, & optimalCeiling);
1032 	return optimalCeiling;
1033 }
1034 
Sound_to_Formant_interval(Sound me,double startTime,double endTime,double windowLength,double timeStep,double minFreq,double maxFreq,integer numberOfFrequencySteps,double preemphasisFrequency,integer numberOfFormantTracks,integer numberOfParametersPerTrack,kFormantModelerWeights weighFormants,double numberOfSigmas,double power,bool useConstraints,double minF1,double maxF1,double minF2,double maxF2,double minF3,double * out_optimalCeiling)1035 autoFormant Sound_to_Formant_interval (Sound me, double startTime, double endTime,
1036 	double windowLength, double timeStep, double minFreq, double maxFreq, integer numberOfFrequencySteps,
1037 	double preemphasisFrequency, integer numberOfFormantTracks, integer numberOfParametersPerTrack, kFormantModelerWeights weighFormants,
1038 	double numberOfSigmas, double power, bool useConstraints, double minF1, double maxF1, double minF2, double maxF2, double minF3,
1039 	double *out_optimalCeiling)
1040 {
1041 	try {
1042 		// parameter check
1043 		Function_unidirectionalAutowindow (me, & startTime, & endTime);
1044 		const double nyquistFrequency = 0.5 / my dx;
1045 		Melder_require (maxFreq <= nyquistFrequency,
1046 			U"The upper value of the maximum frequency range should not exceed the Nyquist frequency of the sound.");
1047 		autoINTVEC noPararametersPerTrack = newINTVECasNumbers (numberOfFormantTracks, numberOfParametersPerTrack);
1048 		double df = 0, mincriterium = 1e28;
1049 		if (minFreq >= maxFreq)
1050 			numberOfFrequencySteps = 1;
1051 		else
1052 			df = (maxFreq - minFreq) / (numberOfFrequencySteps - 1);
1053 
1054 		double optimalCeiling = minFreq;
1055 		integer istep_best = 0;
1056 
1057 		// extract part +- windowLength because of Gaussian windowing in the formant analysis
1058 		// +timeStep/2 to have the analysis points maximally spread in the new domain.
1059 
1060 		autoSound part = Sound_extractPart (me, startTime - windowLength + timeStep / 2.0, endTime + windowLength + timeStep / 2.0, kSound_windowShape::RECTANGULAR, 1, 1);
1061 
1062 		// Resample to 2*maxFreq to reduce resampling load in Sound_to_Formant
1063 		autoSound resampled = Sound_resample (part.get(), 2.0 * maxFreq, 50);
1064 		OrderedOf<structFormant> formants;
1065 		Melder_progressOff ();
1066 		for (integer istep = 1; istep <= numberOfFrequencySteps; istep ++) {
1067 			const double currentCeiling = minFreq + (istep - 1) * df;
1068 			autoFormant formant = Sound_to_Formant_burg (resampled.get(), timeStep, 5.0, currentCeiling, windowLength, preemphasisFrequency);
1069 			autoFormantModeler fm = Formant_to_FormantModeler (formant.get(), startTime, endTime, noPararametersPerTrack.get());
1070 			//TODO FormantModeler_setFormantWeighting (me, weighFormants);
1071 			FormantModeler_setParameterValuesToZero (fm.get(), 1, numberOfFormantTracks, numberOfSigmas);
1072 			formants. addItem_move (formant.move());
1073 			const double cf = ( useConstraints ? FormantModeler_getFormantsConstraintsFactor (fm.get(), minF1, maxF1, minF2, maxF2, minF3) : 1.0 );
1074 			const double chiVar = FormantModeler_getStress (fm.get(), 1, numberOfFormantTracks, numberOfParametersPerTrack, power);
1075 			const double criterium = chiVar * cf;
1076 			if (isdefined (chiVar) && criterium < mincriterium) {
1077 				mincriterium = criterium;
1078 				optimalCeiling = currentCeiling;
1079 				istep_best = istep;
1080 			}
1081 		}
1082 		Melder_require (istep_best > 0,
1083 			U"No optimal ceiling found.");
1084 		autoFormant thee = Formant_extractPart (formants.at [istep_best], startTime, endTime);
1085 		Melder_progressOn ();
1086 		if (out_optimalCeiling)
1087 			*out_optimalCeiling = optimalCeiling;
1088 		return thee;
1089 	} catch (MelderError) {
1090 		Melder_throw (U"No Formant object created.");
1091 	}
1092 }
1093 
Sound_to_Formant_interval_robust(Sound me,double startTime,double endTime,double windowLength,double timeStep,double minFreq,double maxFreq,integer numberOfFrequencySteps,double preemphasisFrequency,integer numberOfFormantTracks,integer numberOfParametersPerTrack,kFormantModelerWeights weighFormants,double numberOfSigmas,double power,bool useConstraints,double minF1,double maxF1,double minF2,double maxF2,double minF3,double * out_optimalCeiling)1094 autoFormant Sound_to_Formant_interval_robust (Sound me, double startTime, double endTime,
1095 	double windowLength, double timeStep, double minFreq, double maxFreq, integer numberOfFrequencySteps,
1096 	double preemphasisFrequency, integer numberOfFormantTracks, integer numberOfParametersPerTrack, kFormantModelerWeights weighFormants,
1097 	double numberOfSigmas, double power, bool useConstraints, double minF1, double maxF1, double minF2, double maxF2, double minF3,
1098 	double *out_optimalCeiling)
1099 {
1100 	try {
1101 		if (endTime <= startTime) {
1102 			startTime = my xmin;
1103 			endTime = my xmax;
1104 		}
1105 		const double nyquistFrequency = 0.5 / my dx;
1106 		Melder_require (maxFreq <= nyquistFrequency,
1107 			U"The upper value of the maximum frequency range should not exceed the Nyquist frequency of the sound.");
1108 		double df = 0, mincriterium = 1e28;
1109 		if (minFreq >= maxFreq)
1110 			numberOfFrequencySteps = 1;
1111 		else
1112 			df = (maxFreq - minFreq) / (numberOfFrequencySteps - 1);
1113 
1114 		autoINTVEC noPararametersPerTrack = newINTVECasNumbers (numberOfFormantTracks, numberOfParametersPerTrack);
1115 
1116 		integer istep_best = 0;
1117 		double optimalCeiling = minFreq;
1118 		// extract part +- windowLength because of Gaussian windowing in the formant analysis
1119 		// +timeStep/2 to have the analysis points maximally spread in the new domain.
1120 
1121 		autoSound part = Sound_extractPart (me, startTime - windowLength + timeStep / 2.0, endTime + windowLength + timeStep / 2.0,
1122 				kSound_windowShape::RECTANGULAR, 1, true);
1123 
1124 		/*
1125 			Resample to 2*maxFreq to reduce resampling load in Sound_to_Formant.
1126 		*/
1127 		autoSound resampled = Sound_resample (part.get(), 2.0 * maxFreq, 50);
1128 
1129 		OrderedOf<structFormant> formants;
1130 		Melder_progressOff ();
1131 		for (integer istep = 1; istep <= numberOfFrequencySteps; istep ++) {
1132 			const double currentCeiling = minFreq + (istep - 1) * df;
1133 			autoFormant formant = Sound_to_Formant_robust (resampled.get(), timeStep, 5.0,
1134 					currentCeiling, windowLength, preemphasisFrequency, 50.0, 1.5, 3, 0.0000001, true);
1135 			autoFormantModeler fm = Formant_to_FormantModeler (formant.get(), startTime, endTime, noPararametersPerTrack.get());
1136 			// TODO set weighing
1137 			FormantModeler_setParameterValuesToZero (fm.get(), 1, numberOfFormantTracks, numberOfSigmas);
1138 			formants. addItem_move (formant.move());
1139 			const double cf = ( useConstraints ? FormantModeler_getFormantsConstraintsFactor (fm.get(), minF1, maxF1, minF2, maxF2, minF3) : 1.0 );
1140 			const double chiVar = FormantModeler_getStress (fm.get(), 1, numberOfFormantTracks, numberOfParametersPerTrack, power);
1141 			const double criterium = chiVar * cf;
1142 			if (isdefined (chiVar) && criterium < mincriterium) {
1143 				mincriterium = criterium;
1144 				optimalCeiling = currentCeiling;
1145 				istep_best = istep;
1146 			}
1147 		}
1148 		Melder_require (istep_best > 0,
1149 			U"No optimal ceiling found.");
1150 		autoFormant thee = Formant_extractPart (formants.at [istep_best], startTime, endTime);
1151 		Melder_progressOn ();
1152 		if (out_optimalCeiling)
1153 			*out_optimalCeiling = optimalCeiling;
1154 		return thee;
1155 	} catch (MelderError) {
1156 		Melder_throw (U"No Formant object created.");
1157 	}
1158 }
1159 
1160 #if 0
1161 // If e.g. first formant is obviously "missing" then assign F1 as
1162 static void FormantModeler_correctFormantsProbablyIndexedFalsely (FormantModeler /* me */) {
1163 	/* if shift down F1 ("correct" F1 missed)
1164 	 * elsif shift down F2  ("correct" F2 missed)
1165 	 * else if spurious formant before F1
1166 	 * else if spurious formant between F1 and F2
1167 	 * endif
1168 	 * */
1169 }
1170 #endif
1171 
Sound_to_OptimalCeilingTier(Sound me,double windowLength,double timeStep,double minCeiling,double maxCeiling,integer numberOfFrequencySteps,double preemphasisFrequency,double smoothingWindow,integer numberOfFormantTracks,integer numberOfParametersPerTrack,kFormantModelerWeights weighFormants,double numberOfSigmas,double power)1172 autoOptimalCeilingTier Sound_to_OptimalCeilingTier (Sound me,
1173 	double windowLength, double timeStep, double minCeiling, double maxCeiling, integer numberOfFrequencySteps,
1174 	double preemphasisFrequency, double smoothingWindow, integer numberOfFormantTracks, integer numberOfParametersPerTrack, kFormantModelerWeights weighFormants, double numberOfSigmas, double power) {
1175 	try {
1176 		OrderedOf<structFormant> formants;
1177 		const double frequencyStep = ( numberOfFrequencySteps > 1 ? (maxCeiling - minCeiling) / (numberOfFrequencySteps - 1) : 0.0 );
1178 		for (integer i = 1; i <= numberOfFrequencySteps; i ++) {
1179 			const double ceiling = minCeiling + (i - 1) * frequencyStep;
1180 			autoFormant formant = Sound_to_Formant_burg (me, timeStep, 5, ceiling, windowLength, preemphasisFrequency);
1181 			formants. addItem_move (formant.move());
1182 		}
1183 		integer numberOfFrames;
1184 		double firstTime;
1185 		const double modellingTimeStep = timeStep;
1186 		autoOptimalCeilingTier octier = OptimalCeilingTier_create (my xmin, my xmax);
1187 		Sampled_shortTermAnalysis (me, smoothingWindow, modellingTimeStep, & numberOfFrames, & firstTime);
1188 		for (integer iframe = 1; iframe <= numberOfFrames; iframe ++) {
1189 			const double time = firstTime + (iframe - 1) * modellingTimeStep;
1190 			const double tmin = time - smoothingWindow / 2.0;
1191 			const double tmax = tmin + smoothingWindow;
1192 			const integer index = Formants_getSmoothestInInterval (& formants, tmin, tmax, numberOfFormantTracks, numberOfParametersPerTrack,	weighFormants,
1193 				false, numberOfSigmas, power, 200.0, 1500.0, 300.0, 3000.0, 1000.0);   // min/max values are not used
1194 			const double ceiling = minCeiling + (index - 1) * frequencyStep;
1195 			RealTier_addPoint (octier.get(), time, ceiling);
1196 		}
1197 		return octier;
1198 	} catch (MelderError) {
1199 		Melder_throw (me, U" no OptimalCeilingTier calculated.");
1200 	}
1201 }
1202 
1203 /* End of file FormantModeler.cpp */
1204