static void Table_setEventTypeString (Table me) { try { for (long i = 1; i <= my rows.size; i ++) { int type = Table_getNumericValue_Assert (me, i, 2); const char32 *label = U"0"; if (type == espeakEVENT_WORD) { label = U"word"; } else if (type == espeakEVENT_SENTENCE) { label = U"sent"; } else if (type == espeakEVENT_MARK) { label = U"mark"; } else if (type == espeakEVENT_PLAY) { label = U"play"; } else if (type == espeakEVENT_END) { label = U"s-end"; } else if (type == espeakEVENT_MSG_TERMINATED) { label = U"msg_term"; } else if (type == espeakEVENT_PHONEME) { label = U"phoneme"; } Table_setStringValue (me, i, 3, label); } } catch (MelderError) { Melder_throw (U"Event types not set."); } }
LinearRegression Table_to_LinearRegression (Table me) { try { long numberOfIndependentVariables = my numberOfColumns - 1, numberOfParameters = my numberOfColumns; long numberOfCells = my rows -> size, icell, ivar; if (numberOfParameters < 1) /* Includes intercept. */ Melder_throw (U"Not enough columns (has to be more than 1)."); if (numberOfCells < numberOfParameters) { Melder_warning (U"Solution is not unique (more parameters than cases)."); } autoNUMmatrix <double> u (1, numberOfCells, 1, numberOfParameters); autoNUMvector <double> b (1, numberOfCells); autoNUMvector <double> x (1, numberOfParameters); autoLinearRegression thee = LinearRegression_create (); for (ivar = 1; ivar <= numberOfIndependentVariables; ivar ++) { double minimum = Table_getMinimum (me, ivar); double maximum = Table_getMaximum (me, ivar); Regression_addParameter (thee.peek(), my columnHeaders [ivar]. label, minimum, maximum, 0.0); } for (icell = 1; icell <= numberOfCells; icell ++) { for (ivar = 1; ivar < numberOfParameters; ivar ++) { u [icell] [ivar] = Table_getNumericValue_Assert (me, icell, ivar); } u [icell] [numberOfParameters] = 1.0; /* For the intercept. */ b [icell] = Table_getNumericValue_Assert (me, icell, my numberOfColumns); // the dependent variable } NUMsolveEquation (u.peek(), numberOfCells, numberOfParameters, b.peek(), NUMeps * numberOfCells, x.peek()); thy intercept = x [numberOfParameters]; for (ivar = 1; ivar <= numberOfIndependentVariables; ivar ++) { RegressionParameter parm = static_cast<RegressionParameter> (thy parameters -> item [ivar]); parm -> value = x [ivar]; } return thee.transfer(); } catch (MelderError) { Melder_throw (me, U": linear regression not performed."); } }
static LogisticRegression _Table_to_LogisticRegression (Table me, long *factors, long numberOfFactors, long dependent1, long dependent2) { long numberOfParameters = numberOfFactors + 1; long numberOfCells = my rows -> size, numberOfY0 = 0, numberOfY1 = 0, numberOfData = 0; double logLikelihood = 1e300, previousLogLikelihood = 2e300; if (numberOfParameters < 1) // includes intercept Melder_throw ("Not enough columns (has to be more than 1)."); /* * Divide up the contents of the table into a number of independent variables (x) and two dependent variables (y0 and y1). */ autoNUMmatrix <double> x (1, numberOfCells, 0, numberOfFactors); // column 0 is the intercept autoNUMvector <double> y0 (1, numberOfCells); autoNUMvector <double> y1 (1, numberOfCells); autoNUMvector <double> meanX (1, numberOfFactors); autoNUMvector <double> stdevX (1, numberOfFactors); autoNUMmatrix <double> smallMatrix (0, numberOfFactors, 0, numberOfParameters); autoLogisticRegression thee = LogisticRegression_create (my columnHeaders [dependent1]. label, my columnHeaders [dependent2]. label); for (long ivar = 1; ivar <= numberOfFactors; ivar ++) { double minimum = Table_getMinimum (me, factors [ivar]); double maximum = Table_getMaximum (me, factors [ivar]); Regression_addParameter (thee.peek(), my columnHeaders [factors [ivar]]. label, minimum, maximum, 0.0); } for (long icell = 1; icell <= numberOfCells; icell ++) { y0 [icell] = Table_getNumericValue_Assert (me, icell, dependent1); y1 [icell] = Table_getNumericValue_Assert (me, icell, dependent2); numberOfY0 += y0 [icell]; numberOfY1 += y1 [icell]; numberOfData += y0 [icell] + y1 [icell]; x [icell] [0] = 1.0; /* Intercept. */ for (long ivar = 1; ivar <= numberOfFactors; ivar ++) { x [icell] [ivar] = Table_getNumericValue_Assert (me, icell, factors [ivar]); meanX [ivar] += x [icell] [ivar] * (y0 [icell] + y1 [icell]); } } if (numberOfY0 == 0 && numberOfY1 == 0) Melder_throw ("No data in either class. Cannot determine result."); if (numberOfY0 == 0) Melder_throw ("No data in class ", my columnHeaders [dependent1]. label, ". Cannot determine result."); if (numberOfY1 == 0) Melder_throw ("No data in class ", my columnHeaders [dependent2]. label, ". Cannot determine result."); /* * Normalize the data. */ for (long ivar = 1; ivar <= numberOfFactors; ivar ++) { meanX [ivar] /= numberOfData; for (long icell = 1; icell <= numberOfCells; icell ++) { x [icell] [ivar] -= meanX [ivar]; } } for (long icell = 1; icell <= numberOfCells; icell ++) { for (long ivar = 1; ivar <= numberOfFactors; ivar ++) { stdevX [ivar] += x [icell] [ivar] * x [icell] [ivar] * (y0 [icell] + y1 [icell]); } } for (long ivar = 1; ivar <= numberOfFactors; ivar ++) { stdevX [ivar] = sqrt (stdevX [ivar] / numberOfData); for (long icell = 1; icell <= numberOfCells; icell ++) { x [icell] [ivar] /= stdevX [ivar]; } } /* * Initial state of iteration: the null model. */ thy intercept = log ((double) numberOfY1 / (double) numberOfY0); // initial state of intercept: best guess for average log odds for (long ivar = 1; ivar <= numberOfFactors; ivar ++) { RegressionParameter parm = static_cast<RegressionParameter> (thy parameters -> item [ivar]); parm -> value = 0.0; // initial state of dependence: none } long iteration = 1; for (; iteration <= 100; iteration ++) { previousLogLikelihood = logLikelihood; for (long ivar = 0; ivar <= numberOfFactors; ivar ++) { for (long jvar = ivar; jvar <= numberOfParameters; jvar ++) { smallMatrix [ivar] [jvar] = 0.0; } } /* * Compute the current log likelihood. */ logLikelihood = 0.0; for (long icell = 1; icell <= numberOfCells; icell ++) { double fittedLogit = thy intercept, fittedP, fittedQ, fittedLogP, fittedLogQ, fittedPQ, fittedVariance; for (long ivar = 1; ivar <= numberOfFactors; ivar ++) { RegressionParameter parm = static_cast<RegressionParameter> (thy parameters -> item [ivar]); fittedLogit += parm -> value * x [icell] [ivar]; } /* * Basically we have fittedP = 1.0 / (1.0 + exp (- fittedLogit)), * but that works neither for fittedP values near 0 nor for values near 1. */ if (fittedLogit > 15.0) { /* * For large fittedLogit, fittedLogP = ln (1/(1+exp(-fittedLogit))) = -ln (1+exp(-fittedLogit)) =~ - exp(-fittedLogit) */ fittedLogP = - exp (- fittedLogit); fittedLogQ = - fittedLogit; fittedPQ = exp (- fittedLogit); fittedP = exp (fittedLogP); fittedQ = 1.0 - fittedP; } else if (fittedLogit < -15.0) { fittedLogP = fittedLogit; fittedLogQ = - exp (fittedLogit); fittedPQ = exp (fittedLogit); fittedP = exp (fittedLogP); fittedQ = 1 - fittedP; } else { fittedP = 1.0 / (1.0 + exp (- fittedLogit)); fittedLogP = log (fittedP); fittedQ = 1.0 - fittedP; fittedLogQ = log (fittedQ); fittedPQ = fittedP * fittedQ; } logLikelihood += -2 * (y1 [icell] * fittedLogP + y0 [icell] * fittedLogQ); /* * Matrix shifting stuff. * Suppose a + b Sk + c Tk = ln (pk / qk), * where {a, b, c} are the coefficients to be optimized, * Sk and Tk are properties of stimulus k, * and pk and qk are the fitted probabilities for y1 and y0, respectively, given stimulus k. * Then ln pk = - ln (1 + qk / pk) = - ln (1 + exp (- (a + b Sk + c Tk))) * d ln pk / da = 1 / (1 + exp (a + b Sk + c Tk)) = qk * d ln pk / db = qk Sk * d ln pk / dc = qk Tk * d ln qk / da = - pk * Now LL = Sum(k) (y1k ln pk + y0k ln qk) * so that dLL/da = Sum(k) (y1k d ln pk / da + y0k ln qk / da) = Sum(k) (y1k qk - y0k pk) */ fittedVariance = fittedPQ * (y0 [icell] + y1 [icell]); for (long ivar = 0; ivar <= numberOfFactors; ivar ++) { /* * The last column gets the gradient of LL: dLL/da, dLL/db, dLL/dc. */ smallMatrix [ivar] [numberOfParameters] += x [icell] [ivar] * (y1 [icell] * fittedQ - y0 [icell] * fittedP); for (long jvar = ivar; jvar <= numberOfFactors; jvar ++) { smallMatrix [ivar] [jvar] += x [icell] [ivar] * x [icell] [jvar] * fittedVariance; } } } if (fabs (logLikelihood - previousLogLikelihood) < 1e-11) { break; } /* * Make matrix symmetric. */ for (long ivar = 1; ivar <= numberOfFactors; ivar ++) { for (long jvar = 0; jvar < ivar; jvar ++) { smallMatrix [ivar] [jvar] = smallMatrix [jvar] [ivar]; } } /* * Invert matrix in the simplest way, and shift and wipe the last column with it. */ for (long ivar = 0; ivar <= numberOfFactors; ivar ++) { double pivot = smallMatrix [ivar] [ivar]; /* Save diagonal. */ smallMatrix [ivar] [ivar] = 1.0; for (long jvar = 0; jvar <= numberOfParameters; jvar ++) { smallMatrix [ivar] [jvar] /= pivot; } for (long jvar = 0; jvar <= numberOfFactors; jvar ++) { if (jvar != ivar) { double temp = smallMatrix [jvar] [ivar]; smallMatrix [jvar] [ivar] = 0.0; for (long kvar = 0; kvar <= numberOfParameters; kvar ++) { smallMatrix [jvar] [kvar] -= temp * smallMatrix [ivar] [kvar]; } } } } /* * Update the parameters from the last column of smallMatrix. */ thy intercept += smallMatrix [0] [numberOfParameters]; for (long ivar = 1; ivar <= numberOfFactors; ivar ++) { RegressionParameter parm = static_cast<RegressionParameter> (thy parameters -> item [ivar]); parm -> value += smallMatrix [ivar] [numberOfParameters]; } } if (iteration > 100) { Melder_warning (L"Logistic regression has not converged in 100 iterations. The results are unreliable."); } for (long ivar = 1; ivar <= numberOfFactors; ivar ++) { RegressionParameter parm = static_cast<RegressionParameter> (thy parameters -> item [ivar]); parm -> value /= stdevX [ivar]; thy intercept -= parm -> value * meanX [ivar]; } return thee.transfer(); }
autoSound SpeechSynthesizer_to_Sound (SpeechSynthesizer me, const char32 *text, autoTextGrid *tg, autoTable *events) { try { int fsamp = espeak_Initialize (AUDIO_OUTPUT_SYNCHRONOUS, 0, nullptr, // 5000ms espeakINITIALIZE_PHONEME_EVENTS|espeakINITIALIZE_PHONEME_IPA); if (fsamp == -1) { Melder_throw (U"Internal espeak error."); } int synth_flags = espeakCHARS_WCHAR; if (my d_inputTextFormat == SpeechSynthesizer_INPUT_TAGGEDTEXT) { synth_flags |= espeakSSML; } if (my d_inputTextFormat != SpeechSynthesizer_INPUT_TEXTONLY) { synth_flags |= espeakPHONEMES; } option_phoneme_events = espeakINITIALIZE_PHONEME_EVENTS; // extern int option_phoneme_events; if (my d_outputPhonemeCoding == SpeechSynthesizer_PHONEMECODINGS_IPA) { option_phoneme_events |= espeakINITIALIZE_PHONEME_IPA; } espeak_SetParameter (espeakRATE, my d_wordsPerMinute, 0); espeak_SetParameter (espeakPITCH, my d_pitchAdjustment, 0); espeak_SetParameter (espeakRANGE, my d_pitchRange, 0); const char32 *voiceLanguageCode = SpeechSynthesizer_getVoiceLanguageCodeFromName (me, my d_voiceLanguageName); const char32 *voiceVariantCode = SpeechSynthesizer_getVoiceVariantCodeFromName (me, my d_voiceVariantName); espeakdata_SetVoiceByName ((const char *) Melder_peek32to8 (voiceLanguageCode), (const char *) Melder_peek32to8 (voiceVariantCode)); espeak_SetParameter (espeakWORDGAP, my d_wordgap * 100, 0); // espeak wordgap is in units of 10 ms espeak_SetParameter (espeakCAPITALS, 0, 0); espeak_SetParameter (espeakPUNCTUATION, espeakPUNCT_NONE, 0); espeak_SetSynthCallback (synthCallback); my d_events = Table_createWithColumnNames (0, U"time type type-t t-pos length a-pos sample id uniq"); #ifdef _WIN32 wchar_t *textW = Melder_peek32toW (text); espeak_Synth (textW, wcslen (textW) + 1, 0, POS_CHARACTER, 0, synth_flags, nullptr, me); #else espeak_Synth (text, str32len (text) + 1, 0, POS_CHARACTER, 0, synth_flags, nullptr, me); #endif espeak_Terminate (); autoSound thee = buffer_to_Sound (my d_wav, my d_numberOfSamples, my d_internalSamplingFrequency); if (my d_samplingFrequency != my d_internalSamplingFrequency) { thee = Sound_resample (thee.get(), my d_samplingFrequency, 50); } my d_numberOfSamples = 0; // re-use the wav-buffer if (tg) { double xmin = Table_getNumericValue_Assert (my d_events.get(), 1, 1); if (xmin > thy xmin) { xmin = thy xmin; } double xmax = Table_getNumericValue_Assert (my d_events.get(), my d_events -> rows.size, 1); if (xmax < thy xmax) { xmax = thy xmax; } autoTextGrid tg1 = Table_to_TextGrid (my d_events.get(), text, xmin, xmax); *tg = TextGrid_extractPart (tg1.get(), thy xmin, thy xmax, 0); } if (events) { Table_setEventTypeString (my d_events.get()); *events = my d_events.move(); } my d_events.reset(); return thee; } catch (MelderError) { espeak_Terminate (); Melder_throw (U"Text not played."); } }
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.get(), 1, 1, text); long p1c = 1, p1w = 1; double time_phon_p = xmin; bool wordEnd = false; autoMelderString mark; IntervalTier clauses = (IntervalTier) thy tiers->at [2]; IntervalTier words = (IntervalTier) thy tiers->at [3]; IntervalTier phonemes = (IntervalTier) thy tiers->at [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 && time < xmax) { IntervalTier_addBoundaryUnsorted (clauses, clauses -> 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 && time < xmax) { IntervalTier_addBoundaryUnsorted (clauses, clauses -> intervals.size, time, mark.string, true); } else { TextGrid_setIntervalText (thee.get(), 2, clauses -> 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 && time < xmax) { IntervalTier_addBoundaryUnsorted (words, words -> intervals.size, time, mark.string, true); } else { TextGrid_setIntervalText (thee.get(), 3, words -> 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 && time < xmax) { length = pos - p1w; if (pos == textLength) { length++; } MelderString_ncopy (&mark, text + p1w - 1, length); MelderString_trimWhiteSpaceAtEnd (& mark); IntervalTier_addBoundaryUnsorted (words, words -> 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 > time_phon_p) { // Insert new boudary and label interval with the id // TODO: Translate the id to the correct notation TextInterval ti = phonemes -> intervals.at [phonemes -> intervals.size]; if (time > ti -> xmin && time < ti -> xmax) { IntervalTier_addBoundaryUnsorted (phonemes, phonemes -> intervals.size, time, id, false); } } else { // Just in case the phoneme starts at xmin we only need to set interval text TextGrid_setIntervalText (thee.get(), 4, phonemes -> intervals.size, id); } time_phon_p = time; } } clauses -> intervals. sort (); words -> intervals. sort (); phonemes -> intervals. sort (); IntervalTier_mergeSpecialIntervals (phonemes); // Merge neighbouring empty U"" and U"\001" intervals IntervalTier_removeVeryShortIntervals (words); IntervalTier_removeVeryShortIntervals (clauses); /* Use empty intervals in phoneme tier for more precision in the word tier */ IntervalTier_insertEmptyIntervalsFromOtherTier (words, phonemes); IntervalTier_mergeSpecialIntervals (words); // Merge neighbouring empty U"" and U"\001" intervals return thee; } catch (MelderError) { Melder_throw (U"TextGrid not created from Table with events."); } }
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."); } }