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