static Strings espeak_voices_sort () { try { autoTable names = espeakdata_voices_to_Table (espeakdata_voices); autoStrings fullnames = espeakdata_voices_getNames (names.peek(), 2); FilesInMemory_and_Strings_changeIds (espeakdata_voices, fullnames.peek()); espeakdata_voices -> d_sortKey = 1; // sort id's Sorted_sort (espeakdata_voices); Table_sortRows_string (names.peek(), U"name"); //They hopefully sort the same way autoStrings neworder = espeakdata_voices_getNames (names.peek(), 2); autoStrings names_short = espeakdata_voices_getNames (names.peek(), 1); FilesInMemory_and_Strings_changeIds (espeakdata_voices, names_short.peek()); return neworder.transfer(); } catch (MelderError) { Melder_throw (U"Espeak voices not sorted."); } }
TextGrid Intensity_to_TextGrid_detectSilences (Intensity me, double silenceThreshold_dB, double minSilenceDuration, double minSoundingDuration, const char32 *silenceLabel, const char32 *soundingLabel) { try { double duration = my xmax - my xmin, time; if (silenceThreshold_dB >= 0) { Melder_throw (U"The silence threshold w.r.t. the maximum intensity should be a negative number."); } autoTextGrid thee = TextGrid_create (my xmin, my xmax, U"silences", U""); IntervalTier it = (IntervalTier) thy tiers -> item[1]; TextInterval_setText ( (TextInterval) it -> intervals -> item[1], soundingLabel); if (minSilenceDuration > duration) { return thee.transfer(); } double intensity_max_db, intensity_min_db, xOfMaximum, xOfMinimum; Vector_getMaximumAndX (me, 0, 0, 1, NUM_PEAK_INTERPOLATE_PARABOLIC, &intensity_max_db, &xOfMaximum); Vector_getMinimumAndX (me, 0, 0, 1, NUM_PEAK_INTERPOLATE_PARABOLIC, &intensity_min_db, &xOfMinimum); double intensity_dbRange = intensity_max_db - intensity_min_db; if (intensity_dbRange < 10) Melder_warning (U"The loudest and softest part in your sound only differ by ", intensity_dbRange, U" dB."); double intensityThreshold = intensity_max_db - fabs (silenceThreshold_dB); if (minSilenceDuration > duration || intensityThreshold < intensity_min_db) { return thee.transfer(); } int inSilenceInterval = my z[1][1] < intensityThreshold; long iinterval = 1; const char32 *label; for (long i = 2; i <= my nx; i++) { int addBoundary = 0; if (my z[1][i] < intensityThreshold) { if (!inSilenceInterval) { // Start of silence addBoundary = 1; inSilenceInterval = 1; label = soundingLabel; } } else { if (inSilenceInterval) { // End of silence addBoundary = 1; inSilenceInterval = 0; label = silenceLabel; } } if (addBoundary) { time = my x1 + (i - 1) * my dx; IntervalTier_addBoundaryUnsorted (it, iinterval, time, label); iinterval++; } } // (re)label last interval */ label = inSilenceInterval ? silenceLabel : soundingLabel; TextInterval_setText ( (TextInterval) it -> intervals -> item[iinterval], label); Sorted_sort (it -> intervals); // First remove short non-silence intervals in-between silence intervals and // then remove the remaining short silence intervals. // This works much better than first removing short silence intervals and // then short non-silence intervals. IntervalTier_cutIntervals_minimumDuration (it, soundingLabel, minSoundingDuration); IntervalTier_cutIntervalsOnLabelMatch (it, silenceLabel); IntervalTier_cutIntervals_minimumDuration (it, silenceLabel, minSilenceDuration); IntervalTier_cutIntervalsOnLabelMatch (it, soundingLabel); return thee.transfer(); } catch (MelderError) { Melder_throw (me, U": TextGrid not created."); } }
static autoTextGrid Table_to_TextGrid (Table me, const char32 *text, double xmin, double xmax) { //Table_createWithColumnNames (0, L"time type type-t t-pos length a-pos sample id uniq"); try { long length, textLength = str32len (text); long numberOfRows = my rows -> size; long timeColumnIndex = Table_getColumnIndexFromColumnLabel (me, U"time"); long typeColumnIndex = Table_getColumnIndexFromColumnLabel (me, U"type"); long tposColumnIndex = Table_getColumnIndexFromColumnLabel (me, U"t-pos"); long idColumnIndex = Table_getColumnIndexFromColumnLabel (me, U"id"); autoTextGrid thee = TextGrid_create (xmin, xmax, U"sentence clause word phoneme", U""); TextGrid_setIntervalText (thee.peek(), 1, 1, text); long p1c = 1, p1w = 1; double t1p = xmin; bool wordEnd = false; autoMelderString mark; IntervalTier itc = (IntervalTier) thy tiers -> item[2]; IntervalTier itw = (IntervalTier) thy tiers -> item[3]; IntervalTier itp = (IntervalTier) thy tiers -> item[4]; for (long i = 1; i <= numberOfRows; i++) { double time = Table_getNumericValue_Assert (me, i, timeColumnIndex); int type = Table_getNumericValue_Assert (me, i, typeColumnIndex); long pos = Table_getNumericValue_Assert (me, i, tposColumnIndex); if (type == espeakEVENT_SENTENCE) { // Only insert a new boundary, no text // text will be inserted at end sentence event if (time > xmin and time < xmax) { IntervalTier_addBoundaryUnsorted (itc, itc -> intervals -> size, time, U"", true); } p1c = pos; } else if (type == espeakEVENT_END) { // End of clause: insert new boundary, and fill left interval with text length = pos - p1c + 1; MelderString_ncopy (&mark, text + p1c - 1, length); MelderString_trimWhiteSpaceAtEnd (&mark); if (time > xmin and time < xmax) { IntervalTier_addBoundaryUnsorted (itc, itc -> intervals -> size, time, mark.string, true); } else { TextGrid_setIntervalText (thee.peek(), 2, itc -> intervals -> size, mark.string); } p1c = pos; // End of clause always signals "end of a word" if (pos <= textLength) { length = pos - p1w + 1; MelderString_ncopy (&mark, text + p1w - 1, length); MelderString_trimWhiteSpaceAtEnd (&mark); if (time > xmin and time < xmax) { IntervalTier_addBoundaryUnsorted (itw, itw -> intervals -> size, time, mark.string, true); } else { TextGrid_setIntervalText (thee.peek(), 3, itw -> intervals -> size, mark.string); } // now the next word event should not trigger setting the left interval text wordEnd = false; } } else if (type == espeakEVENT_WORD) { if (pos < p1w) { continue; } if (time > xmin and time < xmax) { length = pos - p1w; if (pos == textLength) length++; MelderString_ncopy (&mark, text + p1w - 1, length); MelderString_trimWhiteSpaceAtEnd (&mark); IntervalTier_addBoundaryUnsorted (itw, itw -> intervals -> size, time, (wordEnd ? mark.string : U""), true); } wordEnd = true; p1w = pos; } else if (type == espeakEVENT_PHONEME) { const char32 *id = Table_getStringValue_Assert (me, i, idColumnIndex); if (time > t1p) { // Insert new boudary and label interval with the id // TODO: Translate the id to the correct notation TextInterval ti = (TextInterval) itp -> intervals -> item[itp -> intervals -> size]; if (time > ti -> xmin and time < ti -> xmax) { IntervalTier_addBoundaryUnsorted (itp, itp -> intervals -> size, time, id, false); } } else { // Just in case the phoneme starts at xmin we only need to set interval text TextGrid_setIntervalText (thee.peek(), 4, itp -> intervals -> size, id); } t1p = time; } } Sorted_sort (itc -> intervals); Sorted_sort (itw -> intervals); Sorted_sort (itp -> intervals); return thee; } catch (MelderError) { Melder_throw (U"TextGrid not created from Table with events."); } }