static void IntervalTier_insertEmptyIntervalsFromOtherTier (IntervalTier to, IntervalTier from) { for (long iint = 1; iint <= from -> intervals.size; iint ++) { TextInterval tifrom = from -> intervals.at [iint]; if (TextInterval_labelLength (tifrom) == 0) { // found empty interval double t_left = tifrom -> xmin, t_right = tifrom -> xmax; long intervalIndex_to = IntervalTier_timeToLowIndex (to, t_left); if (intervalIndex_to > 0) { // insert to the right of intervalIndex_to TextInterval tito = to -> intervals.at [intervalIndex_to]; if (! almost_equal (tito -> xmin, t_left)) { // not on the start boundary of the interval, it cannot be at xmax autoTextInterval newInterval = TextInterval_create (t_left, tito -> xmax, U""); tito -> xmax = t_left; to -> intervals. addItem_move (newInterval.move()); } } intervalIndex_to = IntervalTier_timeToHighIndex (to, t_right); TextInterval tito = to -> intervals.at [intervalIndex_to]; if (intervalIndex_to > 0) { if (! almost_equal (t_right, tito -> xmax)) { // insert to the left of intervalIndex_to autoTextInterval newInterval = TextInterval_create (tito -> xmin, t_right, U""); tito -> xmin = t_right; to -> intervals. addItem_move (newInterval.move()); } } } } }
static void IntervalTier_add (IntervalTier me, double xmin, double xmax, const char32 *label) { long i = IntervalTier_timeToIndex (me, xmin); // xmin is in interval i if (i < 1) { Melder_throw (U"Index too low."); } autoTextInterval newti = TextInterval_create (xmin, xmax, label); TextInterval interval = (TextInterval) my intervals -> item[i]; double xmaxi = interval -> xmax; if (xmax > xmaxi) { Melder_throw (U"Don't know what to do"); // Don't know what to do } if (xmin == interval -> xmin) { if (xmax == interval -> xmax) { // interval already present TextInterval_setText (interval, label); return; } // split interval interval -> xmin = xmax; Collection_addItem (my intervals, newti.transfer()); return; } interval -> xmax = xmin; Collection_addItem (my intervals, newti.transfer()); // extra interval when xmax's are not the same if (xmax < xmaxi) { autoTextInterval newti2 = TextInterval_create (xmax, xmaxi, interval -> text); Collection_addItem (my intervals, newti2.transfer()); } }
static void IntervalTier_insertIntervalDestructively (IntervalTier me, double tmin, double tmax) { Melder_assert (tmin < tmax); Melder_assert (tmin >= my xmin); Melder_assert (tmax <= my xmax); /* * Make sure that the tier has boundaries at the edges of the interval. */ long firstIntervalNumber = IntervalTier_hasTime (me, tmin); if (! firstIntervalNumber) { long intervalNumber = IntervalTier_timeToIndex (me, tmin); if (intervalNumber == 0) Melder_throw (U"Cannot add a boundary at ", Melder_fixed (tmin, 6), U" seconds, because this is outside the time domain of the intervals."); TextInterval interval = my intervals.at [intervalNumber]; /* * Move the text to the left of the boundary. */ autoTextInterval newInterval = TextInterval_create (tmin, interval -> xmax, U""); interval -> xmax = tmin; my intervals. addItem_move (newInterval.move()); firstIntervalNumber = IntervalTier_hasTime (me, interval -> xmin); } Melder_assert (firstIntervalNumber >= 1 && firstIntervalNumber <= my intervals.size); long lastIntervalNumber = IntervalTier_hasTime (me, tmax); if (! lastIntervalNumber) { long intervalNumber = IntervalTier_timeToIndex (me, tmax); if (intervalNumber == 0) Melder_throw (U"Cannot add a boundary at ", Melder_fixed (tmin, 6), U" seconds, because this is outside the time domain of the intervals."); TextInterval interval = my intervals.at [intervalNumber]; /* * Move the text to the right of the boundary. */ autoTextInterval newInterval = TextInterval_create (interval -> xmin, tmax, U""); interval -> xmin = tmax; my intervals. addItem_move (newInterval.move()); lastIntervalNumber = IntervalTier_hasTime (me, interval -> xmax); } Melder_assert (lastIntervalNumber >= 1 && lastIntervalNumber <= my intervals.size); /* * Empty the interval in the word tier. */ trace (U"Empty interval %ld down to ", lastIntervalNumber, U".", firstIntervalNumber); for (long iinterval = lastIntervalNumber; iinterval >= firstIntervalNumber; iinterval --) { TextInterval interval = my intervals.at [iinterval]; if (interval -> xmin > tmin && interval -> xmin < tmax) { Melder_assert (iinterval > 1); TextInterval previous = my intervals.at [iinterval - 1]; previous -> xmax = tmax; // collapse left and right intervals into left interval TextInterval_setText (previous, U""); my intervals. removeItem (iinterval); // remove right interval } if (interval -> xmax == tmax) { TextInterval_setText (interval, U""); } } }
// Precondition: if (preserveTimes) { my xmax <= thy xmin } // Postcondition: my xmin preserved void IntervalTiers_append_inline (IntervalTier me, IntervalTier thee, bool preserveTimes) { try { double xmax_previous = my xmax, time_shift = my xmax - thy xmin; if (preserveTimes && my xmax < thy xmin) { autoTextInterval connection = TextInterval_create (my xmax, thy xmin, L""); xmax_previous = thy xmin; Collection_addItem (my intervals, connection.transfer()); } for (long iint = 1; iint <= thy intervals -> size; iint++) { autoTextInterval ti = (TextInterval) Data_copy ((Data) thy intervals -> item[iint]); if (preserveTimes) { Collection_addItem (my intervals, ti.transfer()); } else { /* the interval could be so short that if we test ti -> xmin < ti->xmax it might be true * but after assigning ti->xmin = xmax_previous and ti->xmax += time_shift the test * ti -> xmin < ti->xmax might be false! * We want to make sure xmin and xmax are not register variables and therefore force double64 * by using volatile variables. */ volatile double xmin = xmax_previous; volatile double xmax = ti -> xmax + time_shift; if (xmin < xmax) { ti -> xmin = xmin; ti -> xmax = xmax; Collection_addItem (my intervals, ti.transfer()); xmax_previous = xmax; } // else don't include interval } } my xmax = preserveTimes ? thy xmax : xmax_previous; } catch (MelderError) { Melder_throw ("IntervalTiers not appended."); } }
void IntervalTier_setEarlierStartTime (IntervalTier me, double xmin, const char32 *mark) { try { if (xmin >= my xmin) return; // nothing to be done TextInterval ti = (TextInterval) my intervals -> item[1]; Melder_assert (xmin < ti -> xmin); if (mark != NULL) { autoTextInterval interval = TextInterval_create (xmin, ti -> xmin, mark); Collection_addItem (my intervals, interval.transfer()); } else { // extend first interval ti -> xmin = xmin; } my xmin = xmin; } catch (MelderError) { Melder_throw (U"Earlier start time of IntervalTier not set."); } }
void IntervalTier_setLaterEndTime (IntervalTier me, double xmax, const char32 *mark) { try { if (xmax <= my xmax) return; // nothing to be done TextInterval ti = (TextInterval) my intervals -> item[my intervals -> size]; Melder_assert (xmax > ti -> xmax); if (mark != NULL) { autoTextInterval interval = TextInterval_create (ti -> xmax, xmax, mark); Collection_addItem (my intervals, interval.transfer()); } else { // extend last interval ti -> xmax = xmax; } my xmax = xmax; } catch (MelderError) { Melder_throw (U"Larger end time of IntervalTier not set."); } }
static void IntervalTier_addBoundaryUnsorted (IntervalTier me, long iinterval, double time, const char32 *leftLabel) { if (time <= my xmin || time >= my xmax) { Melder_throw (U"Time is outside interval."); } // Find interval to split if (iinterval <= 0) { iinterval = IntervalTier_timeToLowIndex (me, time); } // Modify end time of left label TextInterval ti = (TextInterval) my intervals -> item[iinterval]; ti -> xmax = time; TextInterval_setText (ti, leftLabel); autoTextInterval ti_new = TextInterval_create (time, my xmax, U""); Sorted_addItem_unsorted (my intervals, ti_new.transfer()); }
static void IntervalTier_addBoundaryUnsorted (IntervalTier me, long iinterval, double time, const char32 *newLabel, bool isNewleftLabel) { if (time <= my xmin || time >= my xmax) { Melder_throw (U"Time is outside interval domains."); } // Find interval to split if (iinterval <= 0) { iinterval = IntervalTier_timeToLowIndex (me, time); } // Modify end time of left label TextInterval ti = my intervals.at [iinterval]; ti -> xmax = time; if (isNewleftLabel) TextInterval_setText (ti, newLabel); autoTextInterval ti_new = TextInterval_create (time, my xmax, (! isNewleftLabel ? newLabel : U"" )); my intervals. addItem_unsorted_move (ti_new.move()); }
void TextGrid_extendTime (TextGrid me, double extra_time, int position) { autoTextGrid thee = 0; try { double xmax = my xmax, xmin = my xmin; int at_end = position == 0; if (extra_time == 0) { return; } extra_time = fabs (extra_time); // Just in case... thee.reset (Data_copy (me)); if (at_end) { xmax += extra_time; } else { xmin -= extra_time; } for (long i = 1; i <= my tiers -> size; i++) { Function anyTier = (Function) my tiers -> item [i]; double tmin = anyTier -> xmin, tmax = anyTier -> xmax; if (at_end) { anyTier -> xmax = xmax; tmin = tmax; tmax = xmax; } else { anyTier -> xmin = xmin; tmax = tmin; tmin = xmin; } if (anyTier -> classInfo == classIntervalTier) { IntervalTier tier = (IntervalTier) anyTier; autoTextInterval interval = TextInterval_create (tmin, tmax, U""); Collection_addItem (tier -> intervals, interval.transfer()); } } my xmin = xmin; my xmax = xmax; } catch (MelderError) { Melder_throw (me, U": time not extended."); } }
// Precondition: if (preserveTimes) { my xmax <= thy xmin } // Postcondition: my xmin preserved void IntervalTiers_append_inline (IntervalTier me, IntervalTier thee, bool preserveTimes) { try { double xmax_previous = my xmax, time_shift = my xmax - thy xmin; if (preserveTimes && my xmax < thy xmin) { autoTextInterval connection = TextInterval_create (my xmax, thy xmin, L""); xmax_previous = thy xmin; Collection_addItem (my intervals, connection.transfer()); } for (long iint = 1; iint <= thy intervals -> size; iint++) { autoTextInterval ti = (TextInterval) Data_copy ((Data) thy intervals -> item[iint]); if (not preserveTimes) { ti -> xmin = xmax_previous; xmax_previous = ti -> xmax += time_shift; } Collection_addItem (my intervals, ti.transfer()); } my xmax = preserveTimes ? thy xmax : xmax_previous; } catch (MelderError) { Melder_throw ("IntervalTiers not appended."); } }
void IntervalTier_splitInterval (IntervalTier me, double time, const char32 *leftLabel, long interval, double precision) { try { Melder_assert (interval > 0); TextInterval ti = nullptr; long index = 0; for (long i = interval; i <= my intervals.size; i ++) { ti = my intervals.at [i]; if (time < ti -> xmax + precision && time > ti -> xmin - precision) { index = i; break; } } // if index == 0 then search left intervals?? if (index == 0 || TIMES_ARE_CLOSE(time, ti -> xmin) || TIMES_ARE_CLOSE(time, ti -> xmax)) { return; } autoTextInterval newInterval = TextInterval_create (ti -> xmin, time, leftLabel); // Make start of current and begin of new interval equal ti -> xmin = time; my intervals. addItem_move (newInterval.move()); } catch (MelderError) { Melder_throw (U"Boundary not inserted."); } }
void TextGrid_anySound_alignInterval (TextGrid me, Function anySound, long tierNumber, long intervalNumber, const char32 *languageName, bool includeWords, bool includePhonemes) { try { IntervalTier headTier = TextGrid_checkSpecifiedTierIsIntervalTier (me, tierNumber); if (intervalNumber < 1 || intervalNumber > headTier -> intervals.size) Melder_throw (U"Interval ", intervalNumber, U" does not exist."); TextInterval interval = headTier -> intervals.at [intervalNumber]; if (! includeWords && ! includePhonemes) Melder_throw (U"Nothing to be done, because you asked neither for word alignment nor for phoneme alignment."); if (str32str (headTier -> name, U"/") ) Melder_throw (U"The current tier already has a slash (\"/\") in its name. Cannot create a word or phoneme tier from it."); autoSound part = anySound -> classInfo == classLongSound ? LongSound_extractPart (static_cast <LongSound> (anySound), interval -> xmin, interval -> xmax, true) : Sound_extractPart (static_cast <Sound> (anySound), interval -> xmin, interval -> xmax, kSound_windowShape_RECTANGULAR, 1.0, true); autoSpeechSynthesizer synthesizer = SpeechSynthesizer_create (languageName, U"default"); double silenceThreshold = -35, minSilenceDuration = 0.1, minSoundingDuration = 0.1; autoTextGrid analysis; if (! Melder_equ (interval -> text, U"")) { try { analysis = SpeechSynthesizer_and_Sound_and_TextInterval_align (synthesizer.get(), part.get(), interval, silenceThreshold, minSilenceDuration, minSoundingDuration); } catch (MelderError) { Melder_clearError (); // ignore all error messages from DTW and the like } } if (analysis) { /* * Clean up the analysis. */ Melder_assert (analysis -> xmin == interval -> xmin); Melder_assert (analysis -> xmax == interval -> xmax); Melder_assert (analysis -> tiers->size == 4); Thing_cast (IntervalTier, analysisWordTier, analysis -> tiers->at [3]); if (! IntervalTier_check (analysisWordTier)) Melder_throw (U"Analysis word tier out of order."); IntervalTier_removeEmptyIntervals (analysisWordTier, nullptr); Melder_assert (analysisWordTier -> xmax == analysis -> xmax); Melder_assert (analysisWordTier -> intervals.size >= 1); TextInterval firstInterval = analysisWordTier -> intervals.at [1]; TextInterval lastInterval = analysisWordTier -> intervals.at [analysisWordTier -> intervals.size]; firstInterval -> xmin = analysis -> xmin; lastInterval -> xmax = analysis -> xmax; if (lastInterval -> xmax != analysis -> xmax) Melder_fatal (U"analysis ends at ", analysis -> xmax, U", but last interval at ", lastInterval -> xmax, U" seconds"); if (! IntervalTier_check (analysisWordTier)) Melder_throw (U"Analysis word tier out of order (2)."); Thing_cast (IntervalTier, analysisPhonemeTier, analysis -> tiers->at [4]); if (! IntervalTier_check (analysisPhonemeTier)) Melder_throw (U"Analysis phoneme tier out of order."); IntervalTier_removeEmptyIntervals (analysisPhonemeTier, analysisWordTier); Melder_assert (analysisPhonemeTier -> xmax == analysis -> xmax); Melder_assert (analysisPhonemeTier -> intervals.size >= 1); firstInterval = analysisPhonemeTier -> intervals.at [1]; lastInterval = analysisPhonemeTier -> intervals.at [analysisPhonemeTier -> intervals.size]; firstInterval -> xmin = analysis -> xmin; lastInterval -> xmax = analysis -> xmax; Melder_assert (lastInterval -> xmax == analysis -> xmax); if (! IntervalTier_check (analysisPhonemeTier)) Melder_throw (U"Analysis phoneme tier out of order (2)."); } long wordTierNumber = 0, phonemeTierNumber = 0; IntervalTier wordTier = nullptr, phonemeTier = nullptr; /* * Include a word tier. */ if (includeWords) { /* * Make sure that the word tier exists. */ autoMelderString newWordTierName; MelderString_copy (& newWordTierName, headTier -> name, U"/word"); for (long itier = 1; itier <= my tiers->size; itier ++) { IntervalTier tier = static_cast <IntervalTier> (my tiers->at [itier]); if (Melder_equ (newWordTierName.string, tier -> name)) { if (tier -> classInfo != classIntervalTier) Melder_throw (U"A tier with the prospective word tier name (", tier -> name, U") already exists, but it is not an interval tier." U"\nPlease change its name or remove it."); wordTierNumber = itier; break; } } if (! wordTierNumber) { autoIntervalTier newWordTier = IntervalTier_create (my xmin, my xmax); Thing_setName (newWordTier.get(), newWordTierName.string); my tiers -> addItemAtPosition_move (newWordTier.move(), wordTierNumber = tierNumber + 1); } Melder_assert (wordTierNumber >= 1 && wordTierNumber <= my tiers->size); wordTier = static_cast <IntervalTier> (my tiers->at [wordTierNumber]); /* * Make sure that the word tier has boundaries at the edges of the interval. */ IntervalTier_insertIntervalDestructively (wordTier, interval -> xmin, interval -> xmax); /* * Copy the contents of the word analysis into the interval in the word tier. */ long wordIntervalNumber = IntervalTier_hasTime (wordTier, interval -> xmin); Melder_assert (wordIntervalNumber != 0); if (analysis) { Thing_cast (IntervalTier, analysisWordTier, analysis -> tiers->at [3]); if (! IntervalTier_check (analysisWordTier)) Melder_throw (U"Analysis word tier out of order (3)."); if (! IntervalTier_check (wordTier)) Melder_throw (U"Word tier out of order (3)."); for (long ianalysisInterval = 1; ianalysisInterval <= analysisWordTier -> intervals.size; ianalysisInterval ++) { TextInterval analysisInterval = analysisWordTier -> intervals.at [ianalysisInterval]; TextInterval wordInterval = nullptr; double tmin = analysisInterval -> xmin, tmax = analysisInterval -> xmax; if (tmax == analysis -> xmax) { wordInterval = wordTier -> intervals.at [wordIntervalNumber]; TextInterval_setText (wordInterval, analysisInterval -> text); } else { wordInterval = wordTier -> intervals.at [wordIntervalNumber]; autoTextInterval newInterval = TextInterval_create (tmin, tmax, analysisInterval -> text); wordInterval -> xmin = tmax; wordTier -> intervals. addItem_move (newInterval.move()); wordIntervalNumber ++; } } if (! IntervalTier_check (analysisWordTier)) Melder_throw (U"Analysis word tier out of order (4)."); if (! IntervalTier_check (wordTier)) Melder_throw (U"Word tier out of order (4)."); } } /* * Include a phoneme tier. */ if (includePhonemes) { /* * Make sure that the phoneme tier exists. */ autoMelderString newPhonemeTierName; MelderString_copy (& newPhonemeTierName, headTier -> name, U"/phon"); for (long itier = 1; itier <= my tiers->size; itier ++) { IntervalTier tier = (IntervalTier) my tiers->at [itier]; if (Melder_equ (newPhonemeTierName.string, tier -> name)) { if (tier -> classInfo != classIntervalTier) Melder_throw (U"A tier with the prospective phoneme tier name (", tier -> name, U") already exists, but it is not an interval tier." U"\nPlease change its name or remove it."); phonemeTierNumber = itier; break; } } if (! phonemeTierNumber) { autoIntervalTier newPhonemeTier = IntervalTier_create (my xmin, my xmax); Thing_setName (newPhonemeTier.get(), newPhonemeTierName.string); my tiers -> addItemAtPosition_move (newPhonemeTier.move(), phonemeTierNumber = wordTierNumber ? wordTierNumber + 1 : tierNumber + 1); } Melder_assert (phonemeTierNumber >= 1 && phonemeTierNumber <= my tiers->size); phonemeTier = static_cast <IntervalTier> (my tiers->at [phonemeTierNumber]); /* * Make sure that the phoneme tier has boundaries at the edges of the interval. */ IntervalTier_insertIntervalDestructively (phonemeTier, interval -> xmin, interval -> xmax); /* * Copy the contents of the phoneme analysis into the interval in the phoneme tier. */ long phonemeIntervalNumber = IntervalTier_hasTime (phonemeTier, interval -> xmin); Melder_assert (phonemeIntervalNumber != 0); if (analysis.get()) { Thing_cast (IntervalTier, analysisPhonemeTier, analysis -> tiers->at [4]); for (long ianalysisInterval = 1; ianalysisInterval <= analysisPhonemeTier -> intervals.size; ianalysisInterval ++) { TextInterval analysisInterval = analysisPhonemeTier -> intervals.at [ianalysisInterval]; TextInterval phonemeInterval = nullptr; double tmin = analysisInterval -> xmin, tmax = analysisInterval -> xmax; if (tmax == analysis -> xmax) { phonemeInterval = phonemeTier -> intervals.at [phonemeIntervalNumber]; TextInterval_setText (phonemeInterval, analysisInterval -> text); } else { phonemeInterval = phonemeTier -> intervals.at [phonemeIntervalNumber]; autoTextInterval newInterval = TextInterval_create (tmin, tmax, analysisInterval -> text); phonemeInterval -> xmin = tmax; phonemeTier -> intervals. addItem_move (newInterval.move()); phonemeIntervalNumber ++; } } } if (includeWords) { /* * Synchronize the boundaries between the word tier and the phoneme tier. */ //for (long iinterval = 1; iinterval <= } } } catch (MelderError) { Melder_throw (me, U" & ", anySound, U": interval not aligned."); } }