static autoIntervalTier IntervalTiers_patch (IntervalTier me, IntervalTier thee, const char32 *patchLabel, double precision) {
    try {
        autoIntervalTier him = IntervalTier_create (thy xmin, thy xmax);
        long myInterval = 1, hisInterval = 1;
        double xmax = thy xmin;
        for (long i = 1; i <= thy intervals.size; i ++) {
            TextInterval myti, ti = thy intervals.at [i];
            if (Melder_equ (ti -> text, patchLabel)) {
                bool splitInterval = false; double endtime, split = 0;
                if (i > 0) {
                    while (myInterval <= my intervals.size) {
                        myti = my intervals.at [myInterval];
                        endtime = xmax + myti -> xmax - myti -> xmin;
                        if (endtime <= ti -> xmin + precision) {
                            xmax = endtime;
                            IntervalTier_splitInterval (him.peek(), xmax, myti -> text, hisInterval, precision);
                            hisInterval++;
                        } else {
                            if (xmax < ti -> xmin - precision) { // split interval ???
                                splitInterval = true;
                                xmax = ti -> xmin;
                                split = endtime - xmax;
                                IntervalTier_splitInterval (him.peek(), xmax, myti -> text, hisInterval, precision);
                                hisInterval ++; myInterval++;
                            }
                            break;
                        }
                        myInterval++;
                    }
                }
                xmax += ti -> xmax - ti -> xmin;
                IntervalTier_splitInterval (him.peek(), xmax, U"", hisInterval, precision);
                hisInterval++;
                if (splitInterval) {
                    xmax += split;
                    IntervalTier_splitInterval (him.peek(), xmax, myti -> text, hisInterval, precision);
                    hisInterval ++;
                }
            } else if (i == thy intervals.size) { // copy remaining if last interval doesn't match
                while (myInterval <= my intervals.size) {
                    myti = my intervals.at [myInterval];
                    xmax += myti -> xmax - myti -> xmin;
                    IntervalTier_splitInterval (him.peek(), xmax, myti -> text, hisInterval, precision);
                    hisInterval++;
                    myInterval++;
                }
            }
        }
        return him;
    } catch (MelderError) {
        Melder_throw (me, U": not patched.");
    }
}
예제 #2
0
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.");
	}
}
// Patch thy intervals that match patchLabel into my intervals
// The resulting IntervalTier has thy xmin as starting time and thy xmax as end time
autoIntervalTier IntervalTiers_patch_noBoundaries (IntervalTier me, IntervalTier thee, const char32 *patchLabel, double precision) {
    try {
		autoNUMvector<double> durations (0L, my intervals.size + 1);
		for (long i = 1; i <= my intervals.size; i ++) {
			TextInterval myti = my intervals.at [i];
			durations [i] = myti -> xmax - myti -> xmin;
		}
		long myInterval = 1;
		double xShift = thy xmin - my xmin;
        for (long j = 1; j <= thy intervals.size; j ++) {
            TextInterval patch = thy intervals.at [j];
            if (Melder_equ (patch -> text, patchLabel)) {
				if (j == 1) {
					xShift += durations[0] = patch -> xmax - patch -> xmin;
				} else if (j == thy intervals.size) {
					durations [my intervals.size + 1] = patch -> xmax - patch -> xmin;
				} else {
					while (myInterval <= my intervals.size) {
						TextInterval ti = my intervals.at [myInterval];
						double tixmin = ti -> xmin + xShift;
						double tixmax = ti -> xmax + xShift;
						if ((patch -> xmin > tixmin - precision) && (patch -> xmin < tixmax + precision)) {
							durations[myInterval] += patch -> xmax - patch -> xmin;
							break;
						}
						myInterval++;
					}
				}
            } else {
				while (myInterval <= my intervals.size) {
					TextInterval ti = my intervals.at [myInterval];
					double tixmax = ti -> xmax + xShift;
					if (tixmax < patch -> xmin + precision) {
						myInterval++;
					} else {
						break;
					}
				}
			}
        }
        autoIntervalTier him = IntervalTier_create (thy xmin, thy xmax);
        // first interval
		double time = thy xmin + durations[0];
		long hisInterval = 1;
		if (durations [0] > 0) {
			IntervalTier_splitInterval (him.peek(), time , U"", hisInterval, precision);
			hisInterval++;
		}
		for (long i = 1; i <= my intervals.size; i ++) {
			TextInterval ti = my intervals.at [i];
			time += durations [i];
			IntervalTier_splitInterval (him.peek(), time, ti -> text, hisInterval, precision);
			hisInterval++;
		}
		if (durations [my intervals.size + 1] > 0) {
			time += durations [my intervals.size + 1];
			IntervalTier_splitInterval (him.peek(), time , U"", hisInterval, precision);
		}
        return him;
    } catch (MelderError) {
        Melder_throw (me, U": not patched.");
    }
}
// Cut parts from me marked by labels in thee
autoIntervalTier IntervalTier_and_IntervalTier_cutPartsMatchingLabel (IntervalTier me, IntervalTier thee, const char32 *label, double precision) {
    try {
        if (my xmin != thy xmin || my xmax != thy xmax) {
            Melder_throw (U"Domains must be equal.");
        }
        autoNUMvector<double> durations (1, my intervals.size);
        for (long i = 1; i <= my intervals.size; i ++) {
            TextInterval ti = my intervals.at [i];
            durations[i] = ti -> xmax - ti -> xmin;
        }
        long myInterval = 1;
        for (long j = 1; j <= thy intervals.size; j ++) {
            TextInterval cut = thy intervals.at [j];
            if (Melder_equ (cut -> text, label)) { // trim
                while (myInterval <= my intervals.size) {
                    TextInterval ti = my intervals.at [myInterval];
                    if (ti -> xmin > cut -> xmin - precision && ti -> xmax < cut -> xmax + precision) {
                        // 1. interval completely within cut
                        durations[myInterval] = 0;
                        myInterval++;
                    } else if (ti -> xmin < cut -> xmin + precision && cut -> xmin < ti -> xmax + precision) {
                        // 2. cut start is within interval
                        if (cut -> xmax > ti -> xmax - precision) {
                            // interval end is in cut, interval start before
                            durations[myInterval] -= ti -> xmax - cut -> xmin;
                            myInterval++;
                        } else {
                            // 3. cut completely within interval
                            durations[myInterval] -= cut -> xmax - cut -> xmin;
                            break;
                        }
                    } else if (cut -> xmax > ti -> xmin - precision && cut -> xmin < ti -> xmax + precision) {
                        // +1+2 : cut end is within interval, cut start before
                        durations[myInterval] -= cut -> xmax - ti -> xmin;
                        break;
                    } else if (ti -> xmax < cut -> xmin + precision) {
                        myInterval++;
                    }
                }
            }
        }
        double totalDuration = 0;
        for (long i = 1; i <= my intervals.size; i ++) {
            if (durations[i] < precision) {
                durations[i] = 0;
            }
            totalDuration += durations[i];
        }
        autoIntervalTier him = IntervalTier_create (0, totalDuration);
        double time = 0; long hisInterval = 1;
        for (long i = 1; i <= my intervals.size; i ++) {
            if (durations[i] <= 0) continue;
            TextInterval ti = my intervals.at [i];
            time += durations[i];
            if (fabs (time - totalDuration) > precision) {
                IntervalTier_splitInterval (him.peek(), time, ti -> text, hisInterval, precision);
                hisInterval++;
            } else { // last interval
                TextInterval histi = his intervals.at [hisInterval];
                TextInterval_setText (histi, ti -> text);
            }
        }
        return him;
    } catch (MelderError) {
        Melder_throw (me, U": parts not cut.");
    }
}