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