1 /* KlattGrid.cpp
2  *
3  * Copyright (C) 2008-2020 David Weenink, 2015,2017 Paul Boersma
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  * along with this work. If not, see <http://www.gnu.org/licenses/>.
17  */
18 /*
19   djmw 20080917 Initial version
20   djmw 20090109 Add formulas for formant frequencies and bandwidths.
21   djmw 20090123 Add PlayOptions.
22   djmw 20090129 KlattGrid_draw text in boxes displays better
23   djmw 20090311 Add RealTier_valuesInRange
24   djmw 20090312 Add klattGrid_addFormantAndBandwidthTier
25   djmw 20090326 Changed DBSPL_to_A into DB_to_A for bypass and formant amplitudes.
26   djmw 20100223 Removed gsl dependency
27   djmw 20110304 Thing_new
28   djmw 20110308 struc connections -> struct structconnections
29   djmw 20110329 Table_get(Numeric|String)Value is now Table_get(Numeric|String)Value_Assert
30   djmw 20111011 Sound_VocalTractGrid_CouplingGrid_filter_cascade: group warnings
31 */
32 
33 #include "FormantGrid_extensions.h"
34 #include "Formula.h"
35 #include "KlattGrid.h"
36 #include "KlattTable.h"
37 #include "Resonator.h"
38 #include "Pitch_to_PitchTier.h"
39 #include "PitchTier_to_Sound.h"
40 #include "PitchTier_to_PointProcess.h"
41 #include "NUM2.h"
42 #include "Sound_to_Formant.h"
43 #include "Sound_to_Intensity.h"
44 #include "Sound_to_Pitch.h"
45 
46 #include "oo_DESTROY.h"
47 #include "KlattGrid_def.h"
48 #include "oo_COPY.h"
49 #include "KlattGrid_def.h"
50 #include "oo_EQUAL.h"
51 #include "KlattGrid_def.h"
52 #include "oo_CAN_WRITE_AS_ENCODING.h"
53 #include "KlattGrid_def.h"
54 #include "oo_WRITE_TEXT.h"
55 #include "KlattGrid_def.h"
56 #include "oo_WRITE_BINARY.h"
57 #include "KlattGrid_def.h"
58 #include "oo_READ_TEXT.h"
59 #include "KlattGrid_def.h"
60 #include "oo_READ_BINARY.h"
61 #include "KlattGrid_def.h"
62 #include "oo_DESCRIPTION.h"
63 #include "KlattGrid_def.h"
64 
65 
66 #include "enums_getText.h"
67 #include "KlattGrid_enums.h"
68 #include "enums_getValue.h"
69 #include "KlattGrid_enums.h"
70 
71 /*
72  * A KlattGrid consists of a great many tiers that can be independently modified.
73  *
74  * For any particular formant, the formant frequency tier and the formant bandwidth tier can only be added or removed jointly
getStreamSizes()75  * because they are part of a FormantGrid object. There will always be an equal number of formant frequency tiers and
76  * formant bandwidth tiers.
77  *
78  * For parallel synthesis we also need, besides the frequency and bandwidth tier, an additional amplitude tier for each formant.
79  * It is not necessary that there are an equal number of formant frequency tiers (nf) and amplitude tiers (na).
80  * During parallel synthesis we simply synthesize with min(nf,na) number of formants.
81  * These numbers nf and na can get out of sync because of the following (add, remove, replace) actions:
82  * 	- we replace a FormantGrid that has not the same number of tiers as the corresponding number of amplitude tiers
83  * 	- we remove/add a formant tier and a bandwidth tier together and not the corresponding amplitude tier
84  * 	- we remove/add an amplitude tier and not the corresponding formant&bandwidth tiers
85  *
86  * As of 20130113 the KlattGrid_addFormant (/remove) which also added automatically an amplitude tier has been split into two explicit actions
87  *	KlattGrid_addFormantFrequencyAndBandwidth (/remove)
88  *	KlattGrid_addFormantAmplitudeTier (/remove)
89  *
90  */
91 
92 conststring32 KlattGrid_getFormantName (kKlattGridFormantType formantType) {
93 	conststring32 result;
94 	if (formantType == kKlattGridFormantType::ORAL)
95 		result = U"Oral formant";
96 	else if (formantType == kKlattGridFormantType::NASAL)
97 		result = U"Nasal formant";
98 	else if (formantType == kKlattGridFormantType::FRICATION)
99 		result = U"Frication Formant";
100 	else if (formantType == kKlattGridFormantType::TRACHEAL)
101 		result = U"Tracheal formant";
102 	else if (formantType == kKlattGridFormantType::NASAL_ANTI)
103 		result = U"Nasal Antiformant";
104 	else if (formantType == kKlattGridFormantType::TRACHEALANTI)
105 		result = U"Tracheal antiformant";
106 	else if (formantType == kKlattGridFormantType::DELTA)
107 		result = U"Delta formant";
108 	else
109 		result = U"Unknown formant";
110 	return result;
111 }
112 
113 static const conststring32 formant_names [] = { U"", U"oral ", U"nasal ", U"frication ", U"tracheal ", U"nasal anti", U"tracheal anti", U"delta "};
114 
115 #define KlattGrid_OPENPHASE_DEFAULT 0.7
116 #define KlattGrid_POWER1_DEFAULT 3
117 #define KlattGrid_POWER2_DEFAULT (KlattGrid_POWER1_DEFAULT+1)
118 
119 /*	Amplitude scaling: maximum amplitude (-1,+1) corresponds to 91 dB */
120 
121 /*static double NUMinterpolateLinear (double x1, double y1, double x2, double y2, double x)
122 {
123 	if (y1 == y2) return y1;
124 	if (x1 == x2) return undefined;
125 	return (y2 - y1) * (x - x1) / (x2 - x1) + y1;
126 }*/
127 
128 static void rel_to_abs (double *w, double *ws, integer n, double d) {
129 	double sum = 0.0;
130 	for (integer i = 1; i <= n; i ++) { // relative
131 		sum += w [i];
132 	}
133 	if (sum != 0.0) {
134 		double dw = d / sum;
135 		sum = 0.0;
136 		for (integer i = 1; i <= n; i ++) { // to absolute
137 			w [i] *= dw;
138 			sum += w [i];
139 			ws [i] = sum;
140 		}
141 	}
142 }
143 
144 static bool RealTier_valuesInRange (RealTier me, double min, double max) {
145 	for (integer i = 1; i <= my points.size; i ++) {
146 		const RealPoint p = my points.at [i];
147 		if (isdefined (min) && p -> value < min)
148 			return false;
149 		if (isdefined (max) && p -> value < max)
150 			return false;
151 	}
152 	return true;
153 }
154 
155 static double PointProcess_getPeriodAtIndex (PointProcess me, integer it, double maximumPeriod) {
156 	double period = undefined;
157 	if (it >= 2) {
158 		period = my t [it] - my t [it - 1];
159 		if (period > maximumPeriod)
160 			period = undefined;
161 	}
162 	if (isundef (period)) {
163 		if (it < my nt) {
164 			period = my t [it + 1] - my t [it];
165 			if (period > maximumPeriod)
166 				period = undefined;
167 		}
168 	}
169 	// undefined can only occur for a single isolated pulse.
170 	return period;
171 }
172 
173 #define UPDATE_TIER \
174 	RealTier_addPoint (thee.get(), mytime, myvalue); \
175 	lasttime = mytime; \
176 	myindex ++; \
177 	if (myindex <= numberOfValues) { \
178 		mypoint = my points.at [myindex]; \
179 		mytime = mypoint -> number; \
180 		myvalue = mypoint -> value;\
181 	} else { \
182 		mytime = my xmax; \
183 	}
184 
185 static autoRealTier RealTier_updateWithDelta (RealTier me, RealTier delta, PhonationTier glottis, double openglottis_fadeFraction) {
186 	try {
187 		integer myindex = 1;
188 		RealPoint mypoint = my points.at [myindex];
189 		const integer numberOfValues = my points.size;
190 		double mytime = mypoint -> number;
191 		double myvalue = mypoint -> value;
192 		double lasttime = my xmin - 0.001;   // sometime before xmin
193 		autoRealTier thee = RealTier_create (my xmin, my xmax);
194 
195 		if (openglottis_fadeFraction <= 0.0)
196 			openglottis_fadeFraction = 0.0001;
197 		if (openglottis_fadeFraction >= 0.5)
198 			openglottis_fadeFraction = 0.4999;
199 		for (integer ipoint = 1; ipoint <= glottis -> points.size; ipoint ++) {
200 			const PhonationPoint point = glottis -> points.at [ipoint];
201 			const double t4 = point -> number;   // glottis closing
202 			const double openDuration = point -> te;
203 			const double t1 = t4 - openDuration;
204 			const double t2 = t1 + openglottis_fadeFraction * openDuration;
205 			const double t3 = t4 - openglottis_fadeFraction * openDuration;
206 			/*
207 				Add my points that lie before t1 and after previous t4.
208 			*/
209 			while (mytime > lasttime && mytime < t1) {
210 				UPDATE_TIER
211 			}
212 			if (t2 > t1) {
213 				// Set new value at t1
214 				const double myvalue1 = RealTier_getValueAtTime (me, t1);
215 				RealTier_addPoint (thee.get(), t1, myvalue1);
216 				// Add my points between t1 and t2
217 				while (mytime > lasttime && mytime < t2) {
218 					const double dvalue = RealTier_getValueAtTime (delta, mytime);
219 					if (isdefined (dvalue)) {
220 						const double fraction = (mytime - t1) / (openglottis_fadeFraction * openDuration);
221 						myvalue += dvalue * fraction;
222 					}
223 					UPDATE_TIER
224 				}
225 			}
226 
227 			double myvalue2 = RealTier_getValueAtTime (me, t2);
228 			double dvalue = RealTier_getValueAtTime (delta, t2);
229 			if (isdefined (dvalue))
230 				myvalue2 += dvalue;
231 			RealTier_addPoint (thee.get(), t2, myvalue2);
232 
233 			// Add points between t2 and t3
234 
235 			while (mytime > lasttime && mytime < t3) {
236 				dvalue = RealTier_getValueAtTime (delta, mytime);
237 				if (isdefined (dvalue))
238 					myvalue += dvalue;
239 				UPDATE_TIER
240 			}
241 
242 			// set new value at t3
243 
244 			double myvalue3 = RealTier_getValueAtTime (me, t3);
245 			dvalue = RealTier_getValueAtTime (delta, t3);
246 			if (isdefined (dvalue)) {
247 				myvalue3 += dvalue;
248 			}
249 			RealTier_addPoint (thee.get(), t3, myvalue3);
250 
251 			if (t4 > t3) {
252 				// Add my points between t3 and t4
253 				while (mytime > lasttime && mytime < t4) {
254 					dvalue = RealTier_getValueAtTime (delta, mytime);
255 					if (isdefined (dvalue)) {
256 						const double fraction = 1 - (mytime - t3) / (openglottis_fadeFraction * openDuration);
257 						myvalue += dvalue * fraction;
258 					}
259 					UPDATE_TIER
260 				}
261 
262 				// Set new value at t4
263 				const double myvalue4 = RealTier_getValueAtTime (me, t4);
264 				RealTier_addPoint (thee.get(), t4, myvalue4);
265 			}
266 		}
267 		return thee;
268 	} catch (MelderError) {
269 		Melder_throw (me, U": not updated with delta.");
270 	}
271 }
272 
273 static bool FormantGrid_isFormantDefined (FormantGrid me, integer iformant) {
274 	// formant and bandwidth are always in sync
275 	const RealTier ftier = my formants.at [iformant];
276 	const RealTier btier = my bandwidths.at [iformant];
277 	return ftier -> points.size > 0 and btier -> points.size > 0;
278 }
279 
280 static bool FormantGrid_Intensities_isFormantDefined (FormantGrid me, OrderedOf<structIntensityTier>* thee, integer iformant) {
281 	bool exists = false;
282 	if (iformant <= my formants.size && iformant <= my bandwidths.size && iformant <= thee->size) {
283 		const RealTier ftier = my formants.at [iformant];
284 		const RealTier btier = my bandwidths.at [iformant];
285 		const IntensityTier atier = thy at [iformant];
286 		exists = ( ftier -> points.size > 0 && btier -> points.size > 0 && atier -> points.size > 0 );
287 	}
288 	return exists;
289 }
290 
291 static void check_formants (integer numberOfFormants, integer *ifb, integer *ife) {
292 	if (numberOfFormants <= 0 || *ifb > numberOfFormants || *ife < *ifb || *ife < 1) {
293 		*ife = 0;  // overrules everything *ifb value is a don't care now
294 		return;
295 	}
296 	if (*ifb <= 1)
297 		*ifb = 1;
298 	if (*ife > numberOfFormants)
299 		*ife = numberOfFormants;
300 }
301 
302 static autoSound Sound_createEmptyMono (double xmin, double xmax, double samplingFrequency) {
303 	const integer nt = Melder_iceiling ((xmax - xmin) * samplingFrequency);
304 	const double dt = 1.0 / samplingFrequency;
305 	const double tmid = (xmin + xmax) / 2.0;
306 	const double t1 = tmid - 0.5 * (nt - 1) * dt;
307 
308 	return Sound_create (1, xmin, xmax, nt, dt, t1);
309 }
310 
311 static void _Sounds_add_inplace (Sound me, Sound thee) {
312 	for (integer i = 1; i <= my nx; i ++)
313 		my z [1] [i] += thy z [1] [i];
314 }
315 
316 static autoSound _Sound_diff (Sound me, int scale) {
317 	try {
318 		autoSound thee = Data_copy (me);
319 
320 		// extremum
321 		double amax1 = -1.0e34, amax2 = amax1, val, pval = 0.0;
322 		if (scale) {
323 			for (integer i = 1; i <= thy nx; i ++) {
324 				val = fabs (thy z [1] [i]);
325 				if (val > amax1)
326 					amax1 = val;
327 			}
328 		}
329 		// x [n]-x [n-1]
330 		for (integer i = 1; i <= thy nx; i ++) {
331 			val = thy z [1] [i];
332 			thy z [1] [i] -=  pval;
333 			pval = val;
334 		}
335 		if (scale) {
336 			for (integer i = 1; i <= thy nx; i ++) {
337 				val = fabs (thy z [1] [i]);
338 				if (val > amax2)
339 					amax2 = val;
340 			}
341 			// scale
342 			for (integer i = 1; i <= thy nx; i ++)
343 				thy z [1] [i] *= amax1 / amax2;
344 		}
345 		return thee;
346 	} catch (MelderError) {
347 		Melder_throw (me, U": not differenced.");
348 	}
349 }
350 
351 /*static void _Sounds_addDifferentiated_inplace (Sound me, Sound thee)
352 {
353 	double pval = 0, dx = my dx;
354 	for (integer i = 1; i <= my nx; i ++)
355 	{
356 		const double val =  thy z [1] [i];
357 		my z [1] [i] += (val - pval) / dx; // dx makes amplitude of dz/dt independent of sampling.
358 		pval = val;
359 	}
360 }*/
361 
362 typedef struct structconnections {
363 	integer numberOfConnections;
364 	autoVEC x, y;
365 } *connections;
366 
367 static void connections_free (connections me) {
368 	if (! me)
369 		return;
370 	Melder_free (me);
371 }
372 
373 static connections connections_create (integer numberOfConnections) {
374 	connections me = 0;
375 	try {
376 		me = (connections) Melder_calloc (structconnections, 1);
377 		my numberOfConnections = numberOfConnections;
378 		my x = zero_VEC (numberOfConnections);
379 		my y = zero_VEC (numberOfConnections);
380 		return me;
381 	} catch (MelderError) {
382 		connections_free (me);
383 		Melder_throw (U"Connections not created.");
384 	}
385 }
386 
387 // Calculates the intersection point (xi,yi) of a line with a circle.
388 // The line starts at the origin and P (xp, yp) is on that line.
389 static void NUMcircle_radial_intersection_sq (double x, double y, double r, double xp, double yp, double *xi, double *yi) {
390 	double dx = xp - x, dy = yp - y;
391 	const double d = hypot (dx, dy);
392 	if (d > 0) {
393 		*xi = x + dx * r / d;
394 		*yi = y + dy * r / d;
395 	} else {
396 		*xi = *yi = undefined;
397 	}
398 }
399 
400 static void summer_draw (Graphics g, double x, double y, double r, bool alternating) {
401 	Graphics_setLineWidth (g, 2);
402 	Graphics_circle (g, x, y, r);
403 	const double dy = 3.0 * r / 4.0;
404 	// + symbol
405 	if (alternating)
406 		y += r / 4.0;
407 	Graphics_line (g, x, y + r / 2.0, x, y - r / 2.0);
408 	Graphics_line (g, x - r / 2.0, y, x + r / 2.0, y);
409 	if (alternating)
410 		Graphics_line (g, x - r / 2.0, y - dy , x + r / 2.0, y - dy);
411 }
412 
413 static void _summer_drawConnections (Graphics g, double x, double y, double r, connections thee, bool arrow, bool alternating, double horizontalFraction) {
414 	summer_draw (g, x, y, r, alternating);
415 
416 	for (integer i = 1; i <= thy numberOfConnections; i ++) {
417 		const double yp = thy y [i];
418 		double xto, yto, xp = thy x [i];
419 		if (horizontalFraction > 0) {
420 			const double dx = x - xp;
421 			if (dx > 0) {
422 				xp += horizontalFraction * dx;
423 				Graphics_line (g, thy x [i], yp, xp, yp);
424 			}
425 		}
426 		NUMcircle_radial_intersection_sq (x, y, r, xp, yp, & xto, & yto);
427 		if (isundef (xto) || isundef (yto))
428 			continue;
429 		if (arrow)
430 			Graphics_arrow (g, xp, yp, xto, yto);
431 		else
432 			Graphics_line (g, xp, yp, xto, yto);
433 	}
434 }
435 
436 static void summer_drawConnections (Graphics g, double x, double y, double r, connections thee, bool arrow, double horizontalFraction) {
437 	_summer_drawConnections (g, x, y, r, thee, arrow, false, horizontalFraction);
438 }
439 
440 static void alternatingSummer_drawConnections (Graphics g, double x, double y, double r, connections thee, bool arrow, double horizontalFraction) {
441 	_summer_drawConnections (g, x, y, r, thee, arrow, true, horizontalFraction);
442 }
443 
444 static void draw_oneSection (Graphics g, double xmin, double xmax, double ymin, double ymax,
445 	conststring32 line1, conststring32 line2, conststring32 line3)
446 {
447 	Graphics_rectangle (g, xmin, xmax, ymin, ymax);
448 	integer numberOfTextLines = 0;
449 	if (line1)
450 		numberOfTextLines ++;
451 	if (line2)
452 		numberOfTextLines ++;
453 	if (line3)
454 		numberOfTextLines ++;
455 	const double dy = (ymax - ymin) / (numberOfTextLines + 1), ddy = dy / 10.0;
456 	const double x = (xmax + xmin) / 2.0;
457 	double y = ymax;
458 	integer iline = 0;
459 	if (line1) {
460 		iline ++;
461 		y -= dy - ( numberOfTextLines == 2 ? ddy : 0.0 ); // extra spacing for two lines
462 		Graphics_text (g, x, y, line1);
463 	}
464 	if (line2) {
465 		iline ++;
466 		y -= dy - ( numberOfTextLines == 2 ? ( iline == 1 ? ddy : -iline * ddy ) : 0.0 );
467 		Graphics_text (g, x, y, line2);
468 	}
469 	if (line3) {
470 		iline ++;
471 		y -= dy - ( numberOfTextLines == 2 ? -iline * ddy : 0.0 );
472 		Graphics_text (g, x, y, line3);
473 	}
474 }
475 
476 // Maximum amplitue (-1,1) at 93.97940008672037 dB
477 #define DBSPL_to_A(x) (pow (10.0, x / 20.0) * 2.0e-5)
478 // Normal dB's
479 #define DB_to_A(x) (pow (10.0, x / 20.0))
480 
481 /************************ Sound & FormantGrid *********************************************/
482 
483 static void _Sound_FormantGrid_filterWithOneFormant_inplace (Sound me, FormantGrid thee, integer iformant, bool antiformant) {
484 	if (iformant < 1 || iformant > thy formants.size) {
485 		Melder_warning (U"Formant ", iformant, U" does not exist.");
486 		return;
487 	}
488 	const RealTier ftier = thy formants.at [iformant];
489 	const RealTier btier = thy bandwidths.at [iformant];
490 	if (ftier -> points.size == 0 && btier -> points.size == 0)
491 		return;
492 	Melder_require (ftier -> points.size != 0 && btier -> points.size != 0,
493 		U"Tier should not be empty,");
494 	const double nyquist = 0.5 / my dx;
495 	autoFilter r;
496 	if (antiformant)
497 		r = AntiResonator_create (my dx);
498 	else
499 		r = Resonator_create (my dx, true);
500 
501 	for (integer is = 1; is <= my nx; is ++) {
502 		const double t = my x1 + (is - 1) * my dx;
503 		const double f = RealTier_getValueAtTime (ftier, t);
504 		const double b = RealTier_getValueAtTime (btier, t);
505 		if (f <= nyquist && isdefined (b))
506 			Filter_setCoefficients (r.get(), f, b);
507 		my z [1] [is] = Filter_getOutput (r.get(), my z [1] [is]);
508 	}
509 }
510 
511 void Sound_FormantGrid_filterWithOneAntiFormant_inplace (Sound me, FormantGrid thee, integer iformant) {
512 	_Sound_FormantGrid_filterWithOneFormant_inplace (me, thee, iformant, true);
513 }
514 
515 void Sound_FormantGrid_filterWithOneFormant_inplace (Sound me, FormantGrid thee, integer iformant) {
516 	_Sound_FormantGrid_filterWithOneFormant_inplace (me, thee, iformant, false);
517 }
518 
519 void Sound_FormantGrid_Intensities_filterWithOneFormant_inplace (Sound me, FormantGrid thee, OrderedOf<structIntensityTier>* amplitudes, integer iformant) {
520 	try {
521 		Melder_require (iformant > 0 && iformant <= thy formants.size, U"Formant ", iformant, U" not defined.");
522 
523 		const double nyquist = 0.5 / my dx;
524 
525 		const RealTier ftier = thy formants.at [iformant];
526 		const RealTier btier = thy bandwidths.at [iformant];
527 		const IntensityTier atier = amplitudes->at [iformant];
528 
529 		if (ftier -> points.size == 0 || btier -> points.size == 0 || atier -> points.size == 0)
530 			return;    // nothing to do
531 		autoResonator r = Resonator_create (my dx, false);
532 		for (integer is = 1; is <= my nx; is ++) {
533 			const double t = my x1 + (is - 1) * my dx;
534 			const double f = RealTier_getValueAtTime (ftier, t);
535 			const double b = RealTier_getValueAtTime (btier, t);
536 			if (f <= nyquist && isdefined (b)) {
537 				Filter_setCoefficients (r.get(), f, b);
538 				const double a = RealTier_getValueAtTime (atier, t);
539 				if (isdefined (a))
540 					r -> a *= DB_to_A (a);
541 			}
542 			my z [1] [is] = Filter_getOutput (r.get(), my z [1] [is]);
543 		}
544 	} catch (MelderError) {
545 		Melder_throw (me, U": not filtered with one formant filter.");
546 	}
547 }
548 
549 autoSound Sound_FormantGrid_Intensities_filter (Sound me, FormantGrid thee, OrderedOf<structIntensityTier>* amplitudes, integer iformantb, integer iformante, int alternatingSign) {
550 	try {
551 		if (iformantb > iformante) {
552 			iformantb = 1;
553 			iformante = thy formants.size;
554 		}
555 		Melder_require (iformantb > 0 && iformantb <= thy formants.size ,
556 			U"From formant ", iformantb, U" not defined.");
557 		Melder_require (iformante > 0 && iformante <= thy formants.size ,
558 			U"To formant ", iformante, U" not defined.");
559 
560 		autoSound him = Sound_create (my ny, my xmin, my xmax, my nx, my dx, my x1);
561 
562 		for (integer iformant = iformantb; iformant <= iformante; iformant ++) {
563 			if (FormantGrid_Intensities_isFormantDefined (thee, amplitudes, iformant)) {
564 				autoSound tmp = Data_copy (me);
565 				Sound_FormantGrid_Intensities_filterWithOneFormant_inplace (tmp.get(), thee, amplitudes, iformant);
566 				for (integer is = 1; is <= my nx; is ++)
567 					his z [1] [is] += ( alternatingSign >= 0 ? tmp -> z [1] [is] : - tmp -> z [1] [is] );
568 				if (alternatingSign != 0)
569 					alternatingSign = - alternatingSign;
570 			}
571 		}
572 		return him;
573 	} catch (MelderError) {
574 		Melder_throw (me, U": not filtered.");
575 	}
576 }
577 
578 /********************* PhonationTier ************************/
579 
580 Thing_implement (PhonationPoint, AnyPoint, 0);
581 
582 autoPhonationPoint PhonationPoint_create (double time, double period, double openPhase, double collisionPhase, double te, double power1, double power2, double pulseScale) {
583 	try {
584 		autoPhonationPoint me = Thing_new (PhonationPoint);
585 		my number = time;
586 		my period = period;
587 		my openPhase = openPhase;
588 		my collisionPhase = collisionPhase;
589 		my te = te;
590 		my power1 = power1;
591 		my power2 = power2;
592 		my pulseScale = pulseScale;
593 		return me;
594 	} catch (MelderError) {
595 		Melder_throw (U"PhonationPoint not created.");
596 	}
597 }
598 
599 Thing_implement (PhonationTier, AnyTier, 0);
600 
601 autoPhonationTier PhonationTier_create (double tmin, double tmax) {
602 	try {
603 		autoPhonationTier me = Thing_new (PhonationTier);
604 		Function_init (me.get(), tmin, tmax);
605 		return me;
606 	} catch (MelderError) {
607 		Melder_throw (U"PhonationTier not created.");
608 	}
609 }
610 
611 autoPointProcess PhonationTier_to_PointProcess_closures (PhonationTier me) {
612 	try {
613 		const integer nt = my points.size;
614 		autoPointProcess thee = PointProcess_create (my xmin, my xmax, nt);
615 		for (integer ip = 1; ip <= nt; ip ++) {
616 			const PhonationPoint fp = my points.at [ip];
617 			PointProcess_addPoint (thee.get(), fp -> number);
618 		}
619 		return thee;
620 	} catch (MelderError) {
621 		Melder_throw (me, U": no PointProcess with closure times created.");
622 	}
623 }
624 
625 /********************** PhonationGridPlayOptions **********************/
626 
627 Thing_implement (PhonationGridPlayOptions, Daata, 0);
628 
629 static void PhonationGridPlayOptions_setDefaults (PhonationGridPlayOptions me) {
630 	my flowDerivative = my voicing = 1;
631 	my aspiration = my breathiness = 1;
632 	my flutter = my doublePulsing = 1;
633 	my collisionPhase = my spectralTilt = 1;
634 	my flowFunction = 1; // User defined flow tiers (power1 & power2)
635 	my maximumPeriod = 0;
636 }
637 
638 autoPhonationGridPlayOptions PhonationGridPlayOptions_create () {
639 	try {
640 		autoPhonationGridPlayOptions me = Thing_new (PhonationGridPlayOptions);
641 		return me;
642 	} catch (MelderError) {
643 		Melder_throw (U"PhonationGridPlayOptions not created.");
644 	}
645 }
646 
647 /********************** PhonationGrid **********************/
648 
649 
650 Thing_implement (PhonationGrid, Function, 0);
651 
652 void structPhonationGrid :: v_info () {
653 	structDaata :: v_info ();
654 	conststring32 in1 = U"  ", in2 = U"    ";
655 	MelderInfo_writeLine (in1, U"Time domain:");
656 	MelderInfo_writeLine (in2, U"Start time:     ", xmin, U" seconds");
657 	MelderInfo_writeLine (in2, U"End time:       ", xmax, U" seconds");
658 	MelderInfo_writeLine (in2, U"Total duration: ", xmax - xmin, U" seconds");
659 	MelderInfo_writeLine (in1, U"\nNumber of points in the PHONATION tiers:");
660 	MelderInfo_writeLine (in2, U"pitch:               ", pitch -> points.size);
661 	MelderInfo_writeLine (in2, U"voicingAmplitude:    ", voicingAmplitude -> points.size);
662 	MelderInfo_writeLine (in2, U"openPhase:           ", openPhase -> points.size);
663 	MelderInfo_writeLine (in2, U"collisionPhase:      ", collisionPhase -> points.size);
664 	MelderInfo_writeLine (in2, U"power1:              ", power1 -> points.size);
665 	MelderInfo_writeLine (in2, U"power2:              ", power2 -> points.size);
666 	MelderInfo_writeLine (in2, U"flutter:             ", flutter -> points.size);
667 	MelderInfo_writeLine (in2, U"doublePulsing:       ", doublePulsing -> points.size);
668 	MelderInfo_writeLine (in2, U"spectralTilt:        ", spectralTilt -> points.size);
669 	MelderInfo_writeLine (in2, U"aspirationAmplitude: ", aspirationAmplitude -> points.size);
670 	MelderInfo_writeLine (in2, U"breathinessAmplitude:", breathinessAmplitude -> points.size);
671 }
672 
673 void PhonationGrid_setNames (PhonationGrid me) {
674 	Thing_setName (my pitch.get(), U"pitch");
675 	Thing_setName (my voicingAmplitude.get(), U"voicingAmplitude");
676 	Thing_setName (my openPhase.get(), U"openPhase");
677 	Thing_setName (my collisionPhase.get(), U"collisionPhase");
678 	Thing_setName (my power1.get(), U"power1");
679 	Thing_setName (my power2.get(), U"power2");
680 	Thing_setName (my flutter.get(), U"flutter");
681 	Thing_setName (my doublePulsing.get(), U"doublePulsing");
682 	Thing_setName (my spectralTilt.get(), U"spectralTilt");
683 	Thing_setName (my aspirationAmplitude.get(), U"aspirationAmplitude");
684 	Thing_setName (my breathinessAmplitude.get(), U"breathinessAmplitude");
685 }
686 
687 autoPhonationGrid PhonationGrid_create (double tmin, double tmax) {
688 	try {
689 		autoPhonationGrid me = Thing_new (PhonationGrid);
690 		Function_init (me.get(), tmin, tmax);
691 		my pitch = PitchTier_create (tmin, tmax);
692 		my voicingAmplitude = IntensityTier_create (tmin, tmax);
693 		my openPhase = RealTier_create (tmin, tmax);
694 		my collisionPhase = RealTier_create (tmin, tmax);
695 		my power1 = RealTier_create (tmin, tmax);
696 		my power2 = RealTier_create (tmin, tmax);
697 		my flutter = RealTier_create (tmin, tmax);
698 		my doublePulsing = RealTier_create (tmin, tmax);
699 		my spectralTilt = IntensityTier_create (tmin, tmax);
700 		my aspirationAmplitude = IntensityTier_create (tmin, tmax);
701 		my breathinessAmplitude = IntensityTier_create (tmin, tmax);
702 		my options = PhonationGridPlayOptions_create ();
703 		PhonationGrid_setNames (me.get());
704 		return me;
705 	} catch (MelderError) {
706 		Melder_throw (U"PhonationGrid not created.");
707 	}
708 }
709 
710 static void PhonationGrid_checkFlowFunction (PhonationGrid me) {
711 	int hasPower1Points = my power1 -> points.size > 0;
712 	int hasPower2Points = my power2 -> points.size > 0;
713 
714 	integer ipoint = 1;
715 	do {
716 		const double time = ( hasPower1Points ? my power1 -> points.at [ipoint] -> number : 0.5 * (my xmin + my xmax) );
717 		double power1 = RealTier_getValueAtIndex (my power1.get(), ipoint);
718 		if (isundef (power1))
719 			power1 = KlattGrid_POWER1_DEFAULT;
720 		Melder_require (power1 > 0.0,
721 			U"All power1 values should greater than zero.");
722 
723 		double power2 = RealTier_getValueAtTime (my power2.get(), time);
724 		if (isundef (power2))
725 			power2 = KlattGrid_POWER2_DEFAULT;
726 		Melder_require (power1 < power2,
727 			U"At all times a power1 value should be smaller than the corresponding power2 value.");
728 
729 	} while ( ++ ipoint < my power1 -> points.size);
730 
731 	// Now check power2 values. This is necessary to catch situations where power2 has a valley:
732 	// power1(0) = 3; power2(1)= 4; power2(1)= 4; power2(0.5) = 3;
733 
734 	ipoint = 1;
735 	do {
736 		const double time = ( hasPower2Points ? my power2 -> points.at [ipoint] -> number : 0.5 * (my xmin + my xmax) );
737 		double power2 = RealTier_getValueAtIndex (my power2.get(), ipoint);
738 		if (isundef (power2))
739 			power2 = KlattGrid_POWER2_DEFAULT;
740 		double power1 = RealTier_getValueAtTime (my power1.get(), time);
741 		if (isundef (power1))
742 			power1 = KlattGrid_POWER1_DEFAULT;
743 		Melder_require (power1 < power2,
744 			U"At all times a power1 value should be smaller than the corresponding power2 value.");
745 
746 	} while ( ++ ipoint < my power2 -> points.size);
747 }
748 
749 static void PhonationGrid_draw_inside (PhonationGrid me, Graphics g, double xmin, double xmax, double ymin, double ymax, double dy, double *out_y) {
750 	// dum voicing conn tilt conn summer
751 	(void) me;
752 	double xw [6] = { 0.0, 1.0, 0.5, 1.0, 0.5, 0.5 }, xws [6];
753 
754 	connections thee = connections_create (2);
755 
756 	rel_to_abs (xw, xws, 5, xmax - xmin);
757 
758 	dy = (ymax - ymin) / (1.0 + ( dy < 0.0 ? 0.0 : dy ) + 1.0);
759 
760 	double x1 = xmin, x2 = x1 + xw [1];
761 	double y2 = ymax, y1 = y2 - dy;
762 	draw_oneSection (g, x1, x2, y1, y2, nullptr, U"Voicing", nullptr);
763 
764 	x1 = x2;
765 	x2 = x1 + xw [2];
766 	double ymid = (y1 + y2) / 2.0;
767 	Graphics_line (g, x1, ymid, x2, ymid);
768 
769 	x1 = x2;
770 	x2 = x1 + xw [3];
771 	draw_oneSection (g, x1, x2, y1, y2, nullptr, U"Tilt", nullptr);
772 
773 	thy x [1] = x2;
774 	thy y [1] = ymid;
775 
776 	y2 = y1 - 0.5 * dy;
777 	y1 = y2 - dy;
778 	ymid = (y1 + y2) / 2.0;
779 	x2 = xmin + xws [3];
780 	x1 = x2 - 1.5 * xw [3]; // some extra space
781 	draw_oneSection (g, x1, x2, y1, y2, nullptr, U"Aspiration", nullptr);
782 
783 	thy x [2] = x2;
784 	thy y [2] = ymid;
785 
786 	const double r = xw [5] / 2.0;
787 	const double xs = xmax - r, ys = (ymax + ymin) / 2.0;
788 	const bool arrow = true;
789 
790 	summer_drawConnections (g, xs, ys, r, thee, arrow, 0.4);
791 	connections_free (thee);
792 
793 	if (out_y)
794 		*out_y = ys;
795 }
796 
797 void PhonationGrid_draw (PhonationGrid me, Graphics g) {
798 	const double xmin = 0.0, xmax2 = 0.9, xmax = 1.0, ymin = 0.0, ymax = 1.0, dy = 0.5;
799 
800 	Graphics_setInner (g);
801 	Graphics_setWindow (g, xmin, xmax, ymin, ymax);
802 	Graphics_setTextAlignment (g, kGraphics_horizontalAlignment::CENTRE, Graphics_HALF);
803 	double yout;
804 	PhonationGrid_draw_inside (me, g, xmin, xmax2, ymin, ymax, dy, & yout);
805 
806 	Graphics_arrow (g, xmax2, yout, xmax, yout);
807 	Graphics_unsetInner (g);
808 }
809 
810 double PhonationGrid_getMaximumPeriod (PhonationGrid me) {
811 	const double minimumPitch = RealTier_getMinimumValue (my pitch.get());
812 	return 2.0 / ( isdefined (minimumPitch) && minimumPitch != 0.0 ? minimumPitch : my xmax - my xmin );
813 }
814 
815 static autoPointProcess PitchTier_to_PointProcess_flutter (PitchTier pitch, RealTier flutter, double maximumPeriod) {
816 	try {
817 		autoPointProcess thee = PitchTier_to_PointProcess (pitch);
818 		if (! flutter)
819 			return thee;
820 		double tsum = 0;
821 		for (integer it = 2; it <= thy nt; it ++) {
822 			const double t = thy t [it - 1];
823 			const double period = thy t [it] - thy t [it - 1];
824 			if (period < maximumPeriod && flutter -> points.size > 0) {
825 				const double fltr = RealTier_getValueAtTime (flutter, t);
826 				if (isdefined (fltr)) {
827 					// newF0 = f0 * (1 + (val / 50) * (sin ... + ...));
828 					const double newPeriod = period / (1.0 + (fltr / 50.0) * (sin (NUM2pi * 12.7 * t) + sin (NUM2pi * 7.1 * t) + sin (NUM2pi * 4.7 * t)));
829 					tsum += newPeriod - period;
830 				}
831 			}
832 			thy t [it] += tsum;
833 		}
834 		return thee;
835 	} catch (MelderError) {
836 		Melder_throw (pitch, U": no flutter PointProcess created.");
837 	}
838 }
839 
840 autoSound PhonationGrid_to_Sound_aspiration (PhonationGrid me, double samplingFrequency) {
841 	try {
842 		autoSound thee = Sound_createEmptyMono (my xmin, my xmax, samplingFrequency);
843 
844 		// Noise spectrum is tilted down by soft low-pass filter having a pole near
845 		// the origin in the z-plane, i.e. y [n] = x [n] + (0.75 * y [n-1])
846 		double lastval = 0.0;
847 		if (my aspirationAmplitude -> points.size > 0) {
848 			for (integer i = 1; i <= thy nx; i ++) {
849 				const double t = thy x1 + (i - 1) * thy dx;
850 				double val = NUMrandomUniform (-1.0, 1.0);
851 				const double a = DBSPL_to_A (RealTier_getValueAtTime (my aspirationAmplitude.get(), t));
852 				if (isdefined (a)) {
853 					thy z [1] [i] = lastval = val + 0.75 * lastval;
854 					lastval = (val += 0.75 * lastval); // soft low-pass
855 					thy z [1] [i] = val * a;
856 				}
857 			}
858 		}
859 		return thee;
860 	} catch (MelderError) {
861 		Melder_throw (me, U": no aspiration Sound created.");
862 	}
863 }
864 
865 static void Sound_PhonationGrid_spectralTilt_inplace (Sound thee, PhonationGrid me) {
866 	if (my spectralTilt -> points.size > 0) {
867 		/* Spectral tilt
868 			Filter y [n] = a * x [n] + b * y [n-1] => H(z) = a / (1 - bz^(-1)).
869 			We need attenuation, i.e. low-pass. Therefore 0 <= b <= 1.
870 			|H(f)| = a / sqrt (1 - 2*b*cos(2*pi*f*T) + b^2),
871 			|H(0)|= a /(1 - b) => if |H(0)| == 1, then a = 1 - b.
872 			Now solve 20 log|H(F)|= -c (at F=3 kHz and c > 0)
873 			Solution: if q = (1 - D * cos(2*pi*F*T)) / (1 - D), with D = 10^(-c/10)
874 				then b = q -sqrt(q^2 - 1)
875 		*/
876 
877 		const double cosf = cos (NUM2pi * 3000.0 * thy dx); // samplingFrequency > 6000.0 !
878 		double ynm1 = 0.0;
879 
880 		for (integer i = 1; i <= thy nx; i ++) {
881 			const double t = thy x1 + (i - 1) * thy dx;
882 			const double tilt_db = RealTier_getValueAtTime (my spectralTilt.get(), t);
883 
884 			if (tilt_db > 0) {
885 				const double d = pow (10.0, -tilt_db / 10.0);
886 				const double q = (1.0 - d * cosf) / (1.0 - d);
887 				const double b = q - sqrt (q * q - 1.0);
888 				const double a = 1.0 - b;
889 				thy z [1] [i] = a * thy z [1] [i] + b * ynm1;
890 				ynm1 = thy z [1] [i];
891 			}
892 		}
893 	}
894 }
895 
896 struct nrfunction_struct {
897 	double n;
898 	double m;
899 	double a;
900 };
901 
902 static double nrfunction (double x, double *dfx, void *closure) {
903 	const struct nrfunction_struct *nrfs = (struct nrfunction_struct *) closure;
904 	const double mplusax = nrfs -> m + nrfs -> a * x;
905 	const double mminn = nrfs -> m - nrfs -> n;
906 	const double fx = pow (x, mminn) - (nrfs -> n + nrfs -> a * x) / mplusax;
907 	*dfx = mminn * pow (x, mminn - 1) - nrfs -> a * mminn / (mplusax * mplusax);
908 	return fx;
909 }
910 
911 static double get_collisionPoint_x (double n, double m, double collisionPhase) {
912 	double y = undefined;
913 	/*
914 	Domain [0,1]:
915 	The glottal flow is given by:
916 	U(y) = y^n - y^m               0<= y <= x, and m > n
917 			(x^n - x^m)exp(-a(y-x))  y >= x, where a = 1 / collisionPhase
918 	The x where this occurs is the point where the amplitudes as well as the derivatives are equal.
919 	I.e. the x where n x^(n-1) - m x^(m-1) = (x^n-x^m)(-a).
920 	This can be simplified: find x in (0,1) where f(x) = x^(m-n) - (n+ax)/(m+ax) == 0.
921 		For m - n == 1, f(x) is a second order equation f(x)= a x^2 + (m-a) x - n == 0.
922 	In all other cases we solve with Newton-Raphson. For these cases we also need the derivative:
923 		f'(x)= (m - n)x^(m - n - 1)- a(m - n) / (m + a x)^2
924 	*/
925 	if (collisionPhase <= 0.0)
926 		return 1.0;
927 	double a = 1.0 / collisionPhase;
928 	if (m - n == 1.0) {
929 		const double b = m - a;
930 		const double c = - n;
931 		double y1, y2;
932 		integer nroots = NUMsolveQuadraticEquation (a, b, c, &y1, &y2);
933 		if (nroots == 2)
934 			y = y2;
935 		else if (nroots == 1)
936 			y = y1;
937 	} else { // Newton-Raphson
938 		// search in the interval from where the flow is a maximum to 1
939 		const double xmaxFlow = pow (n / m, 1.0 / (m - n));
940 		struct nrfunction_struct nrfs = {n, m, a};
941 		double root = NUMnrbis (& nrfunction, xmaxFlow, 1.0, & nrfs);
942 		y = root;
943 	}
944 	return y;
945 }
946 
947 autoPhonationTier PhonationGrid_to_PhonationTier (PhonationGrid me) {
948 	try {
949 		integer diplophonicPulseIndex = 0;
950 		const PhonationGridPlayOptions pp = my options.get();
951 
952 		PhonationGrid_checkFlowFunction (me);
953 		Melder_require (my pitch -> points.size > 0,
954 			U"Pitch tier should not be empty.");
955 
956 		if (pp -> maximumPeriod == 0.0)
957 			pp -> maximumPeriod = PhonationGrid_getMaximumPeriod (me);
958 
959 		autoPointProcess point = PitchTier_to_PointProcess_flutter (my pitch.get(), ( pp -> flutter ? my flutter.get() : nullptr ), pp -> maximumPeriod);
960 
961 		autoPhonationTier thee = PhonationTier_create (my xmin, my xmax);
962 
963 		/*
964 		Cycle through the points of the point PointProcess. Each will become a period.
965 		We assume that the planning for the pitch period occurs approximately at a time T before the glottal closure.
966 		For each point t [i]:
967 			Determine the f0 -> period T [i]
968 			Determine time t [i]-T [i] the open quotient, power1, power2, collisionphase etc.
969 			Generate the period.
970 		*/
971 
972 		for (integer it = 1; it <= point -> nt; it ++) {
973 			double re = 0.0, t = point -> t [it];		// the glottis "closing" point
974 			double pulseDelay = 0.0;        // For alternate pulses in case of diplophonia
975 			double pulseScale = 1.0;        // For alternate pulses in case of diplophonia
976 
977 			double period = PointProcess_getPeriodAtIndex (point.get(), it, pp -> maximumPeriod);
978 			if (isundef (period))
979 				period = 0.5 * pp -> maximumPeriod;   // some default value
980 
981 			// Calculate the point where the exponential decay starts:
982 			// Query tiers where period starts .
983 
984 			double periodStart = t - period;
985 			double collisionPhase = ( pp -> collisionPhase ? RealTier_getValueAtTime (my collisionPhase.get(), periodStart) : 0.0 );
986 			if (isundef (collisionPhase))
987 				collisionPhase = 0.0;
988 			double power1 = ( pp -> flowFunction == 1 ? RealTier_getValueAtTime (my power1.get(), periodStart) : pp -> flowFunction );
989 			if (isundef (power1))
990 				power1 = KlattGrid_POWER1_DEFAULT;
991 			double power2 = ( pp -> flowFunction == 1 ? RealTier_getValueAtTime (my power2.get(), periodStart) : pp -> flowFunction + 1 );
992 			if (isundef (power2))
993 				power2 = KlattGrid_POWER2_DEFAULT;
994 			try {
995 				re = get_collisionPoint_x (power1, power2, collisionPhase);
996 			} catch (MelderError) {
997 				Melder_warning (U"Illegal collision point at t = ", t, U" (power1=", power1, U", power2=", power2, U"colPhase=", collisionPhase, U")");
998 			}
999 
1000 			double openPhase = RealTier_getValueAtTime (my openPhase.get(), periodStart);
1001 			if (isundef (openPhase))
1002 				openPhase = KlattGrid_OPENPHASE_DEFAULT;
1003 
1004 			const double te = re * period * openPhase;
1005 
1006 			// In case of diplophonia alternate pulses get modified.
1007 			// A modified puls is delayed in time and its amplitude attenuated.
1008 			// This delay scales to maximally equal the closed phase of the next period.
1009 			// The doublePulsing scales the amplitudes as well as the delay linearly.
1010 
1011 			double doublePulsing = ( pp -> doublePulsing ? RealTier_getValueAtTime (my doublePulsing.get(), periodStart) : 0.0 );
1012 			if (isundef (doublePulsing))
1013 				doublePulsing = 0.0;
1014 			if (doublePulsing > 0.0) {
1015 				diplophonicPulseIndex ++;
1016 				if (diplophonicPulseIndex % 2 == 1) {   // the odd-numbered one
1017 					double nextPeriod = PointProcess_getPeriodAtIndex (point.get(), it + 1, pp -> maximumPeriod);
1018 					if (isundef (nextPeriod))
1019 						nextPeriod = period;
1020 					double openPhase2 = KlattGrid_OPENPHASE_DEFAULT;
1021 					if (my openPhase -> points.size > 0)
1022 						openPhase2 = RealTier_getValueAtTime (my openPhase.get(), t);
1023 					const double maxDelay = period * (1.0 - openPhase2);
1024 					pulseDelay = maxDelay * doublePulsing;
1025 					pulseScale *= 1.0 - doublePulsing;
1026 				}
1027 			} else
1028 				diplophonicPulseIndex = 0;
1029 
1030 			t += pulseDelay;
1031 			autoPhonationPoint phonationPoint = PhonationPoint_create (t, period, openPhase, collisionPhase, te, power1, power2, pulseScale);
1032 			AnyTier_addPoint_move (thee.get()->asAnyTier(), phonationPoint.move());
1033 		}
1034 		return thee;
1035 	} catch (MelderError) {
1036 		Melder_throw (me, U": no PhonationTier created.");
1037 	}
1038 }
1039 
1040 static autoSound PhonationGrid_PhonationTier_to_Sound_voiced (PhonationGrid me, PhonationTier thee, double samplingFrequency) {
1041 	try {
1042 		const PhonationGridPlayOptions p = my options.get();
1043 		double lastVal = undefined;
1044 
1045 		Melder_require (my voicingAmplitude -> points.size > 0,
1046 			U"Voicing amplitude tier should not be empty.");
1047 
1048 		autoSound him = Sound_createEmptyMono (my xmin, my xmax, samplingFrequency);
1049 		autoSound breathy;
1050 		if (p -> breathiness && my breathinessAmplitude -> points.size > 0)
1051 			breathy = Sound_createEmptyMono (my xmin, my xmax, samplingFrequency);
1052 
1053 		/*
1054 			Cycle through the points of the PhonationTier. Each will become a period.
1055 			We assume that the planning for the pitch period occurs approximately at a time T before the glottal closure.
1056 			For each point t [i]:
1057 				Determine the f0 -> period T [i]
1058 				Determine time t [i]-T [i] the open quotient, power1, power2, collisionphase etc.
1059 				Generate the period.
1060 		*/
1061 		VEC sound = his z.row (1);
1062 		for (integer it = 1; it <= thy points.size; it ++) {
1063 			PhonationPoint point = thy points.at [it];
1064 			const double t = point -> number;		// the glottis "closing" point
1065 			const double te = point -> te;
1066 			const double period = point -> period; // duration of the current period
1067 			const double openPhase = point -> openPhase;
1068 			const double collisionPhase = point -> collisionPhase;
1069 			const double pulseScale = point -> pulseScale;        // For alternate pulses in case of diplophonia
1070 			const double power1 = point -> power1, power2 = point -> power2;
1071 			double phase;                 // 0..1
1072 			double flow;
1073 
1074 			//- double amplitude = pulseScale * (power1 + power2 + 1.0) / (power2 - power1);
1075 			//- amplitude /= period * openPhase;
1076 
1077 			// Maximum of U(x) = x^n - x^m is where the derivative U'(x) = n x^(n-1) - m x^(m-1) == 0,
1078 			//	i.e. (n/m) = x^(m-n), so xmax = (n/m)^(1/(m-n))
1079 			//	U(xmax) = x^n (1-x^(m-n)) = (n/m)^(n/(m-n))(1-n/m)
1080 
1081 			const double amplitude = pulseScale / (pow (power1 / power2, 1.0 / (power2 / power1 - 1.0)) * (1.0 - power1 / power2));
1082 
1083 			// Fill in the samples to the left of the current point.
1084 
1085 			integer midSample = Sampled_xToLowIndex (him.get(), t), beginSample;
1086 			beginSample = midSample - Melder_ifloor (te / his dx);
1087 			if (beginSample < 1)
1088 				beginSample = 0;
1089 			if (midSample > his nx)
1090 				midSample = his nx;
1091 			for (integer i = beginSample; i <= midSample; i ++) {
1092 				const double tsamp = his x1 + (i - 1) * his dx;
1093 				phase = (tsamp - (t - te)) / (period * openPhase);
1094 				if (phase > 0.0) {
1095 					flow = amplitude * (pow (phase, power1) - pow (phase, power2));
1096 					if (i == 0) {
1097 						lastVal = flow;    // For the derivative
1098 						continue;
1099 					}
1100 					sound [i] += flow;
1101 
1102 					// Breathiness only during open part modulated by the flow
1103 					if (breathy) {
1104 						double val = flow * NUMrandomUniform (-1.0, 1.0);
1105 						double a = RealTier_getValueAtTime (my breathinessAmplitude.get(), t);
1106 						breathy -> z [1] [i] += val * DBSPL_to_A (a);
1107 					}
1108 				}
1109 			}
1110 
1111 			// Determine the signal parameters at the current point.
1112 
1113 			phase = te / (period * openPhase);
1114 
1115 			//- double flow = amplitude * (period * openPhase) * (pow (phase, power1) - pow (phase, power2));
1116 
1117 			flow = amplitude * (pow (phase, power1) - pow (phase, power2));
1118 
1119 			// Fill in the samples to the right of the current point.
1120 
1121 			if (flow > 0.0) {
1122 				const double ta = collisionPhase * (period * openPhase);
1123 				const double factorPerSample = exp (- his dx / ta);
1124 				double value = flow * exp (- (his x1 + midSample * his dx - t) / ta);
1125 				integer endSample = midSample + Melder_ifloor (20.0 * ta / his dx);
1126 				if (endSample > his nx)
1127 					endSample = his nx;
1128 				for (integer i = midSample + 1; i <= endSample; i ++) {
1129 					sound [i] += value;
1130 					value *= factorPerSample;
1131 				}
1132 			}
1133 		}
1134 
1135 		// Scale voiced part and add breathiness during open phase
1136 		if (p -> flowDerivative) {
1137 			const double extremum = Vector_getAbsoluteExtremum (him.get(), 0.0, 0.0, kVector_peakInterpolation :: CUBIC);
1138 			if (isundef (lastVal))
1139 				lastVal = 0.0;
1140 			for (integer i = 1; i <= his nx; i ++) {
1141 				const double val = his z [1] [i];
1142 				his z [1] [i] -= lastVal;
1143 				lastVal = val;
1144 			}
1145 			Vector_scale (him.get(), extremum);
1146 		}
1147 
1148 		for (integer i = 1; i <= his nx; i ++) {
1149 			const double t = his x1 + (i - 1) * his dx;
1150 			his z [1] [i] *= DBSPL_to_A (RealTier_getValueAtTime (my voicingAmplitude.get(), t));
1151 			if (breathy)
1152 				his z [1] [i] += breathy -> z [1] [i];
1153 		}
1154 		return him;
1155 	} catch (MelderError) {
1156 		Melder_throw (me, U": no Sound created.");
1157 	}
1158 }
1159 
1160 static autoSound PhonationGrid_to_Sound_voiced (PhonationGrid me, double samplingFrequency) {
1161 	try {
1162 		autoPhonationTier thee = PhonationGrid_to_PhonationTier (me);
1163 		return PhonationGrid_PhonationTier_to_Sound_voiced (me, thee.get(), samplingFrequency);
1164 	} catch (MelderError) {
1165 		Melder_throw (me, U": no voiced Sound created.");
1166 	}
1167 }
1168 
1169 static autoSound PhonationGrid_to_Sound (PhonationGrid me, CouplingGrid him, double samplingFrequency) {
1170 	try {
1171 		PhonationGridPlayOptions pp = my options.get();
1172 		autoSound thee;
1173 		if (pp -> voicing) {
1174 			if (him && his glottis -> points.size > 0)
1175 				thee = PhonationGrid_PhonationTier_to_Sound_voiced (me, his glottis.get(), samplingFrequency);
1176 			else
1177 				thee = PhonationGrid_to_Sound_voiced (me, samplingFrequency);
1178 			if (pp -> spectralTilt)
1179 				Sound_PhonationGrid_spectralTilt_inplace (thee.get(), me);
1180 		}
1181 		if (pp -> aspiration) {
1182 			autoSound aspiration = PhonationGrid_to_Sound_aspiration (me, samplingFrequency);
1183 			if (thee)
1184 				_Sounds_add_inplace (thee.get(), aspiration.get());
1185 			else
1186 				thee = aspiration.move();
1187 		}
1188 		if (! thee)
1189 			thee = Sound_createEmptyMono (my xmin, my xmax, samplingFrequency);
1190 		return thee;
1191 	} catch (MelderError) {
1192 		Melder_throw (me, U": no Sound created.");
1193 	}
1194 }
1195 
1196 static void formantsAmplitudes_create (OrderedOf<structIntensityTier>* me, double tmin, double tmax, integer numberOfFormants) {
1197 	try {
1198 		for (integer i = 1; i <= numberOfFormants; i ++) {
1199 			autoIntensityTier a = IntensityTier_create (tmin, tmax);
1200 			me -> addItem_move (a.move());
1201 		}
1202 	} catch (MelderError) {
1203 		Melder_throw (U"No formants amplitudes created.");
1204 	};
1205 }
1206 
1207 /********************** VocalTractGridPlayOptions **********************/
1208 
1209 Thing_implement (VocalTractGridPlayOptions, Daata, 0);
1210 
1211 static void VocalTractGridPlayOptions_setDefaults (VocalTractGridPlayOptions me, VocalTractGrid thee) {
1212 	my filterModel = kKlattGridFilterModel::CASCADE;
1213 	my endOralFormant = std::min (thy oral_formants -> formants.size, thy oral_formants -> bandwidths.size);
1214 	my startOralFormant = 1;
1215 	my endNasalFormant = std::min (thy nasal_formants -> formants.size, thy nasal_formants -> bandwidths.size);
1216 	my startNasalFormant = 1;
1217 	my endNasalAntiFormant = std::min (thy nasal_antiformants -> formants.size, thy nasal_antiformants -> bandwidths.size);
1218 	my startNasalAntiFormant = 1;
1219 }
1220 
1221 autoVocalTractGridPlayOptions VocalTractGridPlayOptions_create () {
1222 	try {
1223 		autoVocalTractGridPlayOptions me = Thing_new (VocalTractGridPlayOptions);
1224 		return me;
1225 	} catch (MelderError) {
1226 		Melder_throw (U"VocalTractGridPlayOptions not created.");
1227 	}
1228 }
1229 
1230 /********************** VocalTractGrid ***************************************/
1231 
1232 static integer FormantGrid_getNumberOfFormantPoints (FormantGrid me, integer iformant) {
1233 	if (iformant < 1 || iformant > my formants.size)
1234 		return -1;
1235 	const RealTier f = my formants.at [iformant];
1236 	return f -> points.size;
1237 }
1238 
1239 static integer FormantGrid_getNumberOfBandwidthPoints (FormantGrid me, integer iformant) {
1240 	if (iformant < 1 || iformant > my bandwidths.size)
1241 		return -1;
1242 	const RealTier b = my bandwidths.at [iformant];
1243 	return b -> points.size;
1244 }
1245 
1246 static integer Ordered_getNumberOfAmplitudePoints (OrderedOf<structIntensityTier>* me, integer iformant) {
1247 	if (! me || iformant < 1 || iformant > my size)
1248 		return -1;
1249 	const RealTier t = my at [iformant];
1250 	return t -> points.size;
1251 }
1252 
1253 static void FormantGrid_info (FormantGrid me, OrderedOf<structIntensityTier>* amplitudes, conststring32 in1, conststring32 in2) {
1254 	const integer nformants = my formants.size;
1255 	const integer namplitudes = ( amplitudes ? amplitudes->size : 0 );
1256 	const integer nmax = std::max (nformants, namplitudes);
1257 
1258 	for (integer iformant = 1; iformant <= nmax; iformant ++) {
1259 		MelderInfo_writeLine (in1, U"Formant ", iformant, U":");
1260 		if (iformant <= my formants.size) {
1261 			const integer nfp = FormantGrid_getNumberOfFormantPoints (me, iformant);
1262 			const integer nbp = FormantGrid_getNumberOfBandwidthPoints (me, iformant);
1263 			MelderInfo_writeLine (in2, U"formants:   ", ( nfp >= 0 ? Melder_integer (nfp) : U"--undefined--" ));
1264 			MelderInfo_writeLine (in2, U"bandwidths: ", ( nbp >= 0 ? Melder_integer (nbp) : U"--undefined--" ));
1265 		}
1266 		if (amplitudes) {
1267 			const integer nap = Ordered_getNumberOfAmplitudePoints (amplitudes, iformant);
1268 			MelderInfo_writeLine (in2, U"amplitudes: ", ( nap >= 0 ? Melder_integer (nap) : U"--undefined--" ));
1269 		}
1270 	}
1271 }
1272 
1273 void structVocalTractGrid :: v_info () {
1274 	our structDaata :: v_info ();
1275 	const conststring32 in1 = U"  ", in2 = U"    ", in3 = U"      ";
1276 	MelderInfo_writeLine (in1, U"Time domain:");
1277 	MelderInfo_writeLine (in2, U"Start time:     ", our xmin, U" seconds");
1278 	MelderInfo_writeLine (in2, U"End time:       ", our xmax, U" seconds");
1279 	MelderInfo_writeLine (in2, U"Total duration: ", our xmax - our xmin, U" seconds");
1280 	MelderInfo_writeLine (in1, U"\nNumber of points in the ORAL FORMANT tiers:");
1281 	FormantGrid_info (our oral_formants.get(), & our oral_formants_amplitudes, in2, in3);
1282 	MelderInfo_writeLine (in1, U"\nNumber of points in the NASAL FORMANT tiers:");
1283 	FormantGrid_info (our nasal_formants.get(), & our nasal_formants_amplitudes, in2, in3);
1284 	MelderInfo_writeLine (in1, U"\nNumber of points in the NASAL ANTIFORMANT tiers:");
1285 	FormantGrid_info (our nasal_antiformants.get(), nullptr, in2, in3);
1286 }
1287 
1288 Thing_implement (VocalTractGrid, Function, 0);
1289 
1290 void VocalTractGrid_setNames (VocalTractGrid me) {
1291 	Thing_setName (my oral_formants.get(), U"oral_formants");
1292 	Thing_setName (my nasal_formants.get(), U"nasal_formants");
1293 	Thing_setName (my nasal_antiformants.get(), U"nasal_antiformants");
1294 	//Thing_setName (my oral_formants_amplitudes.get(), U"oral_formants_amplitudes");
1295 	//Thing_setName (my nasal_formants_amplitudes.get(), U"nasal_formants_amplitudes");
1296 }
1297 
1298 autoVocalTractGrid VocalTractGrid_create (double tmin, double tmax, integer numberOfFormants, integer numberOfNasalFormants, integer numberOfNasalAntiFormants) {
1299 	try {
1300 		autoVocalTractGrid me = Thing_new (VocalTractGrid);
1301 		Function_init (me.get(), tmin, tmax);
1302 		my oral_formants = FormantGrid_createEmpty (tmin, tmax, numberOfFormants);
1303 		my nasal_formants = FormantGrid_createEmpty (tmin, tmax, numberOfNasalFormants);
1304 		my nasal_antiformants = FormantGrid_createEmpty (tmin, tmax, numberOfNasalAntiFormants);
1305 		formantsAmplitudes_create (& my oral_formants_amplitudes, tmin, tmax, numberOfFormants);
1306 		formantsAmplitudes_create (& my nasal_formants_amplitudes, tmin, tmax, numberOfNasalFormants);
1307 		my options = VocalTractGridPlayOptions_create ();
1308 		VocalTractGrid_setNames (me.get());
1309 		return me;
1310 	} catch (MelderError) {
1311 		Melder_throw (U"VocalTractGrid not created.");
1312 	}
1313 }
1314 
1315 static void VocalTractGrid_CouplingGrid_drawCascade_inplace (VocalTractGrid me, CouplingGrid thee, Graphics g, double xmin, double xmax, double ymin, double ymax, double *out_yin, double *out_yout) {
1316 	const integer numberOfOralFormants = my oral_formants -> formants.size;
1317 	const integer numberOfNasalFormants = my nasal_formants -> formants.size;
1318 	const integer numberOfNasalAntiFormants = my nasal_antiformants -> formants.size;
1319 	const integer numberOfTrachealFormants = ( thee ? thy tracheal_formants -> formants.size : 0 );
1320 	const integer numberOfTrachealAntiFormants = ( thee ? thy tracheal_antiformants -> formants.size : 0 );
1321 	const double y1 = ymin, y2 = ymax, ddx = 0.2, ymid = (y1 + y2) / 2.0;
1322 	const conststring32 text [6] = { 0, U"TF", U"TAF", U"NF", U"NAF", U""};
1323 	const integer nf [6] = { 0, numberOfTrachealFormants, numberOfTrachealAntiFormants, numberOfNasalFormants, numberOfNasalAntiFormants, numberOfOralFormants };
1324 	constexpr integer numberOfXSections = 5;
1325 	integer nsx = 0;
1326 	autoMelderString ff, fb;
1327 
1328 	const integer numberOfFilters = numberOfNasalFormants + numberOfNasalAntiFormants + numberOfTrachealFormants + numberOfTrachealAntiFormants + numberOfOralFormants;
1329 	const double dx = (xmax - xmin) / (numberOfFilters + (nsx - 1) * ddx);
1330 
1331 	double x1, x2;
1332 	if (numberOfFilters == 0) {
1333 		x2 = xmax;
1334 		Graphics_line (g, xmin, ymid, x2, ymid);
1335 		goto end;
1336 	}
1337 
1338 	for (integer isection = 1; isection <= numberOfXSections; isection ++)
1339 		if (nf [isection] > 0)
1340 			nsx ++;
1341 
1342 	x1 = xmin;
1343 	for (integer isection = 1; isection <= numberOfXSections; isection ++) {
1344 		const integer numberOfFormants = nf [isection];
1345 
1346 		if (numberOfFormants == 0)
1347 			continue;
1348 
1349 		x2 = x1 + dx;
1350 		for (integer i = 1; i <= numberOfFormants; i ++) {
1351 			MelderString_copy (& ff, U"F", i);
1352 			MelderString_copy (& fb, U"B", i);
1353 			draw_oneSection (g, x1, x2, y1, y2, text [isection], ff.string, fb.string);
1354 
1355 			if (i < numberOfFormants) {
1356 				x1 = x2;
1357 				x2 = x1 + dx;
1358 			}
1359 		}
1360 
1361 		if (isection < numberOfXSections) {
1362 			x1 = x2;
1363 			x2 = x1 + ddx * dx;
1364 			Graphics_line (g, x1, ymid, x2, ymid);
1365 			x1 = x2;
1366 		}
1367 	}
1368 end:
1369 	if (out_yin)
1370 		*out_yin = ymid;
1371 	if (out_yout)
1372 		*out_yout = ymid;
1373 }
1374 
1375 static void VocalTractGrid_CouplingGrid_drawParallel_inplace (VocalTractGrid me, CouplingGrid thee, Graphics g, double xmin, double xmax, double ymin, double ymax, double dy, double *out_yin, double *out_yout) {
1376 	// (0: filler) (1: hor. line to split) (2: split to diff) (3: diff) (4: diff to split)
1377 	// (5: split to filter) (6: filters) (7: conn to summer) (8: summer)
1378 	double xw [9] = { 0.0, 0.3, 0.2, 1.5, 0.5, 0.5, 1.0, 0.5, 0.5 };
1379 	const integer numberOfXSections = 8, numberOfYSections = 4;
1380 	const integer numberOfNasalFormants = my nasal_formants -> formants.size;
1381 	const integer numberOfOralFormants = my oral_formants -> formants.size;
1382 	const integer numberOfTrachealFormants = ( thee ? thy tracheal_formants -> formants.size : 0 );
1383 	const integer numberOfFormants = numberOfNasalFormants + numberOfOralFormants + numberOfTrachealFormants;
1384 	const integer numberOfUpperPartFormants = numberOfNasalFormants + ( numberOfOralFormants > 0 ? 1 : 0 );
1385 	const integer numberOfLowerPartFormants = numberOfFormants - numberOfUpperPartFormants;
1386 	const conststring32 text [5] = { nullptr, U"Nasal", U"", U"", U"Tracheal" };
1387 	const integer nffrom [5] = { 0, 1, 1, 2, 1 };
1388 	const integer nfto [5] = { 0, numberOfNasalFormants, ( numberOfOralFormants > 0 ? 1 : 0 ), numberOfOralFormants, numberOfTrachealFormants };
1389 	autoMelderString fba;
1390 	double xws [9];
1391 	rel_to_abs (xw, xws, numberOfXSections, xmax - xmin);
1392 
1393 	double y1, y2;
1394 	if (numberOfFormants == 0) {
1395 		y1 = y2 = (ymin + ymax) / 2.0;
1396 		Graphics_line (g, xmin, y1, xmax, y1);
1397 		if (out_yin)
1398 			*out_yin = y1;
1399 		if (out_yout)
1400 			*out_yout = y2;
1401 		return;
1402 	}
1403 
1404 	const double ddy = ( dy < 0 ? 0.0 : dy);
1405 	dy = (ymax - ymin) / (numberOfFormants * (1.0 + ddy) - ddy);
1406 
1407 	const connections local_in = connections_create (numberOfFormants);
1408 	const connections local_out = connections_create (numberOfFormants);
1409 
1410 	// parallel section
1411 	double x1 = xmin + xws [5];
1412 	double x2 = x1 + xw [6];
1413 	y2 = ymax;
1414 	double x3 = xmin + xws [4];
1415 	integer ic = 0;
1416 	for (integer isection = 1; isection <= numberOfYSections; isection ++) {
1417 		const integer ifrom = nffrom [isection], ito = nfto [isection];
1418 		if (ito < ifrom)
1419 			continue;
1420 		for (integer i = ifrom; i <= ito; i ++) {
1421 			y1 = y2 - dy;
1422 			const double ymid = (y1 + y2) / 2.0;
1423 			const conststring32 fi = Melder_integer (i);
1424 			MelderString_copy (& fba, U"A", fi, U" F", fi, U" B", fi);
1425 			draw_oneSection (g, x1, x2, y1, y2, text [isection], fba.string, nullptr);
1426 			Graphics_line (g, x3, ymid, x1, ymid); // to the left
1427 			ic ++;
1428 			local_in -> x [ic] = x3;
1429 			local_out -> x [ic] = x2;
1430 			local_in -> y [ic] = local_out -> y [ic] = ymid;
1431 			y2 = y1 - 0.5 * dy;
1432 		}
1433 	}
1434 
1435 	ic = 0;
1436 	x1 = local_in -> y [1];
1437 	if (numberOfUpperPartFormants > 0) {
1438 		x1 = local_in -> x [numberOfUpperPartFormants];
1439 		y1 = local_in -> y [numberOfUpperPartFormants];
1440 		if (numberOfUpperPartFormants > 1)
1441 			Graphics_line (g, x1, y1, local_in -> x [1], local_in -> y [1]);    // vertical
1442 		x2 = xmin;
1443 		if (numberOfLowerPartFormants > 0)
1444 			x2 += xw [1];
1445 		Graphics_line (g, x1, y1, x2, y1); // done
1446 	}
1447 	if (numberOfLowerPartFormants > 0) {
1448 		const integer ifrom = numberOfUpperPartFormants + 1;
1449 		x1 = local_in -> x [ifrom];
1450 		y1 = local_in -> y [ifrom];   // at the split
1451 		if (numberOfLowerPartFormants > 1)
1452 			Graphics_line (g, x1, y1, local_in -> x [numberOfFormants], local_in -> y [numberOfFormants]);    // vertical
1453 		x2 = xmin + xws [3]; // right of diff
1454 		Graphics_line (g, x1, y1, x2, y1); // from vertical to diff
1455 		x1 = xmin + xws [2]; // left of diff
1456 		draw_oneSection (g, x1, x2, y1 + 0.5 * dy, y1 - 0.5 * dy, U"Pre-emphasis", nullptr, nullptr);
1457 		x2 = x1;
1458 		if (numberOfUpperPartFormants > 0) {
1459 			x2 = xmin + xw [1];
1460 			y2 = y1;   // at split
1461 			Graphics_line (g, x1, y1, x2, y1); // to split
1462 			y1 += (1 + ddy) * dy;
1463 			Graphics_line (g, x2, y2, x2, y1); // vertical
1464 			y1 -= 0.5 * (1.0 + ddy) * dy;
1465 		}
1466 		Graphics_line (g, xmin, y1, x2, y1);
1467 	}
1468 
1469 	const double r = xw [8] / 2.0;
1470 	x2 = xmax - r;
1471 	y2 = (ymin + ymax) / 2.0;
1472 
1473 	alternatingSummer_drawConnections (g, x2, y2, r, local_out, true, 0.4);
1474 
1475 	connections_free (local_out);
1476 	connections_free (local_in);
1477 
1478 	if (out_yin)
1479 		*out_yin = y1;
1480 	if (out_yout)
1481 		*out_yout = y2;
1482 }
1483 
1484 static void VocalTractGrid_CouplingGrid_draw_inside (VocalTractGrid me, CouplingGrid thee, Graphics g, kKlattGridFilterModel filterModel, double xmin, double xmax, double ymin, double ymax, double dy, double *out_yin, double *out_yout) {
1485 	if (filterModel == kKlattGridFilterModel::CASCADE)
1486 		VocalTractGrid_CouplingGrid_drawCascade_inplace (me, thee, g, xmin, xmax, ymin, ymax, out_yin, out_yout);
1487 	else if (filterModel == kKlattGridFilterModel::PARALLEL)
1488 		VocalTractGrid_CouplingGrid_drawParallel_inplace (me, thee, g, xmin, xmax, ymin, ymax, dy, out_yin, out_yout);
1489 	else
1490 		;// Not valid
1491 }
1492 
1493 static void VocalTractGrid_CouplingGrid_draw (VocalTractGrid me, CouplingGrid thee, Graphics g, kKlattGridFilterModel filterModel) {
1494 	const double xmin = 0.0, xmin1 = 0.05, xmax1 = 0.95, xmax = 1.0, ymin = 0.0, ymax = 1.0, dy = 0.5;
1495 
1496 	Graphics_setInner (g);
1497 	Graphics_setWindow (g, xmin, xmax, ymin, ymax);
1498 	Graphics_setTextAlignment (g, kGraphics_horizontalAlignment::CENTRE, Graphics_HALF);
1499 	Graphics_setLineWidth (g, 2);
1500 	double yin, yout;
1501 	VocalTractGrid_CouplingGrid_draw_inside (me, thee, g, filterModel, xmin1, xmax1, ymin, ymax, dy, & yin, & yout);
1502 	Graphics_line (g, xmin, yin, xmin1, yin);
1503 	Graphics_arrow (g, xmax1, yout, xmax, yout);
1504 	Graphics_unsetInner (g);
1505 }
1506 
1507 static autoSound Sound_VocalTractGrid_CouplingGrid_filter_cascade (Sound me, VocalTractGrid thee, CouplingGrid coupling) {
1508 	try {
1509 		const VocalTractGridPlayOptions pv = thy options.get();
1510 		const CouplingGridPlayOptions pc = coupling -> options.get();
1511 		const bool useOpenGlottisInfo = pc -> openglottis && coupling && coupling -> glottis && coupling -> glottis -> points.size > 0;
1512 		const FormantGrid oral_formants = thy oral_formants.get();
1513 		const FormantGrid nasal_formants = thy nasal_formants.get();
1514 		const FormantGrid nasal_antiformants = thy nasal_antiformants.get();
1515 		const FormantGrid tracheal_formants = coupling -> tracheal_formants.get();
1516 		const FormantGrid tracheal_antiformants = coupling -> tracheal_antiformants.get();
1517 
1518 		const integer numberOfFormants = oral_formants -> formants.size;
1519 		const integer numberOfTrachealFormants = tracheal_formants -> formants.size;
1520 		const integer numberOfTrachealAntiFormants = tracheal_antiformants -> formants.size;
1521 		const integer numberOfNasalFormants = nasal_formants -> formants.size;
1522 		const integer numberOfNasalAntiFormants = nasal_antiformants -> formants.size;
1523 		check_formants (numberOfFormants, & pv -> startOralFormant, & pv -> endOralFormant);
1524 		check_formants (numberOfNasalFormants, & pv -> startNasalFormant, & pv -> endNasalFormant);
1525 		check_formants (numberOfTrachealFormants, & pc -> startTrachealFormant, & pc -> endTrachealFormant);
1526 		check_formants (numberOfNasalAntiFormants, & pv -> startNasalAntiFormant, & pv -> endNasalAntiFormant);
1527 		check_formants (numberOfTrachealAntiFormants, & pc -> startTrachealAntiFormant, & pc -> endTrachealAntiFormant);
1528 
1529 		autoSound him = Data_copy (me);
1530 
1531 		autoFormantGrid formants;
1532 		if (useOpenGlottisInfo) {
1533 			formants = Data_copy (thy oral_formants.get());
1534 			FormantGrid_CouplingGrid_updateOpenPhases (formants.get(), coupling);
1535 		}
1536 
1537 		integer nasal_formant_warning = 0, any_warning = 0;
1538 		if (pv -> endNasalFormant > 0) {   // nasal formants
1539 			for (integer iformant = pv -> startNasalFormant; iformant <= pv -> endNasalFormant; iformant ++) {
1540 				if (FormantGrid_isFormantDefined (thy nasal_formants.get(), iformant)) {
1541 					_Sound_FormantGrid_filterWithOneFormant_inplace (him.get(), thy nasal_formants.get(), iformant, false);
1542 				} else {
1543 					// Melder_warning ("Nasal formant", iformant, ": frequency and/or bandwidth missing.");
1544 					nasal_formant_warning ++;
1545 					any_warning ++;
1546 				}
1547 			}
1548 		}
1549 
1550 		integer nasal_antiformant_warning = 0;
1551 		if (pv -> endNasalAntiFormant > 0) {   // nasal antiformants
1552 			for (integer iformant = pv -> startNasalAntiFormant; iformant <= pv -> endNasalAntiFormant; iformant ++) {
1553 				if (FormantGrid_isFormantDefined (thy nasal_antiformants.get(), iformant)) {
1554 					_Sound_FormantGrid_filterWithOneFormant_inplace (him.get(), thy nasal_antiformants.get(), iformant, true);
1555 				} else {
1556 					// Melder_warning ("Nasal antiformant", iformant, ": frequency and/or bandwidth missing.");
1557 					nasal_antiformant_warning ++;
1558 					any_warning ++;
1559 				}
1560 			}
1561 		}
1562 
1563 		integer tracheal_formant_warning = 0;
1564 		if (pc -> endTrachealFormant > 0) {   // tracheal formants
1565 			for (integer iformant = pc -> startTrachealFormant; iformant <= pc -> endTrachealFormant; iformant ++) {
1566 				if (FormantGrid_isFormantDefined (tracheal_formants, iformant)) {
1567 					_Sound_FormantGrid_filterWithOneFormant_inplace (him.get(), tracheal_formants, iformant, false);
1568 				} else {
1569 					// Melder_warning ("Tracheal formant", iformant, ": frequency and/or bandwidth missing.");
1570 					tracheal_formant_warning ++;
1571 					any_warning ++;
1572 				}
1573 			}
1574 		}
1575 
1576 		integer tracheal_antiformant_warning = 0;
1577 		if (pc -> endTrachealAntiFormant > 0) {   // tracheal antiformants
1578 			for (integer iformant = pc -> startTrachealAntiFormant; iformant <= pc -> endTrachealAntiFormant; iformant ++) {
1579 				if (FormantGrid_isFormantDefined (tracheal_antiformants, iformant)) {
1580 					_Sound_FormantGrid_filterWithOneFormant_inplace (him.get(), tracheal_antiformants, iformant, true);
1581 				} else {
1582 					// Melder_warning ("Tracheal antiformant", iformant, ": frequency and/or bandwidth missing.");
1583 					tracheal_antiformant_warning ++;
1584 					any_warning ++;
1585 				}
1586 			}
1587 		}
1588 
1589 		integer oral_formant_warning = 0;
1590 		if (pv -> endOralFormant > 0) {   // oral formants
1591 			if (! formants)
1592 				formants = Data_copy (thy oral_formants.get());
1593 
1594 			for (integer iformant = pv -> startOralFormant; iformant <= pv -> endOralFormant; iformant ++) {
1595 				if (FormantGrid_isFormantDefined (formants.get(), iformant)) {
1596 					_Sound_FormantGrid_filterWithOneFormant_inplace (him.get(), formants.get(), iformant, false);
1597 				} else {
1598 					// Melder_warning ("Oral formant", iformant, ": frequency and/or bandwidth missing.");
1599 					oral_formant_warning ++;
1600 					any_warning ++;
1601 				}
1602 			}
1603 		}
1604 		if (any_warning > 0)
1605 		{
1606 			autoMelderString warning;
1607 			if (nasal_formant_warning > 0)
1608 				MelderString_append (& warning, U"\tNasal formants: one or more are missing.\n");
1609 			if (nasal_antiformant_warning)
1610 				MelderString_append (& warning, U"\tNasal antiformants: one or more are missing.\n");
1611 			if (tracheal_formant_warning)
1612 				MelderString_append (& warning, U"\tTracheal formants: one or more are missing.\n");
1613 			if (tracheal_antiformant_warning)
1614 				MelderString_append (& warning, U"\tTracheal antiformants: one or more are missing.\n");
1615 			if (oral_formant_warning)
1616 				MelderString_append (& warning, U"\tOral formants: one or more are missing.\n");
1617 			MelderInfo_write (U"\nWarning:\n", warning.string);
1618 			MelderInfo_drain ();
1619 		}
1620 		return him;
1621 	} catch (MelderError) {
1622 		Melder_throw (me, U": not filtered by vocaltract and coupling grid.");
1623 	}
1624 }
1625 
1626 static autoSound Sound_VocalTractGrid_CouplingGrid_filter_parallel (Sound me, VocalTractGrid thee, CouplingGrid coupling) {
1627 	try {
1628 		const VocalTractGridPlayOptions pv = thy options.get();
1629 		const CouplingGridPlayOptions pc = coupling -> options.get();
1630 		autoSound him;
1631 		FormantGrid oral_formants = thy oral_formants.get();
1632 		autoFormantGrid aof;
1633 		int alternatingSign = 0; // 0: no alternating signs in parallel adding of filter outputs, 1/-1 start sign
1634 		const bool useOpenGlottisInfo = pc -> openglottis && coupling -> glottis && coupling -> glottis -> points.size > 0;
1635 		int scale = 1;
1636 		const integer numberOfFormants = thy oral_formants -> formants.size;
1637 		const integer numberOfNasalFormants = thy nasal_formants -> formants.size;
1638 		const integer numberOfTrachealFormants = coupling -> tracheal_formants -> formants.size;
1639 
1640 		check_formants (numberOfFormants, & (pv -> startOralFormant), & (pv -> endOralFormant));
1641 		check_formants (numberOfNasalFormants, & (pv -> startNasalFormant), & (pv -> endNasalFormant));
1642 		check_formants (numberOfTrachealFormants, & (pc -> startTrachealFormant), & (pc -> endTrachealFormant));
1643 
1644 		if (useOpenGlottisInfo) {
1645 			aof = Data_copy (thy oral_formants.get());
1646 			oral_formants = aof.get();
1647 			FormantGrid_CouplingGrid_updateOpenPhases (oral_formants, coupling);
1648 		}
1649 
1650 		if (pv -> endOralFormant > 0) {
1651 			if (pv -> startOralFormant == 1) {
1652 				him = Data_copy (me);
1653 				if (oral_formants -> formants.size > 0)
1654 					Sound_FormantGrid_Intensities_filterWithOneFormant_inplace (him.get(), oral_formants, & thy oral_formants_amplitudes, 1);
1655 			}
1656 		}
1657 
1658 		if (pv -> endNasalFormant > 0) {
1659 			alternatingSign = 0;
1660 			autoSound nasal = Sound_FormantGrid_Intensities_filter (me, thy nasal_formants.get(), & thy nasal_formants_amplitudes, pv -> startNasalFormant, pv -> endNasalFormant, alternatingSign);
1661 
1662 			if (! him)
1663 				him = Data_copy (nasal.get());
1664 			else
1665 				_Sounds_add_inplace (him.get(), nasal.get());
1666 		}
1667 
1668 		// Formants 2 and up, with alternating signs.
1669 		// We perform pre-emphasis by differentiating.
1670 		// Klatt (1980) states that a first difference for the higher formants is necessary to remove low-frequency
1671 		// energy from them. This energy would otherwise distort the spectrum in the region of F1 during the synthesis
1672 		// of some vowels.
1673 
1674 		autoSound me_diff = _Sound_diff (me, scale);
1675 
1676 		if (pv -> endOralFormant >= 2) {
1677 			const integer startOralFormant2 = ( pv -> startOralFormant > 2 ? pv -> startOralFormant : 2 );
1678 			alternatingSign = ( startOralFormant2 % 2 == 0 ? -1 : 1 );   // 2 starts with negative sign
1679 			if (startOralFormant2 <= oral_formants -> formants.size) {
1680 				autoSound vocalTract = Sound_FormantGrid_Intensities_filter (me_diff.get(), oral_formants, & thy oral_formants_amplitudes, startOralFormant2, pv -> endOralFormant, alternatingSign);
1681 
1682 				if (! him)
1683 					him = Data_copy (vocalTract.get());
1684 				else
1685 					_Sounds_add_inplace (him.get(), vocalTract.get());
1686 			}
1687 		}
1688 
1689 		if (pc -> endTrachealFormant > 0) {   // tracheal formants
1690 			alternatingSign = 0;
1691 			autoSound trachea = Sound_FormantGrid_Intensities_filter (me_diff.get(), coupling -> tracheal_formants.get(),
1692 				& coupling -> tracheal_formants_amplitudes,
1693 				pc -> startTrachealFormant, pc -> endTrachealFormant, alternatingSign);
1694 
1695 			if (! him)
1696 				him = Data_copy (trachea.get());
1697 			else
1698 				_Sounds_add_inplace (him.get(), trachea.get());
1699 		}
1700 
1701 		if (! him)
1702 			him = Data_copy (me);
1703 		return him;
1704 	} catch (MelderError) {
1705 		Melder_throw (me, U": not filtered in parallel.");
1706 	}
1707 }
1708 
1709 autoSound Sound_VocalTractGrid_CouplingGrid_filter (Sound me, VocalTractGrid thee, CouplingGrid coupling) {
1710 	return thy options -> filterModel == kKlattGridFilterModel::CASCADE ?
1711 	       Sound_VocalTractGrid_CouplingGrid_filter_cascade (me, thee, coupling) :
1712 	       Sound_VocalTractGrid_CouplingGrid_filter_parallel (me, thee, coupling);
1713 }
1714 
1715 /********************** CouplingGridPlayOptions **********************/
1716 
1717 Thing_implement (CouplingGridPlayOptions, Daata, 0);
1718 
1719 static void CouplingGridPlayOptions_setDefaults (CouplingGridPlayOptions me, CouplingGrid thee) {
1720 	my fadeFraction = 0.1;
1721 	my openglottis = 1;
1722 	my endTrachealFormant = std::min (thy tracheal_formants -> formants.size, thy tracheal_formants -> bandwidths.size);
1723 	my startTrachealFormant = 1;
1724 	my endTrachealAntiFormant = std::min (thy tracheal_antiformants -> formants.size, thy tracheal_antiformants -> bandwidths.size);
1725 	my startTrachealAntiFormant = 1;
1726 	my startDeltaFormant = 1;
1727 	my endDeltaFormant = thy delta_formants -> formants.size;
1728 	my startDeltaBandwidth = 1;
1729 	my endDeltaBandwidth = thy delta_formants -> bandwidths.size;
1730 }
1731 
1732 autoCouplingGridPlayOptions CouplingGridPlayOptions_create () {
1733 	try {
1734 		autoCouplingGridPlayOptions me = Thing_new (CouplingGridPlayOptions);
1735 		return me;
1736 	} catch (MelderError) {
1737 		Melder_throw (U"CouplingGridPlayOptions not created.");
1738 	}
1739 }
1740 
1741 /********************** CouplingGrid *************************************/
1742 
1743 Thing_implement (CouplingGrid, Function, 0);
1744 
1745 void structCouplingGrid :: v_info () {
1746 	structDaata :: v_info ();
1747 	conststring32 in1 = U"  ", in2 = U"    ", in3 = U"      ";
1748 	MelderInfo_writeLine (in1, U"Time domain:");
1749 	MelderInfo_writeLine (in2, U"Start time:     ", xmin, U" seconds");
1750 	MelderInfo_writeLine (in2, U"End time:       ", xmax, U" seconds");
1751 	MelderInfo_writeLine (in2, U"Total duration: ", xmax - xmin, U" seconds");
1752 	MelderInfo_writeLine (in1, U"\nNumber of points in the TRACHEAL FORMANT tiers:");
1753 	FormantGrid_info (our tracheal_formants.get(), & tracheal_formants_amplitudes, in2, in3);
1754 	MelderInfo_writeLine (in1, U"\nNumber of points in the TRACHEAL ANTIFORMANT tiers:");
1755 	FormantGrid_info (our tracheal_antiformants.get(), nullptr, in2, in3);
1756 	MelderInfo_writeLine (in1, U"\nNumber of points in the DELTA FORMANT tiers:");
1757 	FormantGrid_info (our delta_formants.get(), nullptr, in2, in3);
1758 }
1759 
1760 void CouplingGrid_setNames (CouplingGrid me) {
1761 	Thing_setName (my tracheal_formants.get(), U"tracheal_formants");
1762 	Thing_setName (my tracheal_antiformants.get(), U"tracheal_antiformants");
1763 	//Thing_setName (my tracheal_formants_amplitudes.get(), U"tracheal_formants_amplitudes");
1764 	Thing_setName (my delta_formants.get(), U"delta_formants");
1765 	Thing_setName (my glottis.get(), U"glottis");
1766 }
1767 
1768 autoCouplingGrid CouplingGrid_create (double tmin, double tmax, integer numberOfTrachealFormants, integer numberOfTrachealAntiFormants, integer numberOfDeltaFormants) {
1769 	try {
1770 		autoCouplingGrid me = Thing_new (CouplingGrid);
1771 		Function_init (me.get(), tmin, tmax);
1772 		my tracheal_formants = FormantGrid_createEmpty (tmin, tmax, numberOfTrachealFormants);
1773 		my tracheal_antiformants = FormantGrid_createEmpty (tmin, tmax, numberOfTrachealAntiFormants);
1774 		formantsAmplitudes_create (& my tracheal_formants_amplitudes, tmin, tmax, numberOfTrachealFormants);
1775 		my delta_formants = FormantGrid_createEmpty (tmin, tmax, numberOfDeltaFormants);
1776 		my glottis = PhonationTier_create (tmin, tmax);
1777 		my options = CouplingGridPlayOptions_create ();
1778 		CouplingGrid_setNames (me.get());
1779 		return me;
1780 	} catch (MelderError) {
1781 		Melder_throw (U"CouplingGrid not created.");
1782 	}
1783 }
1784 
1785 /********************** FormantGrid & CouplingGrid *************************************/
1786 
1787 void FormantGrid_CouplingGrid_updateOpenPhases (FormantGrid me, CouplingGrid thee) {
1788 	try {
1789 		CouplingGridPlayOptions pc = thy options.get();
1790 		for (integer itier = 1; itier <= thy delta_formants -> formants.size; itier ++) {
1791 			RealTier delta = thy delta_formants -> formants.at [itier];
1792 			if (itier <= my formants.size) {
1793 				if (delta -> points.size > 0) {
1794 					autoRealTier rt = RealTier_updateWithDelta (my formants.at [itier], delta, thy glottis.get(), pc -> fadeFraction);
1795 					Melder_require (RealTier_valuesInRange (rt.get(), 0, undefined),
1796 						U"Formant ", itier, U" coupling should not give negative values.");
1797 
1798 					my formants. replaceItem_move (rt.move(), itier);
1799 				}
1800 			}
1801 			delta = thy delta_formants -> bandwidths.at [itier];
1802 			if (itier <= my bandwidths.size) {
1803 				if (delta -> points.size > 0) {
1804 					autoRealTier rt = RealTier_updateWithDelta (my bandwidths.at [itier], delta, thy glottis.get(), pc -> fadeFraction);
1805 					Melder_require (RealTier_valuesInRange (rt.get(), 0, undefined),
1806 						U"Bandwidth ", itier, U" coupling gives negative values.");
1807 					my bandwidths. replaceItem_move (rt.move(), itier);
1808 				}
1809 			}
1810 		}
1811 	} catch (MelderError) {
1812 		Melder_throw (me, U": not updated with open hase information.");
1813 	}
1814 }
1815 
1816 /********************** FricationGridPlayOptions **********************/
1817 
1818 Thing_implement (FricationGridPlayOptions, Daata, 0);
1819 
1820 static void FricationGridPlayOptions_setDefaults (FricationGridPlayOptions me, FricationGrid thee) {
1821 	my endFricationFormant = std::min (thy frication_formants -> formants.size, thy frication_formants -> bandwidths.size);
1822 	my startFricationFormant = 2;
1823 	my bypass = 1;
1824 }
1825 
1826 autoFricationGridPlayOptions FricationGridPlayOptions_create () {
1827 	try {
1828 		autoFricationGridPlayOptions me = Thing_new (FricationGridPlayOptions);
1829 		return me;
1830 	} catch (MelderError) {
1831 		Melder_throw (U"FricationGridPlayOptions not created.");
1832 	}
1833 }
1834 
1835 /************************ FricationGrid (& Sound) *********************************************/
1836 
1837 void structFricationGrid :: v_info () {
1838 	structDaata :: v_info ();
1839 	const static char32 *in1 = U"  ", *in2 = U"    ", *in3 = U"      ";
1840 	MelderInfo_writeLine (in1, U"Time domain:");
1841 	MelderInfo_writeLine (in2, U"Start time:     ", xmin, U" seconds");
1842 	MelderInfo_writeLine (in2, U"End time:       ", xmax, U" seconds");
1843 	MelderInfo_writeLine (in2, U"Total duration: ", xmax - xmin, U" seconds");
1844 	MelderInfo_writeLine (in1, U"\nNumber of points in the FRICATION tiers:");
1845 	MelderInfo_writeLine (in2, U"fricationAmplitude:  ", fricationAmplitude -> points.size);
1846 	MelderInfo_writeLine (in2, U"bypass:              ", bypass -> points.size);
1847 	MelderInfo_writeLine (in1, U"\nNumber of points in the FRICATION FORMANT tiers:");
1848 	FormantGrid_info (our frication_formants.get(), & our frication_formants_amplitudes, in2, in3);
1849 }
1850 
1851 Thing_implement (FricationGrid, Function, 0);
1852 
1853 void FricationGrid_setNames (FricationGrid me) {
1854 	Thing_setName (my fricationAmplitude.get(), U"fricationAmplitude");
1855 	Thing_setName (my frication_formants.get(), U"frication_formants");
1856 	Thing_setName (my bypass.get(), U"bypass");
1857 	//Thing_setName (my frication_formants_amplitudes.get(), U"frication_formants_amplitudes");
1858 }
1859 
1860 autoFricationGrid FricationGrid_create (double tmin, double tmax, integer numberOfFormants) {
1861 	try {
1862 		autoFricationGrid me = Thing_new (FricationGrid);
1863 		Function_init (me.get(), tmin, tmax);
1864 		my fricationAmplitude = IntensityTier_create (tmin, tmax);
1865 		my frication_formants = FormantGrid_createEmpty (tmin, tmax, numberOfFormants);
1866 		my bypass = IntensityTier_create (tmin, tmax);
1867 		formantsAmplitudes_create (& my frication_formants_amplitudes, tmin, tmax, numberOfFormants);
1868 		my options = FricationGridPlayOptions_create ();
1869 		FricationGrid_setNames (me.get());
1870 		return me;
1871 	} catch (MelderError) {
1872 		Melder_throw (U"FricationGrid not created.");
1873 	}
1874 }
1875 
1876 static void FricationGrid_draw_inside (FricationGrid me, Graphics g, double xmin, double xmax, double ymin, double ymax, double dy, double *yout) {
1877 	constexpr integer numberOfXSections = 5;
1878 	const integer numberOfFormants = my frication_formants -> formants.size;
1879 	const integer numberOfParts = numberOfFormants + ( numberOfFormants > 1 ? 0 : 1 ) ;   // 2..number + bypass
1880 	// dum noise, connections, filter, connections, adder
1881 	double xw [6] = { 0.0, 2, 0.6, 1.5, 0.6, 0.5 }, xws [6];
1882 	double r, x1, y1, x2, y2, x3, xs, ys, ymid = (ymin + ymax) / 2.0;
1883 
1884 	rel_to_abs (xw, xws, numberOfXSections, xmax - xmin);
1885 
1886 	dy = std::max (dy, 0.0);
1887 	dy = (ymax - ymin) / (numberOfParts * (1.0 + dy) - dy);
1888 
1889 	connections cp = connections_create (numberOfParts);
1890 	if (cp == 0)
1891 		return;
1892 
1893 	// section 1
1894 	x1 = xmin;
1895 	x2 = x1 + xw [1];
1896 	y1 = ymid - 0.5 * dy;
1897 	y2 = y1 + dy;
1898 	draw_oneSection (g, x1, x2, y1, y2, U"Frication", U"noise", nullptr);
1899 
1900 	// section 2, horizontal line halfway, vertical line
1901 	x1 = x2;
1902 	x2 = x1 + xw [2] / 2.0;
1903 	Graphics_line (g, x1, ymid, x2, ymid);
1904 	Graphics_line (g, x2, ymax - dy / 2, x2, ymin + dy / 2.0);
1905 	x3 = x2;
1906 	// final connection to section 2 , filters , connections to adder
1907 	x1 = xmin + xws [2];
1908 	x2 = x1 + xw [3];
1909 	y2 = ymax;
1910 	autoMelderString fba;
1911 	for (integer i = 1; i <= numberOfParts; i ++) {
1912 		conststring32 fi = Melder_integer (i + 1);
1913 		y1 = y2 - dy;
1914 		if (i < numberOfParts)
1915 			MelderString_copy (& fba, U"A", fi, U" F", fi, U" B", fi);
1916 		else
1917 			MelderString_copy (& fba,  U"Bypass");
1918 		draw_oneSection (g, x1, x2, y1, y2, nullptr, fba.string, nullptr);
1919 		double ymidi = (y1 + y2) / 2.0;
1920 		Graphics_line (g, x3, ymidi, x1, ymidi); // from noise to filter
1921 		cp -> x [i] = x2;
1922 		cp -> y [i] = ymidi;
1923 		y2 = y1 - 0.5 * dy;
1924 	}
1925 
1926 	r = xw [5] / 2.0;
1927 	xs = xmax - r;
1928 	ys = ymid;
1929 
1930 	if (numberOfParts > 1)
1931 		alternatingSummer_drawConnections (g, xs, ys, r, cp, 1, 0.4);
1932 	else
1933 		Graphics_line (g, cp -> x [1], cp -> y [1], xs + r, ys);
1934 
1935 	connections_free (cp);
1936 
1937 	if (yout)
1938 		*yout = ys;
1939 }
1940 
1941 void FricationGrid_draw (FricationGrid me, Graphics g) {
1942 	const double xmin = 0.0, xmax = 1.0, xmax2 = 0.9, ymin = 0.0, ymax = 1.0, dy = 0.5;
1943 
1944 	Graphics_setInner (g);
1945 	Graphics_setWindow (g, xmin, xmax, ymin, ymax);
1946 	Graphics_setTextAlignment (g, kGraphics_horizontalAlignment::CENTRE, Graphics_HALF);
1947 	Graphics_setLineWidth (g, 2);
1948 
1949 	double yout;
1950 	FricationGrid_draw_inside (me, g, xmin, xmax2, ymin, ymax, dy, & yout);
1951 
1952 	Graphics_arrow (g, xmax2, yout, xmax, yout);
1953 	Graphics_unsetInner (g);
1954 }
1955 
1956 autoSound FricationGrid_to_Sound (FricationGrid me, double samplingFrequency) {
1957 	try {
1958 		autoSound thee = Sound_createEmptyMono (my xmin, my xmax, samplingFrequency);
1959 
1960 		double lastval = 0.0;
1961 		for (integer i = 1; i <= thy nx; i ++) {
1962 			const double t = thy x1 + (i - 1) * thy dx;
1963 			double val = NUMrandomUniform (-1.0, 1.0);
1964 			double a = 0.0;
1965 			if (my fricationAmplitude -> points.size > 0) {
1966 				const double dba = RealTier_getValueAtTime (my fricationAmplitude.get(), t);
1967 				a = ( isdefined (dba) ? DBSPL_to_A (dba) : 0.0 );
1968 			}
1969 			lastval = (val += 0.75 * lastval); // TODO: soft low-pass coefficient should be Fs dependent!
1970 			thy z [1] [i] = val * a;
1971 		}
1972 
1973 		autoSound him = Sound_FricationGrid_filter (thee.get(), me);
1974 		return him;
1975 	} catch (MelderError) {
1976 		Melder_throw (me, U": no frication Sound created.");
1977 	}
1978 }
1979 
1980 /************************ Sound & FricationGrid *********************************************/
1981 
1982 autoSound Sound_FricationGrid_filter (Sound me, FricationGrid thee) {
1983 	try {
1984 		const FricationGridPlayOptions pf = thy options.get();
1985 		autoSound him;
1986 		const integer numberOfFricationFormants = thy frication_formants -> formants.size;
1987 
1988 		check_formants (numberOfFricationFormants, & (pf -> startFricationFormant), & (pf -> endFricationFormant));
1989 
1990 		if (pf -> endFricationFormant > 1) {
1991 			const integer startFricationFormant2 = pf -> startFricationFormant > 2 ? pf -> startFricationFormant : 2;
1992 			int alternatingSign = ( startFricationFormant2 % 2 == 0 ? 1 : -1 ); // 2 starts with positive sign
1993 			him = Sound_FormantGrid_Intensities_filter (me, thy frication_formants.get(), & thy frication_formants_amplitudes, startFricationFormant2, pf -> endFricationFormant, alternatingSign);
1994 		}
1995 
1996 		if (! him)
1997 			him = Data_copy (me);
1998 
1999 		if (pf -> bypass) {
2000 			for (integer is = 1; is <= his nx; is ++) {	// Bypass
2001 				const double t = his x1 + (is - 1) * his dx;
2002 				double ab = 0.0;
2003 				if (thy bypass -> points.size > 0) {
2004 					const double val = RealTier_getValueAtTime (thy bypass.get(), t);
2005 					ab = ( isundef (val) ? 0.0 : DB_to_A (val) );
2006 				}
2007 				his z [1] [is] += my z [1] [is] * ab;
2008 			}
2009 		}
2010 		return him;
2011 	} catch (MelderError) {
2012 		Melder_throw (me, U": not filtered by frication filter.");
2013 	}
2014 }
2015 
2016 /********************** KlattGridPlayOptions **********************/
2017 
2018 Thing_implement (KlattGridPlayOptions, Daata, 0);
2019 
2020 static void KlattGridPlayOptions_setDefaults (KlattGridPlayOptions me, KlattGrid thee) {
2021 	my samplingFrequency = 44100.0;
2022 	my scalePeak = 1;
2023 	my xmin = thy xmin;
2024 	my xmax = thy xmax;
2025 }
2026 
2027 autoKlattGridPlayOptions KlattGridPlayOptions_create () {
2028 	try {
2029 		autoKlattGridPlayOptions me = Thing_new (KlattGridPlayOptions);
2030 		return me;
2031 	} catch (MelderError) {
2032 		Melder_throw (U"KlattGridPlayOptions not created.");
2033 	}
2034 }
2035 
2036 void KlattGrid_setDefaultPlayOptions (KlattGrid me) {
2037 	KlattGridPlayOptions_setDefaults (my options.get(), me);
2038 	PhonationGridPlayOptions_setDefaults (my phonation -> options.get());
2039 	VocalTractGridPlayOptions_setDefaults (my vocalTract -> options.get(), my vocalTract.get());
2040 	CouplingGridPlayOptions_setDefaults (my coupling -> options.get(), my coupling.get());
2041 	FricationGridPlayOptions_setDefaults (my frication -> options.get(), my frication.get());
2042 }
2043 
2044 /************************ KlattGrid *********************************************/
2045 
2046 Thing_implement (KlattGrid, Function, 0);
2047 
2048 void structKlattGrid :: v_info () {
2049 	structDaata :: v_info ();
2050 	MelderInfo_writeLine (U"Time domain:");
2051 	MelderInfo_writeLine (U"   Start time:     ", xmin, U" seconds");
2052 	MelderInfo_writeLine (U"   End time:       ", xmax, U" seconds");
2053 	MelderInfo_writeLine (U"   Total duration: ", xmax - xmin, U" seconds");
2054 	MelderInfo_writeLine (U"\n--- PhonationGrid ---\n");
2055 	phonation -> v_info ();
2056 	MelderInfo_writeLine (U"\n--- VocalTractGrid ---\n");
2057 	vocalTract -> v_info ();
2058 	MelderInfo_writeLine (U"\n--- CouplingGrid ---\n");
2059 	coupling -> v_info ();
2060 	MelderInfo_writeLine (U"\n--- FricationGrid ---\n");
2061 	frication -> v_info ();
2062 }
2063 
2064 void KlattGrid_setNames (KlattGrid me) {
2065 	Thing_setName (my phonation.get(), U"phonation");
2066 	Thing_setName (my vocalTract.get(), U"vocalTract");
2067 	Thing_setName (my coupling.get(), U"coupling");
2068 	Thing_setName (my frication.get(), U"frication");
2069 	Thing_setName (my gain.get(), U"gain");
2070 }
2071 
2072 autoKlattGrid KlattGrid_create (double tmin, double tmax, integer numberOfFormants, integer numberOfNasalFormants, integer numberOfNasalAntiFormants, integer numberOfTrachealFormants, integer numberOfTrachealAntiFormants, integer numberOfFricationFormants, integer numberOfDeltaFormants) {
2073 	try {
2074 		autoKlattGrid me = Thing_new (KlattGrid);
2075 		Function_init (me.get(), tmin, tmax);
2076 		my phonation = PhonationGrid_create (tmin, tmax);
2077 		my vocalTract = VocalTractGrid_create (tmin, tmax, numberOfFormants, numberOfNasalFormants, numberOfNasalAntiFormants);
2078 		my coupling = CouplingGrid_create (tmin, tmax, numberOfTrachealFormants,  numberOfTrachealAntiFormants, numberOfDeltaFormants);
2079 		my frication = FricationGrid_create (tmin, tmax, numberOfFricationFormants);
2080 		my gain = IntensityTier_create (tmin, tmax);
2081 		my options = KlattGridPlayOptions_create ();
2082 
2083 		KlattGrid_setDefaultPlayOptions (me.get());
2084 		KlattGrid_setNames (me.get());
2085 		return me;
2086 	} catch (MelderError) {
2087 		Melder_throw (U"KlattGrid not created.");
2088 	}
2089 }
2090 
2091 autoKlattGrid KlattGrid_createExample () {
2092 	try {
2093 		autoKlattTable thee = KlattTable_createExample ();
2094 		autoKlattGrid me = KlattTable_to_KlattGrid (thee.get(), 0.005);
2095 		return me;
2096 	} catch (MelderError) {
2097 		Melder_throw (U"KlattGrid example not created.");
2098 	};
2099 }
2100 
2101 // y is the height in units of the height of one section,
2102 // y1 is the height from the top to the split between the uppper, non-diffed, and lower diffed part
2103 static void _KlattGrid_queryParallelSplit (KlattGrid me, double dy, double *out_y, double *out_y1) {
2104 	const integer ny = my vocalTract -> nasal_formants -> formants.size +
2105 		my vocalTract -> oral_formants -> formants.size + my coupling -> tracheal_formants -> formants.size;
2106 	const integer n1 = my vocalTract -> nasal_formants -> formants.size +
2107 		( my vocalTract -> oral_formants -> formants.size > 0 ? 1 : 0 );
2108 
2109 	const integer n2 = ny - n1;
2110 	double y = 0.0, y1 = 0.0;
2111 	if (ny != 0) {
2112 		y = ny + (ny - 1) * dy;
2113 
2114 		if (n1 == 0)
2115 			y1 = 0.5;
2116 		else if (n2 == 0)
2117 			y1 = y - 0.5;
2118 		else
2119 			y1 = n1 + (n1 - 1) * dy + 0.5 * dy;
2120 	}
2121 	if (out_y)
2122 		*out_y = y;
2123 	if (out_y1)
2124 		*out_y1 = y1;
2125 	return;
2126 }
2127 
2128 static void getYpositions (double h1, double h2, double h3, double h4, double h5, double fractionOverlap, double *out_dy, double *out_ymin1, double *out_ymax1, double *out_ymin2, double *out_ymax2, double *out_ymin3, double *out_ymax3) {
2129 	// Given: five 'blocks' with relative heights h1..h5 in arbitrary units.
2130 	// Problem: scale all h1..h5 such that:
2131 	// 1. blocks h1 and h2 form one unit, with h1 on top of h2, the quotient h1/h2 is fixed
2132 	// 2. blocks h3 and h4 form one unit, with h3 on top of h4, the quotient h3/h4 is fixed
2133 	// 3. blocks h1 and h3 have the same baseline.
2134 	// 4. h5 is always underneath (h1,h2) but may partially overlap (0.45) with h4.
2135 	// 5. After scaling the new h1+h2 >= 0.3
2136 	// 6. Optimally use the vertical space from 0.. 1, i.e the top of h1 or h3 is at 1,
2137 	// the bottom of h5 is at 0. Preferably scale all blocks with the same factor, if not possible than
2138 	// scale h3,h4 and h5 the same
2139 	//
2140 	// h1  h3
2141 	// h2  h4
2142 	//  h5
2143 	/* Cases:
2144 	              x             x       ^
2145 	     x      x x    x      x x       |
2146 	  h1 x x    x x    x x    x x h3    | h13
2147 	     -----------------------------------------------------------
2148 	  h2 x x    x x    x x    x x h4
2149 	     x      x      x x    x x
2150 	                     x      x
2151 	     x      x      x x    x x
2152 	  h5 x      x      x      x
2153 	     x      x      x      x
2154 	*/
2155 	double h; // h12_min = 0.3; not yet
2156 	const double h13 = ( h1 > h3 ? h1 : h3 ); // baselines are now equal
2157 	if (h2 >= h4) {
2158 		h = h13 + h2 + h5;
2159 	} else { // h2 < h4
2160 		double maximumOverlap3 = fractionOverlap * h5;
2161 		if (maximumOverlap3 < h1 + h2)
2162 			maximumOverlap3 = 0.0;
2163 		else if (maximumOverlap3 > h4 - h2)
2164 			maximumOverlap3 = h4 - h2;
2165 		h = h13 + h4 + h5 - maximumOverlap3;
2166 	}
2167 	const double dy = 1.0 / (1.1 * h);
2168 	const double ymin1 = 1.0 - (h13 + h2) * dy;
2169 	const double ymax1 = ymin1 + (h1 + h2) * dy;
2170 	const double ymin2 = 1.0 - (h13 + h4) * dy;
2171 	const double ymax2 = ymin2 + (h3 + h4) * dy;
2172 	const double ymin3 = 0.0;
2173 	const double ymax3 = h5 * dy;
2174 	if (out_dy)
2175 		*out_dy = dy;
2176 	if (out_ymin1)
2177 		*out_ymin1 = ymin1;
2178 	if (out_ymax1)
2179 		*out_ymax1 = ymax1;
2180 	if (out_ymin2)
2181 		*out_ymin2 = ymin2;
2182 	if (out_ymax2)
2183 		*out_ymax2 = ymax2;
2184 	if (out_ymin3)
2185 		*out_ymin3 = ymin3;
2186 	if (out_ymax3)
2187 		*out_ymax3 = ymax3;
2188 }
2189 
2190 void KlattGrid_drawVocalTract (KlattGrid me, Graphics g, kKlattGridFilterModel filterModel, int withTrachea) {
2191 	VocalTractGrid_CouplingGrid_draw (my vocalTract.get(), withTrachea ? my coupling.get() : nullptr, g, filterModel);
2192 }
2193 
2194 void KlattGrid_draw (KlattGrid me, Graphics g, kKlattGridFilterModel filterModel) {
2195 	const double xmin = 0.0, xmax2 = 0.90, xmax3 = 0.95, xmax = 1.0, ymin = 0.0, ymax = 1.0;
2196 	const double dy_phonation = 0.5, dy_vocalTract_p = 0.5, dy_frication = 0.5;
2197 
2198 	connections tf;
2199 	try {
2200 		tf = connections_create (2);
2201 	} catch (MelderError) {
2202 		Melder_clearError ();
2203 		return;
2204 	}
2205 
2206 	Graphics_setInner (g);
2207 
2208 	Graphics_setWindow (g, xmin, xmax, ymin, ymax);
2209 	Graphics_setTextAlignment (g, kGraphics_horizontalAlignment::CENTRE, Graphics_HALF);
2210 	Graphics_setLineWidth (g, 2);
2211 
2212 	const integer nff = my frication -> frication_formants -> formants.size - 1 + 1;
2213 	const double yh_frication = ( nff > 0 ? nff + (nff - 1) * dy_frication : 1.0 );
2214 	const double yh_phonation = 1.0 + dy_phonation + 1.0;
2215 	double yout_phonation, yout_frication;
2216 	double height_phonation = 0.3;
2217 	double dy = height_phonation / yh_phonation; // 1 vertical unit in source section height units
2218 
2219 	double xs1, xs2, ys1, ys2, xf1, xf2, yf1, yf2;
2220 	double xp1, xp2, yp1, yp2, xc1, xc2, yc1, yc2;
2221 	double xws [6];
2222 	double xw [6] = {0, 1.75, 0.125, 3.0, 0.25, 0.125 };
2223 	rel_to_abs (xw, xws, 5, xmax2 - xmin);
2224 
2225 	if (filterModel == kKlattGridFilterModel::CASCADE) { // Cascade section
2226 		/*
2227 			limit height of frication unit dy !
2228 		*/
2229 		height_phonation = yh_phonation / (yh_phonation + yh_frication);
2230 		if (height_phonation < 0.3)
2231 			height_phonation = 0.3;
2232 		dy = height_phonation / yh_phonation;
2233 
2234 		xs1 = xmin;
2235 		xs2 = xs1 + xw [1];
2236 		ys2 = ymax;
2237 		ys1 = ys2 - height_phonation;
2238 		PhonationGrid_draw_inside (my phonation.get(), g, xs1, xs2, ys1, ys2, dy_phonation, & yout_phonation);
2239 
2240 		// units in cascade have same heigth as units in source part.
2241 
2242 		xc1 = xmin + xws [2];
2243 		xc2 = xc1 + xw [3];
2244 		yc2 = yout_phonation + dy / 2.0;
2245 		yc1 = yc2 - dy;
2246 		double yin_vocalTract_c, yout_vocalTract_c;
2247 		VocalTractGrid_CouplingGrid_drawCascade_inplace (my vocalTract.get(), my coupling.get(), g, xc1, xc2, yc1, yc2, & yin_vocalTract_c, & yout_vocalTract_c);
2248 
2249 		tf -> x [1] = xc2;
2250 		tf -> y [1] = yout_vocalTract_c;
2251 
2252 		Graphics_line (g, xs2, yout_phonation, xc1, yin_vocalTract_c);
2253 
2254 		xf1 = xmin + xws [2];
2255 		xf2 = xf1 + xw [3];
2256 		yf2 = ymax - height_phonation;
2257 		yf1 = 0.0;
2258 
2259 		FricationGrid_draw_inside (my frication.get(), g, xf1, xf2, yf1, yf2, dy_frication, &yout_frication);
2260 	} else { // Parallel
2261 		/*
2262 			optimize the vertical space for source, parallel and frication
2263 			source part is relatively fixed. let the number of vertical section units be the divisor
2264 			connector line from source to parallel has to be horizontal
2265 			determine y's of source and parallel section
2266 		*/
2267 		double yf_parallel, yh_parallel, yh_overlap = 0.3, yin_vocalTract_p, yout_vocalTract_p;
2268 		_KlattGrid_queryParallelSplit (me, dy_vocalTract_p, &yh_parallel, & yf_parallel);
2269 		if (yh_parallel == 0.0) {
2270 			yh_parallel = yh_phonation;
2271 			yf_parallel = yh_parallel / 2.0;
2272 			yh_overlap = -0.1;
2273 		}
2274 
2275 		height_phonation = yh_phonation / (yh_phonation + yh_frication);
2276 		if (height_phonation < 0.3) {
2277 			height_phonation = 0.3;
2278 		}
2279 		//double yunit = (ymax - ymin) / (yh_parallel + (1 - yh_overlap) * yh_frication); // some overlap
2280 
2281 		//double ycs = ymax - 0.5 * height_phonation; // source output connector
2282 		//double ycp = ymax - yf_parallel * yunit; // parallel input connector
2283 		//double ytrans_phonation = ycs > ycp ? ycp - ycs : 0;
2284 		//double ytrans_parallel = ycp > ycs ? ycs - ycp : 0;
2285 
2286 		// source, tract, frication
2287 		xs1 = xmin;
2288 		xs2 = xs1 + xw [1];
2289 
2290 		const double h1 = yh_phonation / 2.0, h2 = h1, h3 = yf_parallel, h4 = yh_parallel - h3, h5 = yh_frication;
2291 		getYpositions (h1, h2, h3, h4, h5, yh_overlap, & dy, & ys1, & ys2, & yp1, & yp2, & yf1, & yf2);
2292 
2293 		PhonationGrid_draw_inside (my phonation.get(), g, xs1, xs2, ys1, ys2, dy_phonation, & yout_phonation);
2294 
2295 		xp1 = xmin + xws [2];
2296 		xp2 = xp1 + xw [3];
2297 		VocalTractGrid_CouplingGrid_drawParallel_inplace (my vocalTract.get(), my coupling.get(), g, xp1, xp2, yp1, yp2, dy_vocalTract_p, &yin_vocalTract_p, &yout_vocalTract_p);
2298 
2299 		tf -> x [1] = xp2;
2300 		tf -> y [1] = yout_vocalTract_p;
2301 
2302 		Graphics_line (g, xs2, yout_phonation, xp1, yin_vocalTract_p);
2303 
2304 		xf1 = xmin /* + 0.5 * xws [1] */;
2305 		xf2 = xf1 + 0.55 * (xw [2] + xws [3]);
2306 
2307 		FricationGrid_draw_inside (my frication.get(), g, xf1, xf2, yf1, yf2, dy_frication, &yout_frication);
2308 	}
2309 
2310 	tf -> x [2] = xf2;
2311 	tf -> y [2] = yout_frication;
2312 	const double r = (xmax3 - xmax2) / 2.0;
2313 	const double xs = xmax2 + r / 2.0;
2314 	const double ys = (ymax - ymin) / 2.0;
2315 
2316 	summer_drawConnections (g, xs, ys, r, tf, true, 0.6);
2317 
2318 	Graphics_arrow (g, xs + r, ys, xmax, ys);
2319 
2320 	Graphics_unsetInner (g);
2321 	connections_free (tf);
2322 }
2323 
2324 /**** Query, Add, Remove, Extract Replace ********/
2325 
2326 #define PhonationGrid_QUERY_ADD_REMOVE_EXTRACT_REPLACE(Name,name,tierType) \
2327 double KlattGrid_get##Name##AtTime (KlattGrid me, double t) \
2328 { return RealTier_getValueAtTime (my phonation -> name.get(), t); } \
2329 void KlattGrid_add##Name##Point (KlattGrid me, double t, double value) \
2330 { RealTier_addPoint (my phonation -> name.get(), t, value);} \
2331 void KlattGrid_remove##Name##Points (KlattGrid me, double t1, double t2) \
2332 { AnyTier_removePointsBetween (my phonation -> name.get()->asAnyTier(), t1, t2); } \
2333 auto##tierType KlattGrid_extract##Name##Tier (KlattGrid me) \
2334 { return Data_copy (my phonation -> name.get()); } \
2335 void KlattGrid_replace##Name##Tier (KlattGrid me, tierType thee) \
2336 { try {\
2337 	Melder_require (my xmin == thy xmin && my xmax == thy xmax, U"Domains should be equal"); \
2338 	auto##tierType any = Data_copy (thee); \
2339 	my phonation -> name = any.move(); \
2340 	} catch (MelderError) { Melder_throw (me, U": tier not replaced."); } \
2341 }
2342 
2343 // Generate 55 functions
2344 PhonationGrid_QUERY_ADD_REMOVE_EXTRACT_REPLACE (Pitch, pitch, PitchTier)
2345 PhonationGrid_QUERY_ADD_REMOVE_EXTRACT_REPLACE (VoicingAmplitude, voicingAmplitude, IntensityTier)
2346 PhonationGrid_QUERY_ADD_REMOVE_EXTRACT_REPLACE (Flutter, flutter, RealTier)
2347 PhonationGrid_QUERY_ADD_REMOVE_EXTRACT_REPLACE (Power1, power1, RealTier)
2348 PhonationGrid_QUERY_ADD_REMOVE_EXTRACT_REPLACE (Power2, power2, RealTier)
2349 PhonationGrid_QUERY_ADD_REMOVE_EXTRACT_REPLACE (OpenPhase, openPhase, RealTier)
2350 PhonationGrid_QUERY_ADD_REMOVE_EXTRACT_REPLACE (CollisionPhase, collisionPhase, RealTier)
2351 PhonationGrid_QUERY_ADD_REMOVE_EXTRACT_REPLACE (DoublePulsing, doublePulsing, RealTier)
2352 PhonationGrid_QUERY_ADD_REMOVE_EXTRACT_REPLACE (SpectralTilt, spectralTilt, IntensityTier)
2353 PhonationGrid_QUERY_ADD_REMOVE_EXTRACT_REPLACE (AspirationAmplitude, aspirationAmplitude, IntensityTier)
2354 PhonationGrid_QUERY_ADD_REMOVE_EXTRACT_REPLACE (BreathinessAmplitude, breathinessAmplitude, IntensityTier)
2355 
2356 autoFormantGrid* KlattGrid_getAddressOfFormantGrid (KlattGrid me, kKlattGridFormantType formantType) {
2357 	return formantType == kKlattGridFormantType::ORAL ? & my vocalTract -> oral_formants :
2358 	       formantType == kKlattGridFormantType::NASAL ? & my vocalTract -> nasal_formants :
2359 	       formantType == kKlattGridFormantType::FRICATION ? & my frication -> frication_formants :
2360 	       formantType == kKlattGridFormantType::TRACHEAL ? & my coupling -> tracheal_formants :
2361 	       formantType == kKlattGridFormantType::NASAL_ANTI ? & my vocalTract -> nasal_antiformants :
2362 	       formantType == kKlattGridFormantType::TRACHEALANTI ? & my coupling -> tracheal_antiformants :
2363 		   & my coupling -> delta_formants; // kKlattGridFormantType::Delta
2364 }
2365 
2366 OrderedOf<structIntensityTier>* KlattGrid_getAddressOfAmplitudes (KlattGrid me, kKlattGridFormantType formantType) {
2367 	return formantType == kKlattGridFormantType::ORAL ? & my vocalTract -> oral_formants_amplitudes :
2368 	       formantType == kKlattGridFormantType::NASAL ? & my vocalTract -> nasal_formants_amplitudes :
2369 	       formantType == kKlattGridFormantType::FRICATION ? & my frication -> frication_formants_amplitudes :
2370 	       formantType == kKlattGridFormantType::TRACHEAL ? & my coupling -> tracheal_formants_amplitudes : nullptr;
2371 }
2372 
2373 #define KlattGrid_QUERY_ADD_REMOVE(Name) \
2374 double KlattGrid_get##Name##AtTime (KlattGrid me, kKlattGridFormantType formantType, integer iformant, double t) \
2375 { \
2376 	autoFormantGrid* fg = KlattGrid_getAddressOfFormantGrid (me, formantType); \
2377 	return FormantGrid_get##Name##AtTime (fg->get(), iformant, t); \
2378 } \
2379 void KlattGrid_add##Name##Point (KlattGrid me, kKlattGridFormantType formantType, integer iformant, double t, double value) \
2380 { \
2381 	autoFormantGrid* fg = KlattGrid_getAddressOfFormantGrid (me, formantType); \
2382 	FormantGrid_add##Name##Point (fg->get(), iformant, t, value); \
2383 } \
2384 void KlattGrid_remove##Name##Points (KlattGrid me, kKlattGridFormantType formantType, integer iformant, double t1, double t2) \
2385 { \
2386 	autoFormantGrid* fg = KlattGrid_getAddressOfFormantGrid (me, formantType); \
2387 	FormantGrid_remove##Name##PointsBetween (fg->get(), iformant, t1, t2); \
2388 }
2389 
2390 // 6 functions
2391 KlattGrid_QUERY_ADD_REMOVE (Formant)
2392 KlattGrid_QUERY_ADD_REMOVE (Bandwidth)
2393 
2394 void KlattGrid_formula_frequencies (KlattGrid me, kKlattGridFormantType formantType, conststring32 expression, Interpreter interpreter) {
2395 	autoFormantGrid* fg = KlattGrid_getAddressOfFormantGrid (me, formantType);
2396 	FormantGrid_formula_frequencies (fg->get(), expression, interpreter, nullptr);
2397 }
2398 
2399 void KlattGrid_formula_bandwidths (KlattGrid me, kKlattGridFormantType formantType, conststring32 expression, Interpreter interpreter) {
2400 	autoFormantGrid* fg = KlattGrid_getAddressOfFormantGrid (me, formantType);
2401 
2402 	FormantGrid_formula_bandwidths (fg->get(), expression, interpreter, nullptr);
2403 }
2404 
2405 void KlattGrid_formula_amplitudes (KlattGrid me, kKlattGridFormantType formantType, conststring32 expression, Interpreter interpreter) {
2406 	try {
2407 		const OrderedOf<structIntensityTier>* ordered = KlattGrid_getAddressOfAmplitudes (me, formantType);
2408 		for (integer irow = 1; irow <= ordered->size; irow ++) {
2409 			const IntensityTier amplitudes = ordered->at [irow];
2410 			Formula_compile (interpreter, amplitudes, expression, kFormula_EXPRESSION_TYPE_NUMERIC, true);
2411 			Formula_Result result;
2412 			for (integer icol = 1; icol <= amplitudes -> points.size; icol ++) {
2413 				Formula_run (irow, icol, & result);
2414 				Melder_require (isdefined (result. numericResult),
2415 					U"Cannot put an undefined value into the tier.\nFormula not finished.");
2416 				amplitudes -> points.at [icol] -> value = result. numericResult;
2417 			}
2418 		}
2419 	} catch (MelderError) {
2420 		Melder_throw (me, U": formula not finished on amplitudes.");
2421 	}
2422 }
2423 
2424 double KlattGrid_getAmplitudeAtTime (KlattGrid me, kKlattGridFormantType formantType, integer iformant, double t) {
2425 	const OrderedOf<structIntensityTier>* ordered = KlattGrid_getAddressOfAmplitudes (me, formantType);
2426 	if (iformant < 1 || iformant > ordered->size)
2427 		return undefined;
2428 	return RealTier_getValueAtTime (ordered->at [iformant], t);
2429 }
2430 
2431 void KlattGrid_addAmplitudePoint (KlattGrid me, kKlattGridFormantType formantType, integer iformant, double t, double value) {
2432 	const OrderedOf<structIntensityTier>* ordered = KlattGrid_getAddressOfAmplitudes (me, formantType);
2433 	Melder_require (iformant > 0 && iformant <= ordered -> size,
2434 		U"Formant amplitude tier ", iformant, U"does not exist.");
2435 	RealTier_addPoint (ordered->at [iformant], t, value);
2436 }
2437 
2438 void KlattGrid_removeAmplitudePoints (KlattGrid me, kKlattGridFormantType formantType, integer iformant, double t1, double t2) {
2439 	const OrderedOf<structIntensityTier>* ordered = KlattGrid_getAddressOfAmplitudes (me, formantType);
2440 	if (ordered && iformant > 0 && iformant <= ordered->size) {
2441 		AnyTier_removePointsBetween (ordered->at [iformant]->asAnyTier(), t1, t2);
2442 	}
2443 }
2444 
2445 autoIntensityTier KlattGrid_extractAmplitudeTier (KlattGrid me, kKlattGridFormantType formantType, integer iformant) {
2446 	try {
2447 		const OrderedOf<structIntensityTier>* ordered = KlattGrid_getAddressOfAmplitudes (me, formantType);
2448 		Melder_require (ordered != nullptr,
2449 			U"This amplitude tier does not exist.");
2450 		Melder_require (iformant > 0 && iformant <= ordered -> size,
2451 			U"Formant amplitude tier ", iformant, U"does not exist.");
2452 		autoIntensityTier thee = Data_copy (ordered->at [iformant]);
2453 		return thee;
2454 	} catch (MelderError) {
2455 		Melder_throw (me, U": no IntensityTier extracted.");
2456 	}
2457 }
2458 
2459 void KlattGrid_replaceAmplitudeTier (KlattGrid me, kKlattGridFormantType formantType, integer iformant, IntensityTier thee) {
2460 	try {
2461 		Melder_require (my xmin == thy xmin && my xmax == thy xmax,
2462 			U"Domains should be equal.");
2463 		OrderedOf<structIntensityTier>* ordered = KlattGrid_getAddressOfAmplitudes (me, formantType);
2464 		Melder_require (ordered != nullptr,
2465 			U"This amplitude tier does not exist.");
2466 		Melder_require (iformant > 0 && iformant <= ordered -> size,
2467 			U"Formant amplitude tier ", iformant, U" does not exist.");
2468 		autoIntensityTier any = Data_copy (thee);
2469 		ordered -> replaceItem_move (any.move(), iformant);
2470 	} catch (MelderError) {
2471 		Melder_throw (me, U": no ampitude tier replaced.");
2472 	}
2473 }
2474 
2475 autoFormantGrid KlattGrid_extractFormantGrid (KlattGrid me, kKlattGridFormantType formantType) {
2476 	try {
2477 		autoFormantGrid* fg = KlattGrid_getAddressOfFormantGrid (me, formantType);
2478 		Melder_require ((*fg) -> formants . size > 0,
2479 			KlattGrid_getFormantName (formantType), U"s are not defined.");
2480 		autoFormantGrid thee = Data_copy (fg->get());
2481 		return thee;
2482 	} catch (MelderError) {
2483 		Melder_throw (me, U": no FormantGrid extracted.");
2484 	}
2485 }
2486 
2487 void KlattGrid_replaceFormantGrid (KlattGrid me, kKlattGridFormantType formantType, FormantGrid thee) {
2488 	try {
2489 		Melder_require (my xmin == thy xmin && my xmax == thy xmax,
2490 			U"Domains should be equal");
2491 		autoFormantGrid *fg = KlattGrid_getAddressOfFormantGrid (me, formantType);
2492 		*fg = Data_copy (thee);
2493 	} catch (MelderError) {
2494 		Melder_throw (me, U": no FormantGrid replaced.");
2495 	}
2496 }
2497 
2498 void KlattGrid_addFormantAmplitudeTier (KlattGrid me, kKlattGridFormantType formantType, integer position) {
2499 	try {
2500 		Melder_require (formantType != kKlattGridFormantType::NASAL_ANTI && formantType != kKlattGridFormantType::TRACHEALANTI && formantType != kKlattGridFormantType::DELTA,
2501 			U"Cannot add amplitude tier to this formant type.");
2502 		OrderedOf<structIntensityTier>* ordered = KlattGrid_getAddressOfAmplitudes (me, formantType);
2503 		const integer noa = ordered->size;
2504 		if (position > noa || position < 1)
2505 			position = noa + 1;
2506 		autoIntensityTier tier = IntensityTier_create (my xmin, my xmax);
2507 		ordered -> addItemAtPosition_move (tier.move(), position);
2508 	} catch (MelderError) {
2509 		Melder_throw (me, U": no formant amplitude tier added.");
2510 	}
2511 }
2512 
2513 void KlattGrid_removeFormantAmplitudeTier (KlattGrid me, kKlattGridFormantType formantType, integer position) {
2514 	try {
2515 		Melder_require (formantType != kKlattGridFormantType::NASAL_ANTI && formantType != kKlattGridFormantType::TRACHEALANTI && formantType != kKlattGridFormantType::DELTA,
2516 			U"Cannot remove amplitude tier from this formant type.");
2517 		OrderedOf<structIntensityTier>* ordered = KlattGrid_getAddressOfAmplitudes (me, formantType);
2518 		if (position > 0 && position <= ordered->size) {
2519 			ordered -> removeItem (position);
2520 		}
2521 	} catch (MelderError) {
2522 		Melder_throw (me, U": no formant amplitude tier removed.");
2523 	}
2524 }
2525 
2526 // The following two routines are deprecated.
2527 // We do this in two separate steps now
2528 void KlattGrid_addFormant (KlattGrid me, kKlattGridFormantType formantType, integer position) {
2529 	try {
2530 		autoFormantGrid* fg = KlattGrid_getAddressOfFormantGrid (me, formantType);
2531 
2532 		const integer nof = (*fg) -> formants.size;
2533 		if (position > nof || position < 1) {
2534 			position = nof + 1;
2535 		}
2536 
2537 		if (formantType == kKlattGridFormantType::NASAL_ANTI || formantType == kKlattGridFormantType::TRACHEALANTI ||
2538 			formantType == kKlattGridFormantType::DELTA) {
2539 			FormantGrid_addFormantAndBandwidthTiers (fg->get(), position);
2540 			return;
2541 		}
2542 
2543 		OrderedOf<structIntensityTier>* ordered = KlattGrid_getAddressOfAmplitudes (me, formantType);
2544 		const integer noa = ordered->size;
2545 		Melder_require (nof == noa,
2546 			U"The number of formants (",  nof, U") and the number of amplitudes (", noa, U") should be equal.");
2547 
2548 		FormantGrid_addFormantAndBandwidthTiers (fg->get(), position);
2549 		try {
2550 			autoIntensityTier tier = IntensityTier_create (my xmin, my xmax);
2551 			ordered -> addItemAtPosition_move (tier.move(), position);
2552 		} catch (MelderError) { // restore original
2553 			FormantGrid_removeFormantAndBandwidthTiers (fg->get(), position);
2554 		}
2555 	} catch (MelderError) {
2556 		Melder_throw (me, U": no formant added.");
2557 	}
2558 }
2559 
2560 void KlattGrid_removeFormant (KlattGrid me, kKlattGridFormantType formantType, integer position) {
2561 	autoFormantGrid* fg = KlattGrid_getAddressOfFormantGrid (me, formantType);
2562 	const integer nof = (*fg) -> formants.size;
2563 	if (formantType == kKlattGridFormantType::NASAL_ANTI || formantType == kKlattGridFormantType::TRACHEALANTI ||
2564         formantType == kKlattGridFormantType::DELTA) {
2565 		if (position < 1 || position > nof) {
2566 			return;
2567 		}
2568 		FormantGrid_removeFormantAndBandwidthTiers (fg->get(), position);
2569 	} else {
2570 		// oral & nasal & tracheal formants can have amplitudes
2571 		// only remove a formant and its amplitude tier if number of formants and amplitudes are the same
2572 		OrderedOf<structIntensityTier>* ordered = KlattGrid_getAddressOfAmplitudes (me, formantType);
2573 		const integer noa = ordered->size;
2574 		if (position < 1 || position > nof || position > noa) {
2575 			if (nof != noa) {
2576 				Melder_warning (U"The number of formant tiers (", nof, U") and the number of amplitude tiers (",
2577 					noa, U") don't match. Nothing removed.");
2578 			}
2579 			return;
2580 		}
2581 		FormantGrid_removeFormantAndBandwidthTiers (fg->get(), position);
2582 		ordered -> removeItem (position);
2583 	}
2584 }
2585 
2586 void KlattGrid_addFormantFrequencyAndBandwidthTiers (KlattGrid me, kKlattGridFormantType formantType, integer position) {
2587 	autoFormantGrid* fg = KlattGrid_getAddressOfFormantGrid (me, formantType);
2588 	FormantGrid_addFormantAndBandwidthTiers (fg->get(), position);
2589 }
2590 
2591 void KlattGrid_removeFormantFrequencyAndBandwidthTiers (KlattGrid me, kKlattGridFormantType formantType, integer position) {
2592 	autoFormantGrid* fg = KlattGrid_getAddressOfFormantGrid (me, formantType);
2593 	FormantGrid_removeFormantAndBandwidthTiers (fg->get(), position);
2594 }
2595 
2596 double KlattGrid_getDeltaFormantAtTime (KlattGrid me, integer iformant, double t) {
2597 	return FormantGrid_getFormantAtTime (my coupling -> delta_formants.get(), iformant, t);
2598 }
2599 void KlattGrid_addDeltaFormantPoint (KlattGrid me, integer iformant, double t, double value) {
2600 	FormantGrid_addFormantPoint (my coupling -> delta_formants.get(), iformant, t, value);
2601 }
2602 void KlattGrid_removeDeltaFormantPoints (KlattGrid me, integer iformant, double t1, double t2) {
2603 	FormantGrid_removeFormantPointsBetween (my coupling -> delta_formants.get(), iformant, t1, t2);
2604 }
2605 double KlattGrid_getDeltaBandwidthAtTime (KlattGrid me, integer iformant, double t) {
2606 	return FormantGrid_getBandwidthAtTime (my coupling -> delta_formants.get(), iformant, t);
2607 }
2608 void KlattGrid_addDeltaBandwidthPoint (KlattGrid me, integer iformant, double t, double value) {
2609 	FormantGrid_addBandwidthPoint (my coupling -> delta_formants.get(), iformant, t, value);
2610 }
2611 void KlattGrid_removeDeltaBandwidthPoints (KlattGrid me, integer iformant, double t1, double t2) {
2612 	FormantGrid_removeBandwidthPointsBetween (my coupling -> delta_formants.get(), iformant, t1, t2);
2613 }
2614 
2615 autoFormantGrid KlattGrid_extractDeltaFormantGrid (KlattGrid me) {
2616 	try {
2617 		autoFormantGrid* fg = KlattGrid_getAddressOfFormantGrid (me, kKlattGridFormantType::DELTA);
2618 		autoFormantGrid thee = Data_copy (fg->get());
2619 		return thee;
2620 	} catch (MelderError) {
2621 		Melder_throw (me, U": no delta FormantGrid extracted.");
2622 	}
2623 }
2624 
2625 void KlattGrid_replaceDeltaFormantGrid (KlattGrid me, FormantGrid thee) {
2626 	try {
2627 		Melder_require (my xmin == thy xmin && my xmax == thy xmax,
2628 			U"Domains should be equal");
2629 		autoFormantGrid* fg = KlattGrid_getAddressOfFormantGrid (me, kKlattGridFormantType::DELTA);
2630 		autoFormantGrid him = Data_copy (thee);
2631 		*fg = him.move();
2632 	} catch (MelderError) {
2633 		Melder_throw (me, U": no delta FormantGrid replaced.");
2634 	}
2635 }
2636 
2637 autoFormantGrid KlattGrid_to_oralFormantGrid_openPhases (KlattGrid me, double fadeFraction) {
2638 	try {
2639 		Melder_require (my vocalTract -> oral_formants -> formants.size > 0 || my vocalTract -> oral_formants -> bandwidths.size > 0,
2640 			U"Formant grid should not be empty.");
2641 
2642 		if (fadeFraction < 0.0)
2643 			fadeFraction = 0.0;
2644 		Melder_require (fadeFraction < 0.5,
2645 			U"Fade fraction should be smaller than 0.5");
2646 
2647 		my coupling -> options -> fadeFraction = fadeFraction;
2648 		autoFormantGrid thee = Data_copy ( (FormantGrid) my vocalTract -> oral_formants.get());
2649 		KlattGrid_setGlottisCoupling (me);
2650 		FormantGrid_CouplingGrid_updateOpenPhases (thee.get(), my coupling.get());
2651 		return thee;
2652 	} catch (MelderError) {
2653 		Melder_throw (me, U": no \"open phase\" oral FormantGrid created.");
2654 	}
2655 }
2656 
2657 autoPointProcess KlattGrid_extractPointProcess_glottalClosures (KlattGrid me) {
2658 	try {
2659 		// Update PhonationTier
2660 		autoPhonationTier pt = PhonationGrid_to_PhonationTier (my phonation.get());
2661 		autoPointProcess thee = PhonationTier_to_PointProcess_closures (pt.get());
2662 		return thee;
2663 	} catch (MelderError) {
2664 		Melder_throw (me, U": no glottal closure points extracted.");
2665 	}
2666 }
2667 
2668 double KlattGrid_getFricationAmplitudeAtTime (KlattGrid me, double t) {
2669 	return RealTier_getValueAtTime (my frication -> fricationAmplitude.get(), t);
2670 }
2671 
2672 void KlattGrid_addFricationAmplitudePoint (KlattGrid me, double t, double value) {
2673 	RealTier_addPoint (my frication -> fricationAmplitude.get(), t, value);
2674 }
2675 
2676 void KlattGrid_removeFricationAmplitudePoints (KlattGrid me, double t1, double t2) {
2677 	AnyTier_removePointsBetween (my frication -> fricationAmplitude.get()->asAnyTier(), t1, t2);
2678 }
2679 
2680 autoIntensityTier KlattGrid_extractFricationAmplitudeTier (KlattGrid me) {
2681 	return Data_copy (my frication -> fricationAmplitude.get());
2682 }
2683 
2684 void KlattGrid_replaceFricationAmplitudeTier (KlattGrid me, IntensityTier thee) {
2685 	try {
2686 		Melder_require (my xmin == thy xmin && my xmax == thy xmax,
2687 			U"Domains should be equal");
2688 		my frication -> fricationAmplitude = Data_copy (thee);
2689 	} catch (MelderError) {
2690 		Melder_throw (me, U": no frication amplitude tier replaced.");
2691 	}
2692 }
2693 
2694 double KlattGrid_getFricationBypassAtTime (KlattGrid me, double t) {
2695 	return RealTier_getValueAtTime (my frication -> bypass.get(), t);
2696 }
2697 
2698 void KlattGrid_addFricationBypassPoint (KlattGrid me, double t, double value) {
2699 	RealTier_addPoint (my frication -> bypass.get(), t, value);
2700 }
2701 
2702 void KlattGrid_removeFricationBypassPoints (KlattGrid me, double t1, double t2) {
2703 	AnyTier_removePointsBetween (my frication -> bypass.get()->asAnyTier(), t1, t2);
2704 }
2705 
2706 autoIntensityTier KlattGrid_extractFricationBypassTier (KlattGrid me) {
2707 	return Data_copy (my frication -> bypass.get());
2708 }
2709 
2710 void KlattGrid_replaceFricationBypassTier (KlattGrid me, IntensityTier thee) {
2711 	try {
2712 		Melder_require (my xmin == thy xmin && my xmax == thy xmax,
2713 			U"Domains should be equal");
2714 		my frication -> bypass = Data_copy (thee);
2715 	} catch (MelderError) {
2716 		Melder_throw (me, U": no frication bypass tier replaced.");
2717 	}
2718 }
2719 
2720 void KlattGrid_setGlottisCoupling (KlattGrid me) {
2721 	try {
2722 		my coupling -> glottis = PhonationGrid_to_PhonationTier (my phonation.get());
2723 		Melder_require (my coupling -> glottis,
2724 			U"Phonation tier should not be empty.");
2725 	} catch (MelderError) {
2726 		Melder_throw (me, U": no coupling could be set.");
2727 	}
2728 }
2729 
2730 #if 0
2731 static autoSound KlattGrid_to_Sound_aspiration (KlattGrid me, double samplingFrequency) {
2732 	return PhonationGrid_to_Sound_aspiration (my phonation.get(), samplingFrequency);
2733 }
2734 #endif
2735 
2736 autoSound KlattGrid_to_Sound_phonation (KlattGrid me) {
2737 	return PhonationGrid_to_Sound (my phonation.get(), 0, my options -> samplingFrequency);
2738 }
2739 
2740 autoSound KlattGrid_to_Sound (KlattGrid me) {
2741 	try {
2742 		autoSound thee;
2743 		const PhonationGridPlayOptions pp = my phonation -> options.get();
2744 		const FricationGridPlayOptions pf = my frication -> options.get();
2745 		const double samplingFrequency = my options -> samplingFrequency;
2746 
2747 		if (pp -> voicing)
2748 			KlattGrid_setGlottisCoupling (me);
2749 
2750 		if (pp -> aspiration || pp -> voicing) { // No vocal tract filtering if no glottal source signal present
2751 			autoSound source = PhonationGrid_to_Sound (my phonation.get(), my coupling.get(), samplingFrequency);
2752 			thee = Sound_VocalTractGrid_CouplingGrid_filter (source.get(), my vocalTract.get(), my coupling.get());
2753 		}
2754 
2755 		if (pf -> endFricationFormant > 0 || pf -> bypass) {
2756 			autoSound frication = FricationGrid_to_Sound (my frication.get(), samplingFrequency);
2757 			if (thee)
2758 				_Sounds_add_inplace (thee.get(), frication.get());
2759 			else
2760 				thee = frication.move();
2761 		}
2762 
2763 		if (! thee)
2764 			thee = Sound_createEmptyMono (my xmin, my xmax, samplingFrequency);
2765 
2766 		if (my options -> scalePeak)
2767 			Vector_scale (thee.get(), 0.99);
2768 
2769 		return thee;
2770 	} catch (MelderError) {
2771 		Melder_throw (me, U": no Sound created.");
2772 	}
2773 }
2774 
2775 void KlattGrid_playSpecial (KlattGrid me) {
2776 	try {
2777 		autoSound thee = KlattGrid_to_Sound (me);
2778 		const KlattGridPlayOptions him = my options.get();
2779 		if (his scalePeak)
2780 			Vector_scale (thee.get(), 0.99);
2781 		if (his xmin == 0.0 && his xmax == 0.0) {
2782 			his xmin = my xmin;
2783 			his xmax = my xmax;
2784 		}
2785 		Sound_playPart (thee.get(), his xmin, his xmax, nullptr, nullptr);
2786 	} catch (MelderError) {
2787 		Melder_throw (me, U": not played.");
2788 	}
2789 }
2790 
2791 void KlattGrid_play (KlattGrid me) {
2792 	KlattGrid_setDefaultPlayOptions (me);
2793 	KlattGrid_playSpecial (me);
2794 }
2795 
2796 /************************* Sound(s) & KlattGrid **************************************************/
2797 
2798 autoSound Sound_KlattGrid_filter_frication (Sound me, KlattGrid thee) {
2799 	return Sound_FricationGrid_filter (me, thy frication.get());
2800 }
2801 
2802 autoSound Sound_KlattGrid_filterByVocalTract (Sound me, KlattGrid thee, kKlattGridFilterModel filterModel) {
2803 	try {
2804 		Melder_require (my xmin == thy xmin && my xmax == thy xmax,
2805 						U"Domains should be equal.");
2806 		KlattGrid_setDefaultPlayOptions (thee);
2807 		thy coupling -> options -> openglottis = 0; // don't trust openglottis info!
2808 		thy vocalTract -> options -> filterModel = filterModel;
2809 		return Sound_VocalTractGrid_CouplingGrid_filter (me, thy vocalTract.get(), thy coupling.get());
2810 	} catch (MelderError) {
2811 		Melder_throw (me, U": not filtered by KlattGrid.");
2812 	}
2813 }
2814 
2815 /******************* KlattTable to KlattGrid *********************/
2816 
2817 autoKlattGrid KlattTable_to_KlattGrid (KlattTable me, double frameDuration) {
2818 	try {
2819 		Table kt = (Table) me;
2820 
2821 		const integer numberOfRows = my rows.size;
2822 		const double tmin = 0, tmax = numberOfRows * frameDuration;
2823 		constexpr double dBNul = -300;
2824 		const double dB_offset = -20.0 * log10 (2.0e-5) - 87.0; // in KlattTable maximum in DB_to_LIN is at 87 dB : 32767
2825 		const double dB_offset_voicing = 20.0 * log10 (320000 / 32767); // V' [n] in range (-320000,32000)
2826 		const double dB_offset_noise = -20.0 * log10 (32.767 / 8.192); // noise in range (-8192,8192)
2827 		//	double dB_offset_noise = -20 * log10 (320000/32767)  - 20 * log10 (32.767 / 8.192);
2828 		const double ap [7] = {0, 0.4, 0.15, 0.06, 0.04, 0.022, 0.03 };
2829 		const integer numberOfFormants = 6;
2830 		const integer numberOfNasalFormants = 1;
2831 		const integer numberOfNasalAntiFormants = numberOfNasalFormants;
2832 		const integer numberOfTrachealFormants = 0;
2833 		const integer numberOfTrachealAntiFormants = numberOfTrachealFormants;
2834 		const integer numberOfFricationFormants = 6;
2835 		const integer numberOfDeltaFormants = 1;
2836 
2837 		autoKlattGrid thee = KlattGrid_create (tmin, tmax, numberOfFormants, numberOfNasalFormants,
2838 			numberOfNasalAntiFormants, numberOfTrachealFormants, numberOfTrachealAntiFormants,
2839 			numberOfFricationFormants, numberOfDeltaFormants);
2840 		for (integer irow = 1; irow <= numberOfRows; irow ++) {
2841 			const double t = (irow - 1) * frameDuration;
2842 
2843 			integer icol = 1;
2844 			double val = Table_getNumericValue_Assert (kt, irow, icol) / 10.0;   // F0hz10
2845 			const double f0 = val;
2846 			RealTier_addPoint (thy phonation -> pitch.get(), t, f0);
2847 
2848 			val = Table_getNumericValue_Assert (kt, irow, ++ icol); // AVdb
2849 			// dB values below 13 were put to zero in the DBtoLIN function
2850 			val -= 7.0;
2851 			if (val < 13.0)
2852 				val = dBNul;
2853 
2854 			// RealTier_addPoint (thy source -> voicingAmplitude, t, val);
2855 
2856 			for (integer kf = 1; kf <= 6; kf ++) {
2857 				const double fk = val = Table_getNumericValue_Assert (kt, irow, ++ icol);   // Fhz
2858 				RealTier_addPoint (thy vocalTract -> oral_formants -> formants.at [kf], t, val);
2859 				RealTier_addPoint (thy frication -> frication_formants -> formants.at [kf], t, val);   // only amplitudes and bandwidths in frication section
2860 				val = Table_getNumericValue_Assert (kt, irow, ++ icol);   // Bhz
2861 				if (val <= 0.0)
2862 					val = fk / 10.0;
2863 				RealTier_addPoint (thy vocalTract -> oral_formants -> bandwidths.at [kf], t, val);
2864 			}
2865 
2866 			val = Table_getNumericValue_Assert (kt, irow, ++ icol);   // FNZhz
2867 			RealTier_addPoint (thy vocalTract -> nasal_antiformants -> formants.at [1], t, val);
2868 
2869 			val = Table_getNumericValue_Assert (kt, irow, ++ icol);   // BNZhz
2870 			RealTier_addPoint (thy vocalTract -> nasal_antiformants -> bandwidths.at [1], t, val);
2871 
2872 			val = Table_getNumericValue_Assert (kt, irow, ++ icol);   // FNPhz
2873 			RealTier_addPoint (thy vocalTract -> nasal_formants -> formants.at [1], t, val);
2874 
2875 			val = Table_getNumericValue_Assert (kt, irow, ++ icol);   // BNPhz
2876 			RealTier_addPoint (thy vocalTract -> nasal_formants -> bandwidths.at [1], t, val);
2877 
2878 			val = Table_getNumericValue_Assert (kt, irow, ++ icol);   // ah
2879 			if (val < 13.0)
2880 				val = dBNul;
2881 			else
2882 				val += 20.0 * log10 (0.05) + dB_offset_noise;
2883 
2884 			RealTier_addPoint (thy phonation -> aspirationAmplitude.get(), t, val);
2885 
2886 			val = Table_getNumericValue_Assert (kt, irow, ++ icol);   // Kopen
2887 			const double openPhase = ( f0 > 0.0 ? (val / 16000.0) * f0 : 0.7 );
2888 			RealTier_addPoint (thy phonation -> openPhase.get(), t, openPhase);
2889 
2890 			val = Table_getNumericValue_Assert (kt, irow, ++ icol);   // Aturb breathinessAmplitude during voicing (max is 8192)
2891 			if (val < 13.0)
2892 				val = dBNul;
2893 			else
2894 				val += 20.0 * log10 (0.1) + dB_offset_noise;
2895 
2896 			RealTier_addPoint (thy phonation -> breathinessAmplitude.get(), t, val);
2897 
2898 			val = Table_getNumericValue_Assert (kt, irow, ++ icol);   // TLTdb
2899 			RealTier_addPoint (thy phonation -> spectralTilt.get(), t, val);
2900 
2901 			val = Table_getNumericValue_Assert (kt, irow, ++ icol);   // AF
2902 			if (val < 13.0)
2903 				val = dBNul;
2904 			else
2905 				val += 20.0 * log10 (0.25) + dB_offset_noise;
2906 
2907 			RealTier_addPoint (thy frication -> fricationAmplitude.get(), t, val);
2908 
2909 			val = Table_getNumericValue_Assert (kt, irow, ++ icol);   // Kskew ???
2910 			//RealTier_addPoint (, t, val);
2911 
2912 			for (integer kf = 1; kf <= 6; kf ++) {
2913 				val = Table_getNumericValue_Assert (kt, irow, ++ icol);   // Ap
2914 				if (val < 13.0)
2915 					val = dBNul;
2916 				else
2917 					val += 20.0 * log10 (ap [kf]) + dB_offset;
2918 
2919 				RealTier_addPoint (thy vocalTract -> oral_formants_amplitudes.at [kf], t, val);
2920 				RealTier_addPoint (thy frication -> frication_formants_amplitudes.at [kf], t, val);
2921 				val = Table_getNumericValue_Assert (kt, irow, ++ icol); // Bhz
2922 				RealTier_addPoint (thy frication -> frication_formants -> bandwidths.at [kf], t, val);
2923 			}
2924 
2925 			val = Table_getNumericValue_Assert (kt, irow, ++ icol);   // ANP
2926 			if (val < 13.0)
2927 				val = dBNul;
2928 			else
2929 				val += 20.0 * log10 (0.6) + dB_offset;
2930 
2931 			RealTier_addPoint (thy vocalTract -> nasal_formants_amplitudes.at [1], t, val);
2932 
2933 			val = Table_getNumericValue_Assert (kt, irow, ++ icol); // AB
2934 			if (val < 13.0)
2935 				val = dBNul;
2936 			else
2937 				val += 20.0 * log10 (0.05) + dB_offset_noise;
2938 
2939 			RealTier_addPoint (thy frication -> bypass.get(), t, val);
2940 
2941 			val = Table_getNumericValue_Assert (kt, irow, ++ icol); // AVpdb
2942 			RealTier_addPoint (thy phonation -> voicingAmplitude.get(), t, val + dB_offset_voicing);
2943 
2944 			val = Table_getNumericValue_Assert (kt, irow, ++ icol); // Gain0
2945 			val -= 3.0;
2946 			if (val <= 0.0)
2947 				val = 57.0;
2948 			RealTier_addPoint (thy gain.get(), t, val + dB_offset);
2949 		}
2950 		// We don't need the following low-pass: we do not use oversampling !!
2951 		//RealTier_addPoint (thy tracheal_formants -> formants->at [1], 0.5*(tmin+tmax), 0.095*samplingFrequency);
2952 		//RealTier_addPoint (thy tracheal_formants -> bandwidths->at [1], 0.5*(tmin+tmax), 0.063*samplingFrequency);
2953 		return thee;
2954 	} catch (MelderError) {
2955 		Melder_throw (me, U": no KlattGrid created.");
2956 	}
2957 }
2958 
2959 autoKlattGrid Sound_to_KlattGrid_simple (Sound me, double timeStep, integer maximumNumberOfFormants, double maximumFormantFrequency, double windowLength, double preEmphasisFrequency, double minimumPitch, double maximumPitch, double pitchFloorIntensity, int subtractMean) {
2960 	try {
2961 		const integer numberOfFormants = maximumNumberOfFormants;
2962 		const integer numberOfNasalFormants = 1;
2963 		const integer numberOfNasalAntiFormants = numberOfNasalFormants;
2964 		const integer numberOfTrachealFormants = 1;
2965 		const integer numberOfTrachealAntiFormants = numberOfTrachealFormants;
2966 		const integer numberOfFricationFormants =  maximumNumberOfFormants;
2967 		const integer numberOfDeltaFormants = 1;
2968 		autoSound sound = Data_copy (me);
2969 		Vector_subtractMean (sound.get());
2970 		autoFormant f = Sound_to_Formant_burg (sound.get(), timeStep, maximumNumberOfFormants,
2971 		                                       maximumFormantFrequency, windowLength, preEmphasisFrequency);
2972 		autoFormantGrid fgrid = Formant_downto_FormantGrid (f.get());
2973 		autoPitch p = Sound_to_Pitch (sound.get(), timeStep, minimumPitch, maximumPitch);
2974 		autoPitchTier ptier = Pitch_to_PitchTier (p.get());
2975 		autoIntensity i = Sound_to_Intensity (sound.get(), pitchFloorIntensity, timeStep, subtractMean);
2976 		autoIntensityTier itier = Intensity_downto_IntensityTier (i.get());
2977 		autoKlattGrid thee = KlattGrid_create (my xmin, my xmax, numberOfFormants, numberOfNasalFormants,                            numberOfNasalAntiFormants, numberOfTrachealFormants, numberOfTrachealAntiFormants, numberOfFricationFormants, numberOfDeltaFormants);
2978 		KlattGrid_replacePitchTier (thee.get(), ptier.get());
2979 		KlattGrid_replaceFormantGrid (thee.get(), kKlattGridFormantType::ORAL, fgrid.get());
2980 		KlattGrid_replaceVoicingAmplitudeTier (thee.get(), itier.get());
2981 		return thee;
2982 	} catch (MelderError) {
2983 		Melder_throw (me, U": no simple KlattGrid created.");
2984 	}
2985 }
2986 
2987 autoKlattGrid KlattGrid_createFromVowel (double duration, double f0start, double f1, double b1, double f2, double b2, double f3, double b3, double f4, double bandWidthFraction, double formantFrequencyInterval) {
2988 	const integer numberOfOralFormants = 15;
2989 	const double tstart = 0.0;
2990 	autoKlattGrid me = KlattGrid_create (0.0, duration, numberOfOralFormants, 0, 0, 0, 0, 0, 0);
2991 	KlattGrid_addPitchPoint (me.get(), tstart, f0start);
2992 	KlattGrid_addVoicingAmplitudePoint (me.get(), tstart, 90.0);
2993 	if (f1 > 0.0) {
2994 		KlattGrid_addFormantPoint (me.get(), kKlattGridFormantType::ORAL, 1, tstart, f1);
2995 		KlattGrid_addBandwidthPoint (me.get(), kKlattGridFormantType::ORAL, 1, tstart, b1);
2996 	}
2997 	if (f2 > 0.0) {
2998 		KlattGrid_addFormantPoint (me.get(), kKlattGridFormantType::ORAL, 2, tstart, f2);
2999 		KlattGrid_addBandwidthPoint (me.get(), kKlattGridFormantType::ORAL, 2, tstart, b2);
3000 	}
3001 	if (f3 > 0) {
3002 		KlattGrid_addFormantPoint (me.get(), kKlattGridFormantType::ORAL, 3, tstart, f3);
3003 		KlattGrid_addBandwidthPoint (me.get(), kKlattGridFormantType::ORAL, 3, tstart, b3);
3004 	}
3005 	if (f4 > 0) {
3006 		KlattGrid_addFormantPoint (me.get(), kKlattGridFormantType::ORAL, 4, tstart, f4);
3007 		KlattGrid_addBandwidthPoint (me.get(), kKlattGridFormantType::ORAL, 4, tstart, f4 * bandWidthFraction);
3008 	}
3009 	if (formantFrequencyInterval > 0) {
3010 		double startFrequency = std::max (std::max (f1, f2), std::max (f3, f4));
3011 		for (integer iformant = 5; iformant <= numberOfOralFormants; iformant ++) {
3012 			double frequency =  startFrequency + (iformant - 4) * formantFrequencyInterval;
3013 			KlattGrid_addFormantPoint (me.get(), kKlattGridFormantType::ORAL, iformant, tstart, frequency);
3014 			KlattGrid_addBandwidthPoint (me.get(), kKlattGridFormantType::ORAL, iformant, tstart, frequency * bandWidthFraction);
3015 		}
3016 	}
3017 	return me;
3018 }
3019 
3020 /* End of file KlattGrid.cpp */
3021