[med-svn] [Git][med-team/praat][upstream] New upstream version 6.4.12+dfsg
Rafael Laboissière (@rafael)
gitlab at salsa.debian.org
Sun May 5 11:37:45 BST 2024
Rafael Laboissière pushed to branch upstream at Debian Med / praat
Commits:
f9c970b5 by Rafael Laboissière at 2024-05-03T08:11:31-03:00
New upstream version 6.4.12+dfsg
- - - - -
23 changed files:
- dwtools/CCA.cpp
- dwtools/Confusion.cpp
- dwtools/DTW.cpp
- dwtools/DTW_and_TextGrid.cpp
- dwtools/Sound_and_TextGrid_extensions.cpp
- dwtools/Sound_extensions.cpp
- dwtools/SpeechSynthesizer.cpp
- dwtools/SpeechSynthesizer_and_TextGrid.cpp
- dwtools/TextGrid_extensions.cpp
- dwtools/TextGrid_extensions.h
- dwtools/VowelEditor.cpp
- dwtools/espeakdata_FileInMemory.cpp
- dwtools/praat_David_init.cpp
- external/espeak/common.cpp
- external/espeak/synthesize.h
- external/espeak/wavegen.cpp
- fon/TextGrid.cpp
- fon/TextGrid.h
- fon/TextGrid_Sound.cpp
- sys/Gui_messages.cpp
- sys/HyperPage.cpp
- sys/ManPage.cpp
- sys/ManPages_toHtml.cpp
Changes:
=====================================
dwtools/CCA.cpp
=====================================
@@ -221,7 +221,6 @@ autoTableOfReal CCA_TableOfReal_predict (CCA me, TableOfReal thee, integer from)
Melder_require (ny == nev,
U"There are not enough correlations present for prediction.");
-
if (from == 0)
from = 1;
=====================================
dwtools/Confusion.cpp
=====================================
@@ -341,7 +341,7 @@ autoConfusion Confusion_condense (Confusion me, conststring32 search, conststrin
integer nmatches, nstringmatches;
Melder_require (my rowLabels && my columnLabels,
U"Both row and column labels should be present.");
-
+
autoSTRVEC rowLabels = string32vector_searchAndReplace (my rowLabels.get(),
search, replace, maximumNumberOfReplaces, & nmatches, & nstringmatches, use_regexp);
=====================================
dwtools/DTW.cpp
=====================================
@@ -134,12 +134,16 @@ double DTW_getYTimeFromXTime (DTW me, double tx) {
/*
If `eps` were zero, then the condition would rely on floating-point equality,
which is a bad idea (e.g. the result would be platform-dependent).
- (fix ppgb 20241020, after this crashed on Windows only)
+ (fix ppgb 20240420, after this crashed on Windows only)
+ (rewind 20240501, because of retries in SpeechSynthesizer & Sound & TextGrid alignment)
*/
- const double eps = 1e-6 * (my xmax - my xmin);
+ const double eps = 0.0 * (my xmax - my xmin);
if (tx > my xmin + eps && tx < my xmax - eps) {
DTW_Path_Query thee = & my pathQuery;
time = RealTier_getValueAtTime (thy yfromx.get(), tx);
+ } else {
+ //TRACE
+ trace (U"Time set to ", tx, U" instead of between ", my xmin, U" and ", my xmax, U".");
}
return time;
}
@@ -149,9 +153,10 @@ double DTW_getXTimeFromYTime (DTW me, double ty) {
/*
If `eps` were zero, then the condition would rely on floating-point equality,
which is a bad idea (e.g. the result would be platform-dependent).
- (fix ppgb 20241020, after this crashed on Windows only)
+ (fix ppgb 20240420, after this crashed on Windows only)
+ (rewind 20240501, because of retries in SpeechSynthesizer & Sound & TextGrid alignment)
*/
- const double eps = 1e-6 * (my ymax - my ymin);
+ const double eps = 0.0 * (my ymax - my ymin);
if (ty > my ymin + eps && ty < my ymax - eps) {
DTW_Path_Query thee = & my pathQuery;
time = RealTier_getValueAtTime (thy xfromy.get(), ty);
=====================================
dwtools/DTW_and_TextGrid.cpp
=====================================
@@ -83,7 +83,9 @@ autoIntervalTier DTW_IntervalTier_to_IntervalTier (DTW me, IntervalTier thee, do
}
textinterval = his intervals.at [his intervals.size];
textinterval -> xmax = his xmax;
- Melder_assert (textinterval -> xmin < textinterval -> xmax);
+ //Melder_assert (textinterval -> xmin < textinterval -> xmax);
+ Melder_require (textinterval -> xmin < textinterval -> xmax,
+ U"Interval runs from ", textinterval -> xmin, U" to ", textinterval -> xmax, U" seconds.");
return him;
} else if (fabs (my xmin - thy xmin) <= precision && fabs (my xmax - thy xmax) <= precision) { // map from X to Y
autoIntervalTier him = Data_copy (thee);
@@ -104,7 +106,7 @@ autoIntervalTier DTW_IntervalTier_to_IntervalTier (DTW me, IntervalTier thee, do
textinterval = his intervals.at [his intervals.size];
textinterval -> xmax = his xmax;
trace (U"interval ", his intervals.size, U": from ", textinterval -> xmin, U" to ", textinterval -> xmax, U", label <<", textinterval -> text.get(), U">>");
- Melder_assert (textinterval -> xmin < textinterval -> xmax);
+ //Melder_assert (textinterval -> xmin < textinterval -> xmax);
Melder_require (textinterval -> xmin < textinterval -> xmax,
U"Interval runs from ", textinterval -> xmin, U" to ", textinterval -> xmax, U" seconds.");
return him;
@@ -112,7 +114,7 @@ autoIntervalTier DTW_IntervalTier_to_IntervalTier (DTW me, IntervalTier thee, do
Melder_throw (U"The domain of the IntervalTier and one of the domains of the DTW should be equal.");
}
} catch (MelderError) {
- Melder_throw (U"IntervalTier not created from DTW & IntervalTier.");
+ Melder_throw (U"DTW & IntervalTier: new IntervalTier not created.");
}
}
@@ -148,7 +150,7 @@ autoTextGrid DTW_TextGrid_to_TextGrid (DTW me, TextGrid thee, double precision)
}
return him;
} catch (MelderError) {
- Melder_throw (U"TextGrid not created from DTW & TextGrid.");
+ Melder_throw (U"DTW & TextGrid: new TextGrid not created.");
}
}
=====================================
dwtools/Sound_and_TextGrid_extensions.cpp
=====================================
@@ -1,6 +1,6 @@
/* Sound_and_TextGrid_extensions.cpp
*
- * Copyright (C) 1993-2022 David Weenink
+ * Copyright (C) 1993-2022 David Weenink, 2024 Paul Boersma
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -17,6 +17,7 @@
*/
#include "Intensity_extensions.h"
+#include "Sound_extensions.h"
#include "Sound_and_TextGrid_extensions.h"
#include "Sound_and_Spectrum.h"
#include "Sound_to_Intensity.h"
@@ -69,16 +70,16 @@ autoTextGrid Sound_to_TextGrid_highMidLowIntervals (Sound me, double min, double
}
autoSound Sound_IntervalTier_cutPartsMatchingLabel (Sound me, IntervalTier thee, conststring32 match) {
- try {
+ try {
/*
Count samples of the trimmed sound
*/
- integer ixmin, ixmax, numberOfSamples = 0, previous_ixmax = 0;
+ integer ixmin, ixmax, numberOfSamples = 0, previous_ixmax = 0;
double xmin = my xmin; // start time of output sound is start time of input sound
- for (integer iint = 1; iint <= thy intervals.size; iint ++) {
- TextInterval interval = thy intervals.at [iint];
- if (! Melder_equ (interval -> text.get(), match)) {
- numberOfSamples += Sampled_getWindowSamples (me, interval -> xmin, interval -> xmax, & ixmin, & ixmax);
+ for (integer iint = 1; iint <= thy intervals.size; iint ++) {
+ TextInterval interval = thy intervals.at [iint];
+ if (! Melder_equ (interval -> text.get(), match)) {
+ numberOfSamples += Sampled_getWindowSamples (me, interval -> xmin, interval -> xmax, & ixmin, & ixmax);
/*
If two contiguous intervals have to be copied then the last sample of previous interval
and first sample of current interval might sometimes be equal
@@ -89,31 +90,31 @@ autoSound Sound_IntervalTier_cutPartsMatchingLabel (Sound me, IntervalTier thee,
} else { // matches label
if (iint == 1) // Start time of output sound is end time of first interval
xmin = interval -> xmax;
- }
- }
- /*
+ }
+ }
+ /*
Now copy the parts. The output sound starts at xmin
*/
- autoSound him = Sound_create (my ny, xmin, xmin + numberOfSamples * my dx, numberOfSamples, my dx, xmin + 0.5 * my dx);
- numberOfSamples = 0;
+ autoSound him = Sound_create (my ny, xmin, xmin + numberOfSamples * my dx, numberOfSamples, my dx, xmin + 0.5 * my dx);
+ numberOfSamples = 0;
previous_ixmax = 0;
- for (integer iint = 1; iint <= thy intervals.size; iint ++) {
- const TextInterval interval = thy intervals.at [iint];
- if (! Melder_equ (interval -> text.get(), match)) {
- Sampled_getWindowSamples (me, interval -> xmin, interval -> xmax, & ixmin, & ixmax);
+ for (integer iint = 1; iint <= thy intervals.size; iint ++) {
+ const TextInterval interval = thy intervals.at [iint];
+ if (! Melder_equ (interval -> text.get(), match)) {
+ Sampled_getWindowSamples (me, interval -> xmin, interval -> xmax, & ixmin, & ixmax);
if (ixmin == previous_ixmax)
ixmin ++;
previous_ixmax = ixmax;
integer numberOfSamplesToCopy = ixmax - ixmin + 1;
his z.part (1, my ny, numberOfSamples + 1, numberOfSamples + numberOfSamplesToCopy) <<= my z.part (1, my ny, ixmin, ixmax);
- numberOfSamples += numberOfSamplesToCopy;
- }
- }
- Melder_assert (numberOfSamples == his nx);
- return him;
- } catch (MelderError) {
- Melder_throw (me, U": intervals not trimmed.");
- }
+ numberOfSamples += numberOfSamplesToCopy;
+ }
+ }
+ Melder_assert (numberOfSamples == his nx);
+ return him;
+ } catch (MelderError) {
+ Melder_throw (me, U": intervals not trimmed.");
+ }
}
autoTextGrid Sound_to_TextGrid_detectSilences (Sound me, double minPitch, double timeStep,
=====================================
dwtools/Sound_extensions.cpp
=====================================
@@ -1,6 +1,6 @@
/* Sound_extensions.cpp
*
- * Copyright (C) 1993-2023 David Weenink, 2017 Paul Boersma
+ * Copyright (C) 1993-2023 David Weenink, 2017,2024 Paul Boersma
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -1146,19 +1146,18 @@ Sound Sound_createShepardTone (double minimumTime, double maximumTime, double sa
Sound me; integer i, j, nComponents = 1 + log2 (maximumFrequency / 2 / baseFrequency);
double lmin = pow (10, - amplitudeRange / 10);
double twoPi = NUM2pi, f = baseFrequency * (1 + frequencyShiftFraction);
- if (nComponents < 2) Melder_warning (U"Sound_createShepardTone: only 1 component.");
+ if (nComponents < 2)
+ Melder_warning (U"Sound_createShepardTone: only 1 component.");
Melder_casual (U"Sound_createShepardTone: ", nComponents, U" components.");
- if (! (me = Sound_create2 (minimumTime, maximumTime, samplingFrequency))) return nullptr;
+ if (! (me = Sound_create2 (minimumTime, maximumTime, samplingFrequency)))
+ return nullptr;
- for (j=1; j <= nComponents; j ++)
- {
+ for (j=1; j <= nComponents; j ++) {
double fj = f * pow (2, j-1), wj = twoPi * fj;
double amplitude = lmin + (1 - lmin) *
(1 - cos (twoPi * log (fj + 1) / log (maximumFrequency + 1))) / 2;
for (i=1; i <= my nx; i ++)
- {
my z [1] [i] += amplitude * sin (wj * (i - 0.5) * my dx);
- }
}
Vector_scale (me, 0.99996948);
return me;
@@ -1735,52 +1734,58 @@ void Sound_getStartAndEndTimesOfSounding (Sound me, double minPitch, double time
}
}
-autoSound Sound_trimSilences (Sound me, double trimDuration, bool onlyAtStartAndEnd, double minPitch, double timeStep, double silenceThreshold, double minSilenceDuration, double minSoundingDuration, autoTextGrid *p_tg, conststring32 trimLabel) {
- try {
+autoSound Sound_trimSilences (Sound me, double trimDuration, bool onlyAtStartAndEnd,
+ double minPitch, double timeStep, double silenceThreshold,
+ double minSilenceDuration, double minSoundingDuration,
+ autoTextGrid *out_tg, conststring32 trimLabel
+) {
+ try {
Melder_require (my ny == 1,
U"The sound should be a mono sound.");
-
- const conststring32 silentLabel = U"silent", soundingLabel = U"sounding";
- const conststring32 copyLabel = U"";
- autoTextGrid tg = Sound_to_TextGrid_detectSilences (me, minPitch, timeStep, silenceThreshold, minSilenceDuration, minSoundingDuration, silentLabel, soundingLabel);
- autoIntervalTier itg = Data_copy ((IntervalTier) tg -> tiers->at [1]);
- IntervalTier tier = (IntervalTier) tg -> tiers->at [1];
- for (integer iint = 1; iint <= tier -> intervals.size; iint ++) {
- const TextInterval ti = tier -> intervals.at [iint];
- const TextInterval ati = itg -> intervals.at [iint];
- const double duration = ti -> xmax - ti -> xmin;
- if (duration > trimDuration && Melder_equ (ti -> text.get(), silentLabel)) { // silent
+
+ const conststring32 silentLabel = U"silent", soundingLabel = U"sounding";
+ const conststring32 copyLabel = U"";
+ autoTextGrid tg = Sound_to_TextGrid_detectSilences (me, minPitch, timeStep, silenceThreshold, minSilenceDuration, minSoundingDuration, silentLabel, soundingLabel);
+ autoIntervalTier itg = Data_copy ((IntervalTier) tg -> tiers->at [1]);
+ IntervalTier tier = (IntervalTier) tg -> tiers->at [1];
+ for (integer iint = 1; iint <= tier -> intervals.size; iint ++) {
+ const TextInterval ti = tier -> intervals.at [iint];
+ const TextInterval ati = itg -> intervals.at [iint];
+ const double duration = ti -> xmax - ti -> xmin;
+ if (duration > trimDuration && Melder_equ (ti -> text.get(), silentLabel)) { // silent
conststring32 label = trimLabel;
- if (iint == 1) { // first is special
- const double trim_t = ti -> xmax - trimDuration;
- IntervalTier_moveBoundary (itg.get(), iint, false, trim_t);
- } else if (iint == tier -> intervals.size) { // last is special
- const double trim_t = ti -> xmin + trimDuration;
- IntervalTier_moveBoundary (itg.get(), iint, true, trim_t);
- } else {
+ if (tier -> intervals.size == 1) { // current interval is both the first and the last: very special
+ // all of the sound is silent: do nothing
+ } else if (iint == 1) { // first is special
+ const double trim_t = ti -> xmax - trimDuration;
+ IntervalTier_moveRightBoundary (itg.get(), iint, trim_t);
+ } else if (iint == tier -> intervals.size) { // last is special
+ const double trim_t = ti -> xmin + trimDuration;
+ IntervalTier_moveLeftBoundary (itg.get(), iint, trim_t);
+ } else {
if (onlyAtStartAndEnd) {
label = ati -> text.get();
} else {
- double trim_t = ti -> xmin + 0.5 * trimDuration;
- IntervalTier_moveBoundary (itg.get(), iint, true, trim_t);
- trim_t = ti -> xmax - 0.5 * trimDuration;
- IntervalTier_moveBoundary (itg.get(), iint, false, trim_t);
+ double trim_t = ti -> xmin + 0.5 * trimDuration;
+ IntervalTier_moveLeftBoundary (itg.get(), iint, trim_t);
+ trim_t = ti -> xmax - 0.5 * trimDuration;
+ IntervalTier_moveRightBoundary (itg.get(), iint, trim_t);
}
- }
- TextInterval_setText (ati, label);
- } else { // sounding
- TextInterval_setText (ati, copyLabel);
- }
- }
- autoSound thee = Sound_IntervalTier_cutPartsMatchingLabel (me, itg.get(), trimLabel);
- if (p_tg) {
+ }
+ TextInterval_setText (ati, label);
+ } else { // sounding
+ TextInterval_setText (ati, copyLabel);
+ }
+ }
+ autoSound thee = Sound_IntervalTier_cutPartsMatchingLabel (me, itg.get(), trimLabel);
+ if (out_tg) {
TextGrid_addTier_copy (tg.get(), itg.get());
- *p_tg = tg.move();
- }
- return thee;
- } catch (MelderError) {
- Melder_throw (me, U": silences not trimmed.");
- }
+ *out_tg = tg.move();
+ }
+ return thee;
+ } catch (MelderError) {
+ Melder_throw (me, U": silences not trimmed.");
+ }
}
autoSound Sound_trimSilencesAtStartAndEnd (Sound me, double trimDuration, double minPitch, double timeStep,
@@ -1789,7 +1794,7 @@ autoSound Sound_trimSilencesAtStartAndEnd (Sound me, double trimDuration, double
try {
autoTextGrid tg;
autoSound thee = Sound_trimSilences (me, trimDuration, true, minPitch, timeStep, silenceThreshold,
- minSilenceDuration, minSoundingDuration, & tg, U"trimmed");
+ minSilenceDuration, minSoundingDuration, & tg, U"trimmed");
const IntervalTier trim = (IntervalTier) tg -> tiers->at [2];
const TextInterval ti1 = trim -> intervals.at [1];
if (startTimeOfSounding) {
@@ -1805,7 +1810,7 @@ autoSound Sound_trimSilencesAtStartAndEnd (Sound me, double trimDuration, double
}
return thee;
} catch (MelderError) {
- Melder_throw (me, U": silences not trimmed.");
+ Melder_throw (me, U": silences at start and end not trimmed.");
}
}
=====================================
dwtools/SpeechSynthesizer.cpp
=====================================
@@ -1,6 +1,6 @@
/* SpeechSynthesizer.cpp
*
- * Copyright (C) 2011-2023 David Weenink
+ * Copyright (C) 2011-2023 David Weenink, 2012,2013,2015-2024 Paul Boersma
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
=====================================
dwtools/SpeechSynthesizer_and_TextGrid.cpp
=====================================
@@ -101,15 +101,15 @@ static double TextGrid_getEndTimeOfLastOccurrence (TextGrid thee, integer tierNu
#endif
static void IntervalTier_getLabelInfo (IntervalTier me, conststring32 label, double *labelDurations, integer *numberOfOccurences) {
- *labelDurations = 0.0;
- *numberOfOccurences = 0;
- for (integer i = 1; i <= my intervals.size; i ++) {
+ *labelDurations = 0.0;
+ *numberOfOccurences = 0;
+ for (integer i = 1; i <= my intervals.size; i ++) {
const TextInterval ti = my intervals.at [i];
- if (Melder_equ (ti -> text.get(), label)) {
- *labelDurations += ti -> xmax - ti -> xmin;
- (*numberOfOccurences) ++;
- }
- }
+ if (Melder_equ (ti -> text.get(), label)) {
+ *labelDurations += ti -> xmax - ti -> xmin;
+ (*numberOfOccurences) ++;
+ }
+ }
}
#define TIMES_ARE_CLOSE(x,y) (fabs((x)-(y)) < precision)
@@ -469,7 +469,11 @@ autoTextGrid TextGrid_IntervalTier_patch (TextGrid me, IntervalTier thee, consts
}
// We assume that the Sound and the SpeechSynthesizer have the same samplingFrequency
-autoTextGrid SpeechSynthesizer_Sound_TextInterval_align (SpeechSynthesizer me, Sound thee, TextInterval him, double silenceThreshold, double minSilenceDuration, double minSoundingDuration) {
+autoTextGrid SpeechSynthesizer_Sound_TextInterval_align (
+ const SpeechSynthesizer me, const Sound thee, const TextInterval him,
+ const double silenceThreshold,
+ const double minSilenceDuration, const double minSoundingDuration
+) {
try {
Melder_require (thy xmin == his xmin && thy xmax == his xmax,
U"Domains of Sound and TextGrid should be equal.");
@@ -477,7 +481,8 @@ autoTextGrid SpeechSynthesizer_Sound_TextInterval_align (SpeechSynthesizer me, S
U"The sampling frequencies of the SpeechSynthesizer and the Sound should be equal.");
//TRACE
-trace(1);
+trace(U"original sound: ", thy xmin, U" .. ", thy xmax);
+trace(U"interval: ", his xmin, U" .. ", his xmax, U" ", his text.get());
autoSTRVEC tokens = splitByWhitespace_STRVEC (his text.get());
const integer numberOfTokens = tokens.size;
Melder_require (numberOfTokens > 0,
@@ -490,15 +495,24 @@ trace(1);
trace(2);
const double minPitch = 200.0, timeStep = 0.005, precision = thy dx;
double startTimeOfSounding, endTimeOfSounding;
- autoSound soundTrimmed = Sound_trimSilencesAtStartAndEnd (thee, 0.0, minPitch, timeStep, silenceThreshold, minSilenceDuration, minSoundingDuration, & startTimeOfSounding, & endTimeOfSounding);
+ autoSound soundTrimmed;
+ {// scope
+ autoMelderWarningOff nowarn;
+ soundTrimmed = Sound_trimSilencesAtStartAndEnd (thee, 0.0, minPitch, timeStep,
+ silenceThreshold, minSilenceDuration, minSoundingDuration, & startTimeOfSounding, & endTimeOfSounding);
+ }
+trace (U"- silence-trimmed: ", soundTrimmed -> xmin, U" .. ", soundTrimmed -> xmax);
const double duration_soundTrimmed = soundTrimmed -> xmax - soundTrimmed -> xmin;
+ Melder_require (duration_soundTrimmed > 0.0,
+ soundTrimmed.get(), U" is silent, so we cannot align it with text.");
const bool hasSilence_sound = fabs (startTimeOfSounding - thy xmin) > precision || fabs (endTimeOfSounding - thy xmax) > precision;
trace(3);
- if (my d_estimateSpeechRate) {
+ if (my d_estimateSpeechRate && duration_soundTrimmed != 0.0) {
/*
Estimate speaking rate with the number of words per minute from the text
*/
+ Melder_assert (duration_soundTrimmed != 0.0);
const double wordsPerMinute_rawTokens = 60.0 * numberOfTokens / duration_soundTrimmed;
/*
Compensation for long words: 5 characters / word
@@ -519,10 +533,17 @@ trace(4);
const double silenceThreshold_synth = -40.0, minSilenceDuration_synth = 0.05;
const double minSoundingDuration_synth = 0.05;
double startTimeOfSounding_synth, endTimeOfSounding_synth;
- autoSound synthTrimmed = Sound_trimSilencesAtStartAndEnd (synth.get(), 0.0, minPitch, timeStep, silenceThreshold_synth,
- minSilenceDuration_synth, minSoundingDuration_synth, & startTimeOfSounding_synth, & endTimeOfSounding_synth);
+ autoSound synthTrimmed;
+ {// scope
+ autoMelderWarningOff nowarn;
+ synthTrimmed = Sound_trimSilencesAtStartAndEnd (synth.get(), 0.0, minPitch, timeStep, silenceThreshold_synth,
+ minSilenceDuration_synth, minSoundingDuration_synth, & startTimeOfSounding_synth, & endTimeOfSounding_synth);
+ }
const double synthTrimmed_duration = synthTrimmed -> xmax - synthTrimmed -> xmin;
- const bool hasSilence_synth = fabs (startTimeOfSounding_synth - synth -> xmin) > precision ||
+trace (U"synthesized sound: ", synth -> xmin, U" .. ", synth -> xmax);
+trace (U"- silence-trimmed: ", synthTrimmed -> xmin, U" .. ", synthTrimmed -> xmax);
+trace (U"- sounding: ", startTimeOfSounding_synth, U" .. ", endTimeOfSounding_synth);
+ const bool hasSilence_synth = fabs (startTimeOfSounding_synth - synth -> xmin) > precision ||
fabs (endTimeOfSounding_synth - synth -> xmax) > precision;
trace(5);
@@ -531,12 +552,14 @@ trace(5);
/*
Compare the durations of the two sounds to get an indication of the slope constraint needed for the DTW
*/
+ Melder_assert (synthTrimmed_duration != 0.0);
double slope = duration_soundTrimmed / synthTrimmed_duration;
+ Melder_assert (slope != 0.0);
slope = ( slope > 1.0 ? slope : 1.0 / slope );
- const int constraint = ( slope < 1.5 ? 4 : slope < 2.0 ? 3 : slope < 3.0 ? 2 : 1 ); // TODO enums
+ const int constraint = ( slope < 1.5 ? 4 : slope < 2.0 ? 3 : slope < 3.0 ? 2 : 1 ); // TODO enums
trace(6);
- trace (hasSilence_sound, hasSilence_synth);
+trace (hasSilence_sound, hasSilence_synth);
const double analysisWidth = 0.02, dt = 0.005, band = 0.0;
autoDTW dtw = Sounds_to_DTW ((hasSilence_sound ? soundTrimmed.get() : thee),
(hasSilence_synth ? synthTrimmed.get() : synth.get()), analysisWidth, dt, band, constraint);
@@ -545,15 +568,19 @@ trace(7);
autoTextGrid result = DTW_TextGrid_to_TextGrid (dtw.get(), (hasSilence_synth ? textgrid_synth_sounding.get() : textgrid_synth.get()), precision);
trace(8);
if (hasSilence_sound) {
- if (startTimeOfSounding > thy xmin)
+ if (startTimeOfSounding > thy xmin) {
+ trace (U"Have to set earlier start time ", thy xmin);
TextGrid_setEarlierStartTime (result.get(), thy xmin, U"", U"");
- if (endTimeOfSounding < thy xmax || result -> xmax < thy xmax)
- TextGrid_setLaterEndTime (result.get(), thy xmax, U"", U"");
+ }
+ if (endTimeOfSounding < thy xmax || result -> xmax < thy xmax) {
+ trace (U"Have to set later end time ", thy xmax);
+ TextGrid_setLaterEndTime (result.get(), thy xmax, U"", U"");
+ }
}
trace(9);
return result;
} catch (MelderError) {
- Melder_throw (U"Sound and TextInterval not aligned.");
+ Melder_throw (U"SpeechSynthesizer & Sound & TextInterval: not aligned.");
}
}
/*
@@ -589,7 +616,11 @@ static autoTextGrid SpeechSynthesizer_Sound_TextInterval_align2 (SpeechSynthesiz
*/
const double minPitch = 200, timeStep = 0.005, precision = thy dx;
autoTextGrid thee_trimmer;
- autoSound thee_trimmed = Sound_trimSilences (thee, trimDuration, false, minPitch, timeStep, silenceThreshold, minSilenceDuration, minSoundingDuration, &thee_trimmer, trimLabel);
+ autoSound thee_trimmed;
+ {// scope
+ autoMelderWarningOff nowarn;
+ thee_trimmed = Sound_trimSilences (thee, trimDuration, false, minPitch, timeStep, silenceThreshold, minSilenceDuration, minSoundingDuration, & thee_trimmer, trimLabel);
+ }
/*
2. Synthesize the sound from the TextInterval
*/
=====================================
dwtools/TextGrid_extensions.cpp
=====================================
@@ -1,6 +1,6 @@
/* TextGrid_extensions.cpp
*
- * Copyright (C) 1993-2019, 2023 David Weenink, Paul Boersma 2019
+ * Copyright (C) 1993-2019,2023 David Weenink, 2015-2022,2024 Paul Boersma
*
* This code is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -385,30 +385,6 @@ void IntervalTier_setEarlierStartTime (IntervalTier me, double xmin, conststring
}
}
-void IntervalTier_moveBoundary (IntervalTier me, integer iint, bool atStart, double newTime) {
- try {
- Melder_require (iint >= 1 && iint <= my intervals.size,
- U"The interval number is out of the valid range.");
- Melder_require (! ((iint == 1 && atStart) or (iint == my intervals.size && ! atStart)),
- U"Cannot change the domain.");
- TextInterval interval = my intervals.at [iint];
- if (atStart) {
- const TextInterval pinterval = my intervals.at [iint-1];
- Melder_require (newTime > pinterval -> xmin,
- U"Cannot move past the start of previous interval.");
- pinterval -> xmax = interval -> xmin = newTime;
- } else {
- const TextInterval ninterval = my intervals.at [iint+1];
- Melder_require (newTime < ninterval -> xmax,
- U"Cannot move past the end of next interval.");
- ninterval -> xmin = interval -> xmax = newTime;
- }
- } catch (MelderError) {
- Melder_throw (me, U": boundary not moved.");
- }
-}
-
-
void TextTier_setLaterEndTime (TextTier me, double xmax, conststring32 mark) {
try {
if (xmax <= my xmax)
@@ -565,7 +541,7 @@ static void IntervalTier_cutInterval (IntervalTier me, integer index, int extend
}
void IntervalTier_removeBoundariesBetweenIdenticallyLabeledIntervals (IntervalTier me, conststring32 label) {
- try {
+ try {
for (integer iinterval = my intervals.size; iinterval > 1; iinterval --) {
const TextInterval thisInterval = my intervals.at [iinterval];
if (Melder_equ (thisInterval -> text.get(), label)) {
=====================================
dwtools/TextGrid_extensions.h
=====================================
@@ -102,8 +102,6 @@ void IntervalTier_setLaterEndTime (IntervalTier me, double xmax, conststring32 m
void IntervalTier_setEarlierStartTime (IntervalTier me, double xmin, conststring32 mark);
-void IntervalTier_moveBoundary (IntervalTier me, integer interval, bool atStart, double newTime);
-
void TextTier_setLaterEndTime (TextTier me, double xmax, conststring32 mark);
void TextTier_setEarlierStartTime (TextTier me, double xmin, conststring32 mark);
=====================================
dwtools/VowelEditor.cpp
=====================================
@@ -772,9 +772,9 @@ static void menu_cb_settings (VowelEditor me, EDITOR_ARGS) {
}
const integer numberOfPairs = extraFrequencyBandwidthPairs.size / 2;
Melder_require (numberOfFormants <= numberOfPairs + 2,
- U"The “Number of formants for synthesis” should not exceed 2 plus the number of extra frequency–bandwidth pairs (i.e. 2+",
- numberOfPairs, U"). Either lower the number of formants for synthesis or specify more "
- "frequency–bandwidth pairs.");
+ U"The “Number of formants for synthesis” should not exceed 2 plus the number of extra frequency–bandwidth pairs "
+ U"(i.e. 2+", numberOfPairs,
+ U"). Either lower the number of formants for synthesis, or specify more frequency–bandwidth pairs.");
/*
Formants and bandwidths are valid. It is safe to copy them.
*/
=====================================
dwtools/espeakdata_FileInMemory.cpp
=====================================
@@ -216,13 +216,12 @@ void espeakdata_getIndices (conststring32 language_string, conststring32 voice_s
if (languageIndex == 0) {
if (Melder_equ (language_string, U"Default") || Melder_equ (language_string, U"English")) {
languageIndex = Strings_findString (espeakdata_languages_names.get(), U"English (Great Britain)");
- Melder_casual (U"Language \"", language_string, U"\" is deprecated. Please use \"",
- espeakdata_languages_names -> strings [languageIndex].get(), U"\".");
+ Melder_warning (U"Language \"", language_string, U"\" is deprecated. Please use \"",
+ espeakdata_languages_names -> strings [languageIndex].get(), U"\".");
} else {
languageIndex = Table_searchColumn (espeakdata_languages_propertiesTable.get(), 1, language_string);
- if (languageIndex == 0) {
+ if (languageIndex == 0)
Melder_throw (U"Language \"", language_string, U"\" is not a valid option.");
- }
}
}
*p_languageIndex = languageIndex;
@@ -246,7 +245,7 @@ void espeakdata_getIndices (conststring32 language_string, conststring32 voice_s
if (voiceIndex != *p_voiceIndex) {
*p_voiceIndex = voiceIndex;
Melder_casual (U"Voice \"", voice_string, U"\" is deprecated. Please use \"",
- espeakdata_voices_names -> strings [*p_voiceIndex].get(), U"\".");
+ espeakdata_voices_names -> strings [*p_voiceIndex].get(), U"\".");
} else {
// unknown voice, handled by interface
}
=====================================
dwtools/praat_David_init.cpp
=====================================
@@ -5743,7 +5743,7 @@ DO
}
FORM (CONVERT_EACH_TO_ONE__Sound_trimSilences, U"Sound: Trim silences", U"Sound: Trim silences...") {
- REAL (trimDuration, U"Trim duration (s)", U"0.08")
+ REAL (trimDuration, U"Trim duration (s)", U"0.08")
BOOLEAN (onlyAtStartAndEnd, U"Only at start and end", true);
LABEL (U"Parameters for the intensity analysis")
POSITIVE (pitchFloor, U"Pitch floor (Hz)", U"100")
@@ -5756,7 +5756,7 @@ FORM (CONVERT_EACH_TO_ONE__Sound_trimSilences, U"Sound: Trim silences", U"Sound:
WORD (trim_string, U"Trim label", U"trimmed")
OK
DO
- trimDuration = ( trimDuration < 0.0 ? 0.0 : trimDuration );
+ Melder_clipLeft (0.0, & trimDuration);
CONVERT_EACH_TO_ONE (Sound)
autoTextGrid tg;
autoSound result = Sound_trimSilences (me, trimDuration, onlyAtStartAndEnd, pitchFloor, timeStep,
=====================================
external/espeak/common.cpp
=====================================
@@ -332,14 +332,18 @@ static uint32_t espeak_rand_state = 0;
long espeak_rand(long min, long max) {
// Ref: https://github.com/bminor/glibc/blob/glibc-2.36/stdlib/random_r.c#L364
+#if 1
espeak_rand_state = (((uint64_t)espeak_rand_state * 1103515245) + 12345) % 0x7fffffff;
+#endif
long res = (long)espeak_rand_state;
return (res % (max-min+1))-min;
}
void espeak_srand(long seed) {
+#if 1
espeak_rand_state = (uint32_t)(seed);
(void)espeak_rand(0, 1); // Dummy flush a generator
+#endif
}
#pragma GCC visibility push(default)
=====================================
external/espeak/synthesize.h
=====================================
@@ -412,7 +412,7 @@ extern const unsigned char env_fall[128];
#define WCMD_SONIC_SPEED 15
#define WCMD_PHONEME_ALIGNMENT 16
-#define N_WCMDQ 170
+#define N_WCMDQ 170 // normally 170
#define MIN_WCMDQ 25 // need this many free entries before adding new phoneme
extern intptr_t wcmdq[N_WCMDQ][4];
=====================================
external/espeak/wavegen.cpp
=====================================
@@ -37,6 +37,8 @@
#include "synthesize.h" // for WGEN_DATA, RESONATOR, frame_t
#include "mbrola.h" // for MbrolaFill, MbrolaReset, mbrola...
+#include "melder.h" // for tracing
+
#if USE_KLATT
#include "klatt.h"
#endif
@@ -269,6 +271,8 @@ void WcmdqInc(void)
{
wcmdq_tail++;
if (wcmdq_tail >= N_WCMDQ) wcmdq_tail = 0;
+ //TRACE
+ trace (U"Tail now ", wcmdq_tail);
}
static void WcmdqIncHead(void)
@@ -276,6 +280,8 @@ static void WcmdqIncHead(void)
MAKE_MEM_UNDEFINED(&wcmdq[wcmdq_head], sizeof(wcmdq[wcmdq_head]));
wcmdq_head++;
if (wcmdq_head >= N_WCMDQ) wcmdq_head = 0;
+ //TRACE
+ trace (U"Head now ", wcmdq_head);
}
#define PEAKSHAPEW 256
=====================================
fon/TextGrid.cpp
=====================================
@@ -1189,6 +1189,49 @@ void TextGrid_removeBoundaryAtTime (TextGrid me, integer tierNumber, double t) {
}
}
+void IntervalTier_moveLeftBoundary (const IntervalTier me, const integer intervalNumber, const double newTime) {
+ try {
+ Melder_require (intervalNumber >= 1 && intervalNumber <= my intervals.size,
+ U"The interval number (", intervalNumber, U" is out of the valid range (1 ..", my intervals.size, U").");
+ TextInterval currentInterval = my intervals.at [intervalNumber];
+ Melder_require (newTime < currentInterval -> xmax,
+ U"Cannot move boundary forward from ", currentInterval -> xmin, U" to ", newTime, U" seconds, ",
+ U"because that would be past the end of the current interval (", currentInterval -> xmax, U" seconds).");
+ Melder_require (intervalNumber > 1,
+ U"Trying to change the end of the previous interval (", intervalNumber - 1, U"), but there is no previous interval.");
+ Melder_assert (my intervals.size >= 2); // otherwise we would have thrown
+ const TextInterval previousInterval = my intervals.at [intervalNumber - 1];
+ Melder_require (newTime > previousInterval -> xmin,
+ U"Cannot move boundary back from ", currentInterval -> xmin, U" to ", newTime, U" seconds, ",
+ U"because that would be past the start of the previous interval (", previousInterval -> xmin, U" seconds).");
+ previousInterval -> xmax = currentInterval -> xmin = newTime;
+ } catch (MelderError) {
+ Melder_throw (me, U": left boundary not moved.");
+ }
+}
+
+void IntervalTier_moveRightBoundary (const IntervalTier me, const integer intervalNumber, const double newTime) {
+ try {
+ Melder_require (intervalNumber >= 1 && intervalNumber <= my intervals.size,
+ U"The interval number (", intervalNumber, U" is out of the valid range (1 ..", my intervals.size, U").");
+ TextInterval currentInterval = my intervals.at [intervalNumber];
+ Melder_require (newTime > currentInterval -> xmin,
+ U"Cannot move boundary back from ", currentInterval -> xmax, U" to ", newTime, U" seconds, ",
+ U"because that would be past the start of the current interval (", currentInterval -> xmin, U" seconds).");
+ Melder_require (intervalNumber < my intervals.size,
+ U"Trying to change the start of the next interval (", intervalNumber + 1, U"), but there is no next interval.");
+ Melder_assert (my intervals.size >= 2); // otherwise we would have thrown
+ const TextInterval nextInterval = my intervals.at [intervalNumber + 1];
+ Melder_require (newTime < nextInterval -> xmax,
+ U"Cannot move boundary forward from ", currentInterval -> xmax, U" to ", newTime, U" seconds, ",
+ U"because that would be past the end of the next interval (", nextInterval -> xmax, U" seconds)."
+ );
+ nextInterval -> xmin = currentInterval -> xmax = newTime;
+ } catch (MelderError) {
+ Melder_throw (me, U": right boundary not moved.");
+ }
+}
+
void TextGrid_setIntervalText (TextGrid me, integer tierNumber, integer intervalNumber, conststring32 text) {
try {
IntervalTier intervalTier = TextGrid_checkSpecifiedTierIsIntervalTier (me, tierNumber);
=====================================
fon/TextGrid.h
=====================================
@@ -58,6 +58,8 @@ autoPointProcess IntervalTier_getCentrePoints (IntervalTier me, conststring32 te
autoPointProcess IntervalTier_PointProcess_startToCentre (IntervalTier tier, PointProcess point, double phase);
autoPointProcess IntervalTier_PointProcess_endToCentre (IntervalTier tier, PointProcess point, double phase);
void IntervalTier_removeLeftBoundary (IntervalTier me, integer intervalNumber);
+void IntervalTier_moveLeftBoundary (IntervalTier me, integer interval, double newTime);
+void IntervalTier_moveRightBoundary (IntervalTier me, integer interval, double newTime);
void TextTier_removePoint (TextTier me, integer pointNumber);
=====================================
fon/TextGrid_Sound.cpp
=====================================
@@ -30,7 +30,8 @@ static bool IntervalTier_check (IntervalTier me) {
return false;
}
}
- if (my intervals.size < 2) return true;
+ if (my intervals.size < 2)
+ return true;
for (integer iinterval = 1; iinterval < my intervals.size; iinterval ++) {
TextInterval thisInterval = my intervals.at [iinterval];
TextInterval nextInterval = my intervals.at [iinterval + 1];
@@ -149,6 +150,7 @@ void TextGrid_anySound_alignInterval (
const bool includeWords, const bool includePhonemes
) {
try {
+ //TRACE
IntervalTier headTier = TextGrid_checkSpecifiedTierIsIntervalTier (me, tierNumber);
if (intervalNumber < 1 || intervalNumber > headTier -> intervals.size)
Melder_throw (U"Interval ", intervalNumber, U" does not exist.");
@@ -157,6 +159,8 @@ void TextGrid_anySound_alignInterval (
Melder_throw (U"Nothing to be done, because you asked neither for word alignment nor for phoneme alignment.");
if (str32str (headTier -> name.get(), U"/"))
Melder_throw (U"The current tier already has a slash (\"/\") in its name. Cannot create a word or phoneme tier from it.");
+ trace (U"tier ", tierNumber, U" interval ", intervalNumber,
+ U" (", interval -> xmin, U" .. ", interval -> xmax, U" “", interval -> text.get(), U"”)");
autoSound part =
anySound -> classInfo == classLongSound ?
LongSound_extractPart (static_cast <LongSound> (anySound), interval -> xmin, interval -> xmax, true) :
@@ -169,16 +173,31 @@ void TextGrid_anySound_alignInterval (
);
double silenceThreshold = -30.0, minSilenceDuration = 0.1, minSoundingDuration = 0.1;
autoTextGrid analysis;
+int tries = 0;
+constexpr int maxTries = 5;
+again:
if (! Melder_equ (interval -> text.get(), U""))
- analysis = SpeechSynthesizer_Sound_TextInterval_align
- (synthesizer.get(), part.get(), interval, silenceThreshold, minSilenceDuration, minSoundingDuration);
+ try {
+ analysis = SpeechSynthesizer_Sound_TextInterval_align
+ (synthesizer.get(), part.get(), interval, silenceThreshold, minSilenceDuration, minSoundingDuration);
+ } catch (MelderError) {
+ if (++ tries < maxTries) {
+ Melder_casual (U"TRY ", tries, U" FAILED (SpeechSynthesizer & Sound & TextInterval: align):");
+ Melder_casual (U" Tier ", tierNumber);
+ Melder_casual (U" Interval ", intervalNumber, U": ", interval -> xmin, U" .. ", interval -> xmax, U" “", interval -> text.get(), U"”");
+ Melder_casual (U"REASON OF THIS TEMPORARY FAILURE (now comes a suppressed Praat error message):\n", Melder_getError (), U"(GOING TO RETRY...)");
+ Melder_clearError ();
+ goto again;
+ } else
+ Melder_throw (U"All ", maxTries, U" tries failed.");
+ }
if (analysis) {
/*
Clean up the analysis.
*/
Melder_assert (fabs (analysis -> xmin - interval -> xmin) < 1e-12);
if (analysis -> xmax != interval -> xmax) {
- //Melder_fatal (U"Analysis ends at ", analysis -> xmax, U" but interval at ", interval -> xmax, U"seconds.");
+ Melder_casual (U"Analysis ends at ", analysis -> xmax, U" but interval at ", interval -> xmax, U" seconds.");
analysis -> xmax = interval -> xmax;
analysis -> intervalTier_cast (1) -> xmax = interval -> xmax;
analysis -> intervalTier_cast (2) -> xmax = interval -> xmax;
@@ -191,8 +210,19 @@ void TextGrid_anySound_alignInterval (
}
Melder_assert (analysis -> tiers->size == 4);
IntervalTier analysisWordTier = analysis -> intervalTier_cast (3);
- if (! IntervalTier_check (analysisWordTier))
- Melder_throw (U"Analysis word tier out of order.");
+ tries += 1;
+ if (! IntervalTier_check (analysisWordTier)) {
+ if (tries < maxTries) {
+ Melder_casual (U"TRY ", tries, U" FAILED (SpeechSynthesizer & Sound & TextInterval: align):");
+ Melder_casual (U" Tier ", tierNumber);
+ Melder_casual (U" Interval ", intervalNumber, U": ", interval -> xmin, U" .. ", interval -> xmax, U" “", interval -> text.get(), U"”");
+ Melder_casual (U"REASON OF THIS TEMPORARY FAILURE:\n");
+ Melder_casual (U"Analysis word tier out of order.\n(GOING TO RETRY...)");
+ goto again;
+ } else
+ Melder_throw (U"All ", maxTries, U" tries failed.");
+ } else if (tries > 1)
+ Melder_casual (U"(TRY ", tries, U" SUCCEEDED)");
IntervalTier_removeEmptyIntervals (analysisWordTier, nullptr);
Melder_assert (analysisWordTier -> xmax == analysis -> xmax);
Melder_assert (analysisWordTier -> intervals.size >= 1);
=====================================
sys/Gui_messages.cpp
=====================================
@@ -300,11 +300,7 @@ static void * gui_monitor (double progress, conststring32 message) {
}
/*
Display the alert dialog and synchronously wait for the user to click OK.
- But: it is not impossible that the program crashes during `runModal`,
- especially if `runModal` is called at expose time.
- Write the message to stdout just in case.
*/
- Melder_casual (message32);
[alert runModal];
[alert release];
}
@@ -315,6 +311,7 @@ static char * theMessageFund = nullptr;
static void gui_fatal (conststring32 message) {
free (theMessageFund);
+ Melder_casual (U"PRAAT CRASH MESSAGE:\n", message, U"\n(END OF PRAAT CRASH MESSAGE)");
#if gtk
GuiObject dialog = gtk_message_dialog_new (GTK_WINDOW (Melder_topShell -> d_gtkWindow), GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_ERROR, GTK_BUTTONS_NONE, "%s", Melder_peek32to8 (message));
@@ -333,6 +330,12 @@ static void gui_error (conststring32 message) {
const bool memoryIsLow = str32str (message, U"Out of memory");
if (memoryIsLow)
free (theMessageFund);
+ /*
+ It is not impossible that the program crashes during `gtk_dialog_run` or `MessageBox` or `runModal`,
+ especially if these are called at expose time.
+ Write the message to stdout just in case.
+ */
+ Melder_casual (U"PRAAT ERROR MESSAGE:\n", message, U"(END OF PRAAT ERROR MESSAGE)");
#if gtk
trace (U"create dialog");
GuiObject dialog = gtk_message_dialog_new (GTK_WINDOW (Melder_topShell -> d_gtkWindow), GTK_DIALOG_DESTROY_WITH_PARENT,
@@ -364,6 +367,7 @@ static void gui_error (conststring32 message) {
}
static void gui_warning (conststring32 message) {
+ Melder_casual (U"PRAAT WARNING MESSAGE:\n", message, U"\n(END OF PRAAT WARNING MESSAGE)");
#if gtk
GuiObject dialog = gtk_message_dialog_new (GTK_WINDOW (Melder_topShell -> d_gtkWindow), GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO, GTK_BUTTONS_OK, "%s", Melder_peek32to8 (message));
=====================================
sys/HyperPage.cpp
=====================================
@@ -462,7 +462,7 @@ void HyperPage_script (HyperPage me, double width_inches, double height_inches,
{// scope
autoMelderProgressOff progress;
- autoMelderWarningOff warning;
+ autoMelderWarningOff nowarn;
autoMelderSaveCurrentFolder saveFolder;
if (! MelderFolder_isNull (& my rootDirectory))
Melder_setCurrentFolder (& my rootDirectory);
@@ -560,7 +560,7 @@ void HyperPage_script (HyperPage me, double width_inches, double height_inches,
{// scope
autoMelderProgressOff progress;
- autoMelderWarningOff warning;
+ autoMelderWarningOff nowarn;
autoMelderSaveCurrentFolder saveFolder;
if (! MelderFolder_isNull (& my rootDirectory))
Melder_setCurrentFolder (& my rootDirectory);
=====================================
sys/ManPage.cpp
=====================================
@@ -140,7 +140,7 @@ void ManPage_runAllChunksToCache (ManPage me, Interpreter optionalInterpreterRef
MelderInfo_close ();
} else {
autoMelderProgressOff progress;
- autoMelderWarningOff warning;
+ autoMelderWarningOff nowarn;
autoMelderSaveCurrentFolder saveFolder;
if (! MelderFolder_isNull (rootDirectory))
Melder_setCurrentFolder (rootDirectory);
=====================================
sys/ManPages_toHtml.cpp
=====================================
@@ -228,7 +228,7 @@ static void writeParagraphsAsHtml (ManPages me, Interpreter optionalInterpreterR
);
{// scope
autoMelderProgressOff progress;
- autoMelderWarningOff warning;
+ autoMelderWarningOff nowarn;
autoMelderSaveCurrentFolder saveFolder;
if (! MelderFolder_isNull (& my rootDirectory))
Melder_setCurrentFolder (& my rootDirectory);
View it on GitLab: https://salsa.debian.org/med-team/praat/-/commit/f9c970b50b0b2a39f7e271d1b2075788ca2281fc
--
View it on GitLab: https://salsa.debian.org/med-team/praat/-/commit/f9c970b50b0b2a39f7e271d1b2075788ca2281fc
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20240505/82e831eb/attachment-0001.htm>
More information about the debian-med-commit
mailing list