[med-svn] [praat] 01/10: Imported Upstream version 6.0.17

Rafael Laboissière rlaboiss-guest at moszumanska.debian.org
Mon Jun 13 07:46:27 UTC 2016


This is an automated email from the git hooks/post-receive script.

rlaboiss-guest pushed a commit to branch master
in repository praat.

commit 8932ae14b9aeebbdd3264032163850ebf413919c
Author: Rafael Laboissiere <rafael at laboissiere.net>
Date:   Fri May 6 10:07:58 2016 -0300

    Imported Upstream version 6.0.17
---
 FFNet/FFNet.cpp                            |   2 +-
 LPC/manual_LPC.cpp                         |   6 +-
 dwtools/CC.cpp                             |  13 +-
 dwtools/DTW.cpp                            | 274 +++++++++++++++++------------
 dwtools/DTW.h                              |   6 +-
 dwtools/DTW_and_TextGrid.cpp               |   6 +-
 dwtools/DTW_def.h                          |  19 +-
 dwtools/Sound_extensions.cpp               |   6 +-
 dwtools/Spectrogram_extensions.cpp         |   2 +-
 dwtools/SpeechSynthesizer.cpp              | 191 +++++++++++++++-----
 dwtools/SpeechSynthesizer_and_TextGrid.cpp |  49 +++---
 dwtools/manual_dwtools.cpp                 |   8 +-
 dwtools/praat_David_init.cpp               | 100 +++++++----
 fon/Pitch_Intensity.cpp                    |  15 ++
 fon/Pitch_Intensity.h                      |   2 +
 fon/TextGrid_Sound.cpp                     |  55 +++---
 fon/TextGrid_def.h                         |   7 +
 fon/manual_Script.cpp                      |  13 +-
 fon/manual_tutorials.cpp                   |   9 +-
 fon/praat_Fon.cpp                          |  13 ++
 main/GNU_General_Public_License.txt        |   2 +-
 sys/Formula.cpp                            |  20 ++-
 sys/GuiText.cpp                            |   2 +-
 sys/Interpreter.cpp                        |  23 ++-
 sys/abcio.cpp                              |  58 +++---
 sys/praat_version.h                        |   8 +-
 26 files changed, 568 insertions(+), 341 deletions(-)

diff --git a/FFNet/FFNet.cpp b/FFNet/FFNet.cpp
index 4197a68..5296936 100644
--- a/FFNet/FFNet.cpp
+++ b/FFNet/FFNet.cpp
@@ -67,7 +67,7 @@ Thing_implement (FFNet, Daata, 0);
 static void FFNet_checkLayerNumber (FFNet me, long layer) {
 	if (layer < 1 || layer > my nLayers) {
 		if (layer == 0) {
-			Melder_throw (U"A Layer number of 0 is not allowed.");
+			Melder_throw (U"A layer number of 0 is not allowed.");
 		} else if (layer < 0) {
 			Melder_throw (U"A negative layer number is not allowed.");
 		} else if (layer > my nLayers) {
diff --git a/LPC/manual_LPC.cpp b/LPC/manual_LPC.cpp
index 073e8d8..be1fe93 100644
--- a/LPC/manual_LPC.cpp
+++ b/LPC/manual_LPC.cpp
@@ -1,6 +1,6 @@
 /* manual_LPC.cpp
  *
- * Copyright (C) 1994-2014 David Weenink
+ * Copyright (C) 1994-2016 David Weenink
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -475,10 +475,10 @@ TAG (U"##Use filter at time (s)")
 DEFINITION (U"determines which LPC frame will be chosen to inverse filter the sound. ")
 MAN_END
 
-MAN_BEGIN (U"MFCC", U"djmw", 20141022)
+MAN_BEGIN (U"MFCC", U"djmw", 20160225)
 INTRO (U"One of the @@types of objects@ in P\\s{RAAT}.")
 NORMAL (U"An object of type MFCC represents mel frequency cepstral coefficients "
-	"as a function of time. The coefficients are represented in frames "
+	"as a function of time. The coefficients are represented in a number of frames centred at equally spaced times. "
 	"at constant sampling period.")
 ENTRY (U"MFCC commands")
 NORMAL (U"Creation:")
diff --git a/dwtools/CC.cpp b/dwtools/CC.cpp
index c5e4750..9ad4500 100644
--- a/dwtools/CC.cpp
+++ b/dwtools/CC.cpp
@@ -76,8 +76,17 @@ void CC_init (CC me, double tmin, double tmax, long nt, double dt, double t1, lo
 
 autoMatrix CC_to_Matrix (CC me) {
 	try {
-		autoMatrix thee = Matrix_create (my xmin, my xmax, my nx, my dx, my x1, 1.0, my maximumNumberOfCoefficients, my maximumNumberOfCoefficients, 1.0, 1.0);
-
+		// find number of coefficients by quering all frames.
+		// We cannot use maximumNumberOfCoefficiennts because this number is only used to calculate the inverse
+		long numberOfCoefficients = 0;
+		for (long i = 1; i <= my nx; i++) {
+			CC_Frame cf = & my frame[i];
+			if (cf -> numberOfCoefficients > numberOfCoefficients) {
+				numberOfCoefficients = cf -> numberOfCoefficients;
+			}
+		}
+		autoMatrix thee = Matrix_create (my xmin, my xmax, my nx, my dx, my x1, 1.0, numberOfCoefficients, numberOfCoefficients, 1.0, 1.0);
+		
 		for (long i = 1; i <= my nx; i++) {
 			CC_Frame cf = & my frame[i];
 			for (long j = 1; j <= cf -> numberOfCoefficients; j++) {
diff --git a/dwtools/DTW.cpp b/dwtools/DTW.cpp
index 1a3fc66..fdc20e4 100644
--- a/dwtools/DTW.cpp
+++ b/dwtools/DTW.cpp
@@ -101,10 +101,10 @@ static void DTW_findPath_special (DTW me, int matchStart, int matchEnd, int slop
 /*
    The actual path will be interpolated as follows:
    The distance matrix has cells of dimensions dx by dy.
-   The optimal path connects these cells with one another in the following ways:
+   The optimal path connects these cells with one another in the following way:
 
    In a diagonal ''path'' segment, i.e. when cells have no side in common,
-   the interplated path runs from the lowerleft corner to the upperright corner.
+   the interpolated path runs from the lowerleft corner to the upperright corner.
    If a path segment is horizontal or vertical the path also runs from lowerleft to upperright.
    It is only when a horizontal and a vertical segment have a cell in common that we have to make
    some adjustments to the path in the common cell.
@@ -133,46 +133,20 @@ static void DTW_findPath_special (DTW me, int matchStart, int matchEnd, int slop
 
 /* DTW_getXTime (DTW me, (DTW_getYTime (DTW me, double tx)) == tx */
 double DTW_getYTimeFromXTime (DTW me, double tx) {
-	DTW_Path_Query thee = & my pathQuery;
-
+	// Catch cases where tier would give constant extrapolation
 	if (tx < my xmin) {
 		return my ymin - (my xmin - tx);
 	}
 	if (tx > my xmax) {
 		return my ymax + (tx - my xmax);
 	}
+	DTW_Path_Query thee = & my pathQuery;
+	return RealTier_getValueAtTime(thy yfromx.get(), tx);
 
-	if (! NUMfpp) {
-		NUMmachar ();
-	}
-	double eps = 3 * NUMfpp -> eps;
-
-	/* Find in which column is tx */
-
-	long ib, ie;
-	long ix = (long) floor ( (tx - my x1) / my dx + 1.5);
-	if (ix < 1) {
-		ib = 1; ie = 2;
-	} else if (ix > my nx) {
-		ib = thy nxy - 1; ie = thy nxy;
-	} else {
-		ib = thy xindex[ix].ibegin; ie = thy xindex[ix].iend;
-	}
-	if (ie - ib > 1) {
-		long it = ib + 1;
-		while (tx - thy xytimes[it].x > eps) {
-			it++;
-		}
-		ie = it; ib = it - 1;
-	}
-	double a = (thy xytimes[ib].y - thy xytimes[ie].y) / (thy xytimes[ib].x - thy xytimes[ie].x);
-	double b = thy xytimes[ib].y - a * thy xytimes[ib].x;
-	return a * tx + b;
 }
 
 double DTW_getXTimeFromYTime (DTW me, double ty) {
-	DTW_Path_Query thee = & my pathQuery;
-
+	// Catch cases where tier would give constant extrapolation
 	if (ty < my ymin) {
 		return my ymin - (my ymin - ty);
 	}
@@ -180,32 +154,8 @@ double DTW_getXTimeFromYTime (DTW me, double ty) {
 		return my ymax + (ty - my ymax);
 	}
 
-	if (! NUMfpp) {
-		NUMmachar ();
-	}
-	double eps = 3 * NUMfpp -> eps;
-
-	/* Find in which row is ty */
-
-	long ib, ie;
-	long iy = (long) floor ( (ty - my y1) / my dy + 1.5);
-	if (iy < 1) {
-		ib = 1; ie = 2;
-	} else if (iy > my ny) {
-		ib = thy nxy - 1; ie = thy nxy;
-	} else {
-		ib = thy yindex[iy].ibegin; ie = thy yindex[iy].iend;
-	}
-	if (ie - ib > 1) {
-		long it = ib + 1;
-		while (ty - thy xytimes[it].y > eps) {
-			it++;
-		}
-		ie = it; ib = it - 1;
-	}
-	double a = (thy xytimes[ib].y - thy xytimes[ie].y) / (thy xytimes[ib].x - thy xytimes[ie].x);
-	double b = thy xytimes[ib].y - a * thy xytimes[ib].x;
-	return (ty - b) / a;
+	DTW_Path_Query thee = & my pathQuery;
+	return RealTier_getValueAtTime(thy xfromy.get(), ty);
 }
 
 void DTW_Path_Query_init (DTW_Path_Query me, long ny, long nx) {
@@ -213,56 +163,128 @@ void DTW_Path_Query_init (DTW_Path_Query me, long ny, long nx) {
 	my ny = ny;
 	my nx = nx;
 	my nxy = 2 * (ny > nx ? ny : nx) + 2; // maximum number of points
-	my xytimes = NUMvector<structDTW_Path_xytime> (1, my nxy);
-	my yindex = NUMvector<structDTW_Path_Index> (1, my ny);
-	my xindex = NUMvector<structDTW_Path_Index> (1, my nx);
+	my xfromy = Thing_new (RealTier);
+	my yfromx = Thing_new (RealTier);
 }
 
-static void DTW_Path_makeIndex (DTW me, int xory) {
-	DTW_Path_Query thee = & my pathQuery;
-	DTW_Path_Index index;
-	double x1, dx;
-	long nx;
-
-	if (! NUMfpp) {
-		NUMmachar ();
-	}
-	double eps = 3 * NUMfpp -> eps;
-
-	if (xory == DTW_X) {
-		index = thy xindex;
-		nx = my nx;
-		x1 = my x1;
-		dx = my dx;
-	} else {
-		index = thy yindex;
-		nx = my ny;
-		x1 = my y1;
-		dx = my dy;
-	}
-	double xy_x2 = xory == DTW_X ? thy xytimes[3].x : thy xytimes[3].y;
-	long i = 3;
-	for (long j = 1; j <= nx; j++) {
-		double xlow = x1 + (j - 1 - 0.5) * dx;
-		double xhigh = xlow + dx;
-
-		if (xlow - xy_x2 > -eps && i < thy nxy) { // i.e. xlow >= xy_x2
-			i++;
-			xy_x2 = xory == DTW_X ? thy xytimes[i].x : thy xytimes[i].y;
+/* Recode the path from a chain of cells to a piecewise linear path. */
+void DTW_Path_recode (DTW me) {
+	try {
+		DTW_Path_Query thee = & my pathQuery;
+		long nxy;		// current number of elements in recoded path
+		long nsc_x = 1;	// current number of successive horizontal cells in the cells chain
+		long nsc_y = 1;	// current number of successive vertical cells in the cells chain
+		long nd = 0;	// current number of successive diagonal cells in the cells chain
+		bool yDirection = false;	// previous segment in the original path was vertical
+		bool xDirection = false;	// previous segment in the original path was horizontal
+		long ixp = 0, iyp = 0; // previous cell
+		struct structPoint {
+			double x,y;
+		};
+
+		/* 1. Starting point always at origin */
+		long nxymax = thy nx + thy ny + 2;
+		autoNUMvector<struct structPoint> xytimes (1, nxymax);
+		xytimes[1].x = my xmin;
+		xytimes[1].y = my ymin;
+		/* 2. next point lower left of first cell */
+		nsc_x = my path[1].x;
+		ixp = nsc_x - 1;
+		xytimes[2].x = my x1 + (nsc_x - 1 - 0.5) * my dx;
+		nsc_y = my path[1].y;
+		iyp = nsc_y - 1;
+		xytimes[2].y = my y1 + (nsc_y - 1 - 0.5) * my dy;
+		/* 3. follow all cells. implicit: my x1 - 0.5 * my dx > my xmin && my y1 - 0.5 * my dy > my ymin */
+		nxy = 2;
+		for (long j = 1; j <= my pathLength; j++) {
+			long index; // where are we in the new path?
+			long ix = my path[j].x, iy = my path[j].y;
+			double xright = my x1 + (ix - 1 + 0.5) * my dx;
+			double x, y, f, ytop = my y1 + (iy - 1 + 0.5) * my dy;
+
+			if (iy == iyp) { // horizontal path?
+				xDirection = true;
+				if (yDirection) { // we came from vertical direction?
+					// We came from a vertical direction so this is the second horizontal cell in a row.
+					// The statement after this "if" updates nsc_x to 2.
+					nsc_x = 1; yDirection = false;
+				}
+				nsc_x++;
+
+				if (nsc_y > 1 || nd > 1) {
+					// Previous segment was diagonal or vertical: modify intersection
+					// The vh intersection (x,y) = (nsc_x*dx, dy) * (nsc_y-1)/(nsc_x*nsc_y-1)
+					// A diagonal segment has nsc_y = 1.
+					f = (nsc_y - 1.0) / (nsc_x * nsc_y - 1);
+					x = xright - nsc_x * my dx + nsc_x * my dx * f;
+					y = ytop - my dy + my dy * f;
+					index = nxy - 1;
+					if (nsc_x == 2) {
+						index = nxy;
+						nxy++;
+					}
+					xytimes[index].x = x;
+					xytimes[index].y = y;
+				}
+				nd = 0;
+			} else if (ix == ixp) { // vertical
+				yDirection = true;
+				if (xDirection) {
+					nsc_y = 1; xDirection = false;
+				}
+				nsc_y++;
+
+				if (nsc_x > 1 || nd > 1) {
+					// The hv intersection (x,y) = (dx, dy*nsc_y ) * (nsc_x-1)/(nsc_x*nsc_y-1)
+					f = (nsc_x - 1.0) / (nsc_x * nsc_y - 1);
+					x = xright - my dx + my dx * f;
+					y = ytop - nsc_y * my dy + nsc_y * my dy * f;
+					index = nxy - 1;
+					if (nsc_y == 2) {
+						index = nxy;
+						nxy++;
+					}
+					xytimes[index].x = x;
+					xytimes[index].y = y;
+				}
+				nd = 0;
+			} else if (ix == (ixp + 1) && iy == (iyp + 1)) { // diagonal
+				nd++;
+				if (nd == 1) {
+					nxy++;
+				}
+				nsc_x = nsc_y = 1;
+			} else {
+				Melder_throw (U"The path goes back in time.");
+			}
+			// update
+			xytimes[nxy].x = xright;
+			xytimes[nxy].y = ytop;
+			ixp = ix; iyp = iy;
 		}
 
-		index[j].ibegin = i - 1;
-
-		while (xhigh - xy_x2 > eps && i < thy nxy) { // i.e. xhigh > xy_x2
-			i++;
-			xy_x2 = xory == DTW_X ? thy xytimes[i].x : thy xytimes[i].y;
+		if (my xmax > xytimes[nxy].x || my ymax > xytimes[nxy].y) {
+			nxy++;
+			xytimes[nxy].x = my xmax;
+			xytimes[nxy].y = my ymax;
 		}
-
-		index[j].iend = i;
+		Melder_assert (nxy <= 2 * (my ny > my nx ? my ny : my nx) + 2);
+		
+		thy nxy = nxy;
+		thy yfromx = RealTier_create (my xmin, my xmax);
+		thy xfromy = RealTier_create (my ymin, my ymax);
+		for (long i = 1; i <= nxy; i++) {
+			RealTier_addPoint (thy yfromx.get(), xytimes[i].x, xytimes[i].y);
+			RealTier_addPoint (thy xfromy.get(), xytimes[i].y, xytimes[i].x);
+		}
+		//DTW_Path_makeIndex (me, DTW_X);
+		//DTW_Path_makeIndex (me, DTW_Y);
+	} catch (MelderError) {
+		Melder_throw (me, U": not recoded.");
 	}
 }
 
-/* Recode the path from a chain of cells to a piecewise linear path. */
+#if 0
 void DTW_Path_recode (DTW me) {
 	DTW_Path_Query thee = & my pathQuery;
 	long nxy;		// current number of elements in recoded path
@@ -367,6 +389,7 @@ void DTW_Path_recode (DTW me) {
 	DTW_Path_makeIndex (me, DTW_X);
 	DTW_Path_makeIndex (me, DTW_Y);
 }
+#endif
 
 void DTW_pathRemoveRedundantNodes (DTW me) {
 	long i = 1, skip = 0;
@@ -620,6 +643,14 @@ void DTW_paintDistances (DTW me, Graphics g, double xmin, double xmax, double ym
 	DTW_paintDistances_raw (me, g, xmin, xmax, ymin, ymax, minimum, maximum, garnish, 1);
 }
 
+static double RealTier_getXAtIndex (RealTier me, long point) {
+	double x = NUMundefined;
+	if (point > 0 && point <= my points.size) {
+		x = my points.at [point] -> number;
+	}
+	return x;
+}
+
 static void DTW_drawPath_raw (DTW me, Graphics g, double xmin, double xmax, double ymin,
                               double ymax, int garnish, int inset) {
 	DTW_Path_Query thee = & my pathQuery;
@@ -635,14 +666,17 @@ static void DTW_drawPath_raw (DTW me, Graphics g, double xmin, double xmax, doub
 		Graphics_setInner (g);
 	}
 	Graphics_setWindow (g, xmin, xmax, ymin, ymax);
-
-	for (long i = 1; i < thy nxy; i++) {
-		double x1, y1, x2, y2;
-		if (NUMclipLineWithinRectangle (thy xytimes[i].x, thy xytimes[i].y,
-            thy xytimes[i + 1].x, thy xytimes[i + 1].y,
-            xmin, ymin, xmax, ymax, &x1, &y1, &x2, &y2)) {
-			Graphics_line (g, x1, y1, x2, y2);
+	double x1 = RealTier_getXAtIndex (thy yfromx.get(), 1);
+	double y1 = RealTier_getValueAtIndex (thy yfromx.get(), 1);
+	for (long i = 2; i <= thy yfromx -> points.size; i++) {
+		double x2 = RealTier_getXAtIndex (thy yfromx.get(), i);
+		double y2 = RealTier_getValueAtIndex (thy yfromx.get(), i);
+		double xc1, yc1, xc2, yc2;
+		if (NUMclipLineWithinRectangle (x1, y1,x2, y2, xmin, ymin, xmax, ymax, &xc1, &yc1, &xc2, &yc2)) {
+			Graphics_line (g, xc1, yc1, xc2, yc2);
 		}
+		x1 = x2; 
+		y1 = y2;
 	}
 
 	if (inset) {
@@ -659,8 +693,9 @@ void DTW_drawPath (DTW me, Graphics g, double xmin, double xmax, double ymin, do
 	DTW_drawPath_raw (me, g, xmin, xmax, ymin, ymax, garnish, 1);
 }
 
-static void DTW_drawWarpX_raw (DTW me, Graphics g, double xmin, double xmax, double ymin, double ymax, double tx, int garnish, int inset) {
-	double ty = DTW_getYTimeFromXTime (me, tx);
+static void DTW_drawWarp_raw (DTW me, Graphics g, double xmin, double xmax, double ymin, double ymax, double t, int garnish, bool inset, bool warpX) {
+	double tx = warpX ? t : DTW_getXTimeFromYTime (me, t);
+	double ty = warpX ? DTW_getYTimeFromXTime (me, t): t;
 	int lineType = Graphics_inqLineType (g);
 
 	if (xmin >= xmax) {
@@ -675,7 +710,6 @@ static void DTW_drawWarpX_raw (DTW me, Graphics g, double xmin, double xmax, dou
 	}
 	Graphics_setWindow (g, xmin, xmax, ymin, ymax);
 
-	ty = DTW_getYTimeFromXTime (me, tx);
 	Graphics_setLineType (g, Graphics_DOTTED);
 	if (ty <= ymax) {
 		Graphics_line (g, tx, ymin, tx, ty);
@@ -699,7 +733,11 @@ static void DTW_drawWarpX_raw (DTW me, Graphics g, double xmin, double xmax, dou
 }
 
 void DTW_drawWarpX (DTW me, Graphics g, double xmin, double xmax, double ymin, double ymax, double tx, int garnish) {
-	DTW_drawWarpX_raw (me, g, xmin, xmax, ymin, ymax, tx, garnish, 1);
+	DTW_drawWarp_raw (me, g, xmin, xmax, ymin, ymax, tx, garnish, true, true);
+}
+
+void DTW_drawWarpY (DTW me, Graphics g, double xmin, double xmax, double ymin, double ymax, double ty, int garnish) {
+	DTW_drawWarp_raw (me, g, xmin, xmax, ymin, ymax, ty, garnish, true, false);
 }
 
 static void DTW_and_Sounds_checkDomains (DTW me, Sound *y, Sound *x, double *xmin, double *xmax, double *ymin, double *ymax) {
@@ -1301,7 +1339,6 @@ void DTW_findPath_bandAndSlope (DTW me, double sakoeChibaBand, int localSlope, a
 void DTW_and_Polygon_findPathInside (DTW me, Polygon thee, int localSlope, autoMatrix *cummulativeDists) {
     try {
         double slopes[5] = { DTW_BIG, DTW_BIG, 3, 2, 1.5 };
-        long pathIndex = my nx + my ny - 1; /* Maximum path length */
         // if localSlope == 1 start of path is within 10% of minimum duration. Starts farther away
         long delta_xy = (my nx < my ny ? my nx : my ny) / 10; // if localSlope == 1 start within 10% of
 
@@ -1330,7 +1367,9 @@ void DTW_and_Polygon_findPathInside (DTW me, Polygon thee, int localSlope, autoM
 
         // Make begin part of first column reachable
         long rowto = delta_xy;
-        if (localSlope != 1) rowto = (long) floor (slopes[localSlope]) + 1;
+        if (localSlope != 1) {
+			rowto = (long) floor (slopes[localSlope]) + 1;
+		}
         for (long iy = 2; iy <= rowto; iy++) {
             if (localSlope != 1) {
                 delta[iy][1] = delta[iy - 1][1] + my z[iy][1];
@@ -1341,7 +1380,9 @@ void DTW_and_Polygon_findPathInside (DTW me, Polygon thee, int localSlope, autoM
         }
         // Make begin part of first row reachable
         long colto = delta_xy;
-        if (localSlope != 1) colto = (long) floor (slopes[localSlope]) + 1;
+        if (localSlope != 1) {
+			colto = (long) floor (slopes[localSlope]) + 1;
+		}
         for (long ix = 2; ix <= colto; ix++) {
             if (localSlope != 1) {
                 delta[1][ix] = delta[1][ix -1] + my z[1][ix];
@@ -1469,7 +1510,8 @@ void DTW_and_Polygon_findPathInside (DTW me, Polygon thee, int localSlope, autoM
                 minimum = delta[iy = i][my nx];
             }
         }
-
+        
+        long pathIndex = my nx + my ny - 1; /* Maximum path length */
         my weightedDistance = minimum / (my nx + my ny);
         my path[pathIndex].y = iy;
         long ix = my path[pathIndex].x = my nx;
@@ -1493,7 +1535,7 @@ void DTW_and_Polygon_findPathInside (DTW me, Polygon thee, int localSlope, autoM
             my path[pathIndex].y = iy;
         }
 
-        my pathLength = my nx + my ny - pathIndex;
+        my pathLength = my nx + my ny - 1 - pathIndex + 1;
         if (pathIndex > 1) {
             for (long j = 1; j <= my pathLength; j++) {
                 my path[j] = my path[pathIndex++];
diff --git a/dwtools/DTW.h b/dwtools/DTW.h
index ddd84d2..ef4a037 100644
--- a/dwtools/DTW.h
+++ b/dwtools/DTW.h
@@ -75,11 +75,6 @@ void DTW_findPath (DTW me, int matchStart, int matchEnd, int slope); // deprecat
 
 void DTW_Path_recode (DTW me);
 
-double DTW_getPathY (DTW me, double tx);
-/*
-	Get the time Y-time that corresponds to time t (along X).
-*/
-
 double DTW_getYTimeFromXTime (DTW me, double tx);
 
 double DTW_getXTimeFromYTime (DTW me, double ty);
@@ -93,6 +88,7 @@ void DTW_drawPath (DTW me, Graphics g, double xmin, double xmax, double ymin,
 	double ymax, int garnish);
 
 void DTW_drawWarpX (DTW me, Graphics g, double xmin, double xmax, double ymin, double ymax, double tx, int garnish);
+void DTW_drawWarpY (DTW me, Graphics g, double xmin, double xmax, double ymin, double ymax, double ty, int garnish);
 
 void DTW_pathRemoveRedundantNodes (DTW me);
 
diff --git a/dwtools/DTW_and_TextGrid.cpp b/dwtools/DTW_and_TextGrid.cpp
index c8025f8..e432ed0 100644
--- a/dwtools/DTW_and_TextGrid.cpp
+++ b/dwtools/DTW_and_TextGrid.cpp
@@ -193,7 +193,7 @@ autoTextTier DTW_and_TextTier_to_TextTier_old (DTW me, TextTier thee) {
 
 		for (long i = 1; i <= his points.size; i ++) {
 			TextPoint textpoint = his points.at [i];
-			double time = DTW_getPathY (me, textpoint -> number);
+			double time = DTW_getYTimeFromXTime (me, textpoint -> number);
 			textpoint -> number = time;
 		}
 		return him;
@@ -214,9 +214,9 @@ autoIntervalTier DTW_and_IntervalTier_to_IntervalTier_old (DTW me, IntervalTier
 
 		for (long i = 1; i <= his intervals.size; i ++) {
 			TextInterval textinterval = his intervals.at [i];
-			double xmin = DTW_getPathY (me, textinterval -> xmin);
+			double xmin = DTW_getYTimeFromXTime (me, textinterval -> xmin);
 			textinterval -> xmin = xmin;
-			double xmax = DTW_getPathY (me, textinterval -> xmax);
+			double xmax = DTW_getYTimeFromXTime (me, textinterval -> xmax);
 			textinterval -> xmax = xmax;
 		}
 		return him;
diff --git a/dwtools/DTW_def.h b/dwtools/DTW_def.h
index 86bcf54..4380f4d 100644
--- a/dwtools/DTW_def.h
+++ b/dwtools/DTW_def.h
@@ -30,28 +30,13 @@ oo_DEFINE_STRUCT (DTW_Path)
 oo_END_STRUCT (DTW_Path) 
 #undef ooSTRUCT
 
-#define ooSTRUCT DTW_Path_Index
-oo_DEFINE_STRUCT (DTW_Path_Index)
-	oo_LONG (ibegin)
-	oo_LONG (iend)
-oo_END_STRUCT (DTW_Path_Index)
-#undef ooSTRUCT
-
-#define ooSTRUCT DTW_Path_xytime
-oo_DEFINE_STRUCT (DTW_Path_xytime)
-	oo_DOUBLE (x)
-	oo_DOUBLE (y)
-oo_END_STRUCT (DTW_Path_xytime) 
-#undef ooSTRUCT
-
 #define ooSTRUCT DTW_Path_Query
 oo_DEFINE_STRUCT (DTW_Path_Query)
 	oo_LONG (nx)
 	oo_LONG (ny)
 	oo_LONG (nxy)
-	oo_STRUCT_VECTOR (DTW_Path_xytime, xytimes, nxy)
-	oo_STRUCT_VECTOR (DTW_Path_Index, xindex, nx)
-	oo_STRUCT_VECTOR (DTW_Path_Index, yindex, ny)
+	oo_AUTO_OBJECT (RealTier, 0, yfromx)
+	oo_AUTO_OBJECT (RealTier, 0, xfromy)
 oo_END_STRUCT (DTW_Path_Query) 
 #undef ooSTRUCT
 
diff --git a/dwtools/Sound_extensions.cpp b/dwtools/Sound_extensions.cpp
index 24e5b65..d366f66 100644
--- a/dwtools/Sound_extensions.cpp
+++ b/dwtools/Sound_extensions.cpp
@@ -1307,7 +1307,8 @@ autoTextGrid Sound_to_TextGrid_detectSilences (Sound me, double minPitch, double
 	const char32 *silentLabel, const char32 *soundingLabel) {
 	try {
 		int subtractMeanPressure = 1;
-		autoIntensity thee = Sound_to_Intensity (me, minPitch, timeStep, subtractMeanPressure);
+		autoSound filtered = Sound_filter_passHannBand (me, 80.0, 8000.0, 80.0);
+		autoIntensity thee = Sound_to_Intensity (filtered.get(), minPitch, timeStep, subtractMeanPressure);
 		autoTextGrid him = Intensity_to_TextGrid_detectSilences (thee.get(), silenceThreshold, minSilenceDuration, minSoundingDuration, silentLabel, soundingLabel);
 		return him;
 	} catch (MelderError) {
@@ -1398,8 +1399,7 @@ autoSound Sound_trimSilences (Sound me, double trimDuration, bool onlyAtStartAnd
         }
         const char32 *silentLabel = U"silent", *soundingLabel = U"sounding";
         const char32 *copyLabel = U"";
-        autoTextGrid tg = Sound_to_TextGrid_detectSilences (me, minPitch, timeStep, silenceThreshold,
-            minSilenceDuration, minSoundingDuration, silentLabel, soundingLabel);
+        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 (long iint = 1; iint <= tier -> intervals.size; iint ++) {
diff --git a/dwtools/Spectrogram_extensions.cpp b/dwtools/Spectrogram_extensions.cpp
index 5450f99..13eac71 100644
--- a/dwtools/Spectrogram_extensions.cpp
+++ b/dwtools/Spectrogram_extensions.cpp
@@ -147,7 +147,7 @@ void BandFilterSpectrogram_into_CC (BandFilterSpectrogram me, CC thee, long numb
 	autoNUMvector<double> y (1, my ny);
 	numberOfCoefficients = numberOfCoefficients > my ny - 1 ? my ny - 1 : numberOfCoefficients;
 	Melder_assert (numberOfCoefficients > 0);
-	// 20130220 new interpretation of maximumNumberOfCoefficients necessary for inverse transform 
+	// 20130220 new interpretation of maximumNumberOfCoefficients: necessary for the inverse transform 
 	for (long frame = 1; frame <= my nx; frame++) {
 		CC_Frame ccframe = (CC_Frame) & thy frame[frame];
 		for (long i = 1; i <= my ny; i++) {
diff --git a/dwtools/SpeechSynthesizer.cpp b/dwtools/SpeechSynthesizer.cpp
index 133ec04..3ce9225 100644
--- a/dwtools/SpeechSynthesizer.cpp
+++ b/dwtools/SpeechSynthesizer.cpp
@@ -298,8 +298,8 @@ void SpeechSynthesizer_setSpeechOutputSettings (SpeechSynthesizer me, double sam
 }
 
 void SpeechSynthesizer_playText (SpeechSynthesizer me, const char32 *text) {
-	autoSound thee= SpeechSynthesizer_to_Sound (me, text, 0, 0);
-	Sound_playPart (thee.get(), thy xmin, thy xmax, 0, 0);
+	autoSound thee = SpeechSynthesizer_to_Sound (me, text, nullptr, nullptr);
+	Sound_playPart (thee.get(), thy xmin, thy xmax, nullptr, nullptr);
 }
 
 static autoSound buffer_to_Sound (int *wav, long numberOfSamples, double samplingFrequency)
@@ -319,7 +319,7 @@ static autoSound buffer_to_Sound (int *wav, long numberOfSamples, double samplin
 
 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.");
+		Melder_throw (U"Time is outside interval domains.");
 	}
 
 	// Find interval to split
@@ -332,7 +332,7 @@ static void IntervalTier_addBoundaryUnsorted (IntervalTier me, long iinterval, d
 	ti -> xmax = time;
 	if (isNewleftLabel) TextInterval_setText (ti, newLabel);
 
-	autoTextInterval ti_new = TextInterval_create (time, my xmax, ( ! isNewleftLabel ? newLabel : U"" ));
+	autoTextInterval ti_new = TextInterval_create (time, my xmax, (! isNewleftLabel ? newLabel : U"" ));
 	my intervals. addItem_unsorted_move (ti_new.move());
 }
 
@@ -364,9 +364,104 @@ static void Table_setEventTypeString (Table me) {
 }
 
 static void MelderString_trimWhiteSpaceAtEnd (MelderString *me) {
-	while (my length > 1 && (my string[my length - 1] == U' ' || my string[my length - 1] == U'\t'
-		|| my string[my length - 1] == U'\r' || my string[my length - 1] == U'\n')) {
-		my string[my length - 1] = U'\0'; my length--;
+	while (my length > 1 && (my string [my length - 1] == U' ' || my string [my length - 1] == U'\t'
+		|| my string [my length - 1] == U'\r' || my string [my length - 1] == U'\n'))
+	{
+		my string [my length - 1] = U'\0';
+		my length--;
+	}
+}
+
+static void IntervalTier_mergeSpecialIntervals (IntervalTier me) {
+	long intervalIndex = my intervals.size;
+	TextInterval right = my intervals.at [intervalIndex];
+	long labelLength_right = TextInterval_labelLength (right);
+	bool isEmptyInterval_right = labelLength_right == 0 || (labelLength_right == 1 && Melder_equ (right -> text, U"\001"));
+	while (intervalIndex > 1) {
+		TextInterval left = my intervals.at [intervalIndex - 1];
+		long labelLength_left = TextInterval_labelLength (left);
+		bool isEmptyInterval_left = labelLength_left == 0 || (labelLength_left == 1 && Melder_equ (left -> text, U"\001"));
+		if (isEmptyInterval_right && isEmptyInterval_left) {
+			// remove right interval and empty left interval
+			left -> xmax = right -> xmax;
+			TextInterval_setText (left, U""); 
+			my intervals. removeItem (intervalIndex);
+		}
+		right = left; 
+		isEmptyInterval_right = isEmptyInterval_left;
+		intervalIndex --;
+	}
+}
+
+/* insert boundary at time t and merge/delete intervals after this time */
+static void IntervalTier_insertBoundaryAndMergeIntervalsAfter (IntervalTier me, double t) {
+	if (t <= my xmin || t >= my xmax) {
+		return;
+	}
+	
+	long intervalNumber = IntervalTier_timeToLowIndex (me, t);
+	while (my intervals.size > intervalNumber + 1) {
+		my intervals. removeItem (my intervals.size);
+	}
+	// there can be maximally one interval left to the right of intervalNumber
+	TextInterval ti = my intervals.at [intervalNumber];
+	if (ti -> xmin == t) {   // if t happens to be on a boundary: remove the next interval if it exists
+		if (my intervals.size > intervalNumber) {
+			my intervals. removeItem (my intervals .size);
+		}
+		ti -> xmax = my xmax;
+		TextInterval_setText (ti, U"");
+	} else {
+		ti -> xmax = t;
+		TextInterval last = my intervals.at [my intervals.size];
+		last -> xmin = t;
+		last -> xmax = my xmax;
+		TextInterval_setText (last, U"");
+	}
+}
+
+
+static bool almost_equal (double t1, double t2) {
+	// the "=" sign is essential for a difference of zero if t1 == 0
+	return fabs (t1 - t2) <= 1e-12 * fabs (t1);
+}
+
+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_removeVeryShortIntervals (IntervalTier me) {
+	long iint = 1;
+	while (iint <= my intervals.size) {
+		TextInterval ti = my intervals.at [iint];
+		if (almost_equal (ti -> xmin, ti -> xmax)) {
+			my intervals.removeItem (iint);
+		} else {
+			iint ++;
+		}
 	}
 }
 
@@ -384,13 +479,13 @@ static autoTextGrid Table_to_TextGrid (Table me, const char32 *text, double xmin
 		TextGrid_setIntervalText (thee.get(), 1, 1, text);
 
 		long p1c = 1, p1w = 1;
-		double t1p = xmin;
+		double time_phon_p = xmin;
 		bool wordEnd = false;
 		autoMelderString mark;
 
-		IntervalTier itc = (IntervalTier) thy tiers->at [2];
-		IntervalTier itw = (IntervalTier) thy tiers->at [3];
-		IntervalTier itp = (IntervalTier) thy tiers->at [4];
+		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);
@@ -399,8 +494,8 @@ static autoTextGrid Table_to_TextGrid (Table me, const char32 *text, double xmin
 			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);
+				if (time > xmin && time < xmax) {
+					IntervalTier_addBoundaryUnsorted (clauses, clauses -> intervals.size, time, U"", true);
 				}
 				p1c = pos;
 			} else if (type == espeakEVENT_END) {
@@ -408,10 +503,10 @@ static autoTextGrid Table_to_TextGrid (Table me, const char32 *text, double xmin
 				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);
+				if (time > xmin && time < xmax) {
+					IntervalTier_addBoundaryUnsorted (clauses, clauses -> intervals.size, time, mark.string, true);
 				} else {
-					TextGrid_setIntervalText (thee.get(), 2, itc -> intervals.size, mark.string);
+					TextGrid_setIntervalText (thee.get(), 2, clauses -> intervals.size, mark.string);
 				}
 				p1c = pos;
 
@@ -421,10 +516,10 @@ static autoTextGrid Table_to_TextGrid (Table me, const char32 *text, double xmin
 					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);
+					if (time > xmin && time < xmax) {
+						IntervalTier_addBoundaryUnsorted (words, words -> intervals.size, time, mark.string, true);
 					} else {
-						TextGrid_setIntervalText (thee.get(), 3, itw -> intervals.size, mark.string);
+						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;
@@ -433,34 +528,47 @@ static autoTextGrid Table_to_TextGrid (Table me, const char32 *text, double xmin
 				if (pos < p1w) {
 					continue;
 				}
-				if (time > xmin and time < xmax) {
+				if (time > xmin && time < xmax) {
 					length = pos - p1w;
-					if (pos == textLength) length++;
+					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);
+					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 > t1p) {
+				if (time > time_phon_p) {
 					// Insert new boudary and label interval with the id
 					// TODO: Translate the id to the correct notation
-					TextInterval ti = itp -> intervals.at [itp -> intervals.size];
-					if (time > ti -> xmin and time < ti -> xmax) {
-						IntervalTier_addBoundaryUnsorted (itp, itp -> intervals.size, time, id, false);
+					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, itp -> intervals.size, id);
+					TextGrid_setIntervalText (thee.get(), 4, phonemes -> intervals.size, id);
 				}
-				t1p = time;
+				time_phon_p = time;
 			}
 		}
-		itc -> intervals. sort ();
-		itw -> intervals. sort ();
-		itp -> intervals. sort ();
+		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.");
@@ -471,13 +579,13 @@ static void espeakdata_SetVoiceByName (const char *name, const char *variantName
 {
 	espeak_VOICE voice_selector;
 
-	memset(&voice_selector, 0, sizeof(voice_selector));
+	memset (& voice_selector, 0, sizeof voice_selector);
 	voice_selector.name = Melder_peek32to8 (Melder_cat (Melder_peek8to32 (name), U"+", Melder_peek8to32 (variantName)));  // include variant name in voice stack ??
 
 	if (LoadVoice (name, 1)) {
-		LoadVoice(variantName, 2);
-		DoVoiceChange(voice);
-		SetVoiceStack (&voice_selector, variantName);
+		LoadVoice (variantName, 2);
+		DoVoiceChange (voice);
+		SetVoiceStack (& voice_selector, variantName);
 	}
 }
 
@@ -497,7 +605,7 @@ void SpeechSynthesizer_changeLanguageNameToCurrent (SpeechSynthesizer me) {
 			{ U"Lancashire", nullptr},
 			{ U"Macedonian-test", U"Macedonian"}, { U"Maltese-test", nullptr},
 			{ U"Nahuatl - classical", U"Nahuatl-classical"}, { U"Nepali-test", U"Nepali"}, { U"Northern-sotho", nullptr},
-			{ U"Punjabi-test", U"Punjab"}, 
+			{ U"Punjabi-test", U"Punjabi"},
 			{ U"Russian_test", U"Russian"}, // yes, underscore
 			{ U"Setswana-test", nullptr}, { U"Sinhala", U"Sinhala-test"},
 			{ U"Spanish-latin-american", U"Spanish-latin-am"}, { U"Tatar-test", nullptr},
@@ -505,19 +613,18 @@ void SpeechSynthesizer_changeLanguageNameToCurrent (SpeechSynthesizer me) {
 			{ U"Welsh-test", U"Welsh"}, { U"Wolof-test", nullptr},
 			{nullptr,nullptr}};
 		long index = 0;
-		const char32 *oldName;
-		while (oldName = names[index].oldName) {
+		while (const char32 *oldName = names [index]. oldName) {
 			if (Melder_equ (oldName, my d_voiceLanguageName)) {
-				if (names[index].currentName) {
-					autostring32 newLabel = Melder_dup (names[index].currentName);
+				if (names [index]. currentName) {
+					autostring32 newLabel = Melder_dup (names [index]. currentName);
 					Melder_free (my d_voiceLanguageName);
 					my d_voiceLanguageName = newLabel.transfer();
 					break;
 				} else {
-					Melder_throw (U"Language ", oldName, U" is not available anymore.");
+					Melder_throw (U"Language ", oldName, U" is not available any longer.");
 				}
 			}
-			++index;
+			++ index;
 		}
 	} catch (MelderError) {
 		Melder_throw (U"Cannot change language name.");
diff --git a/dwtools/SpeechSynthesizer_and_TextGrid.cpp b/dwtools/SpeechSynthesizer_and_TextGrid.cpp
index 17a2bdb..4ffbcb4 100644
--- a/dwtools/SpeechSynthesizer_and_TextGrid.cpp
+++ b/dwtools/SpeechSynthesizer_and_TextGrid.cpp
@@ -39,7 +39,7 @@ static autoTable IntervalTiers_to_Table_textAlignmentment (IntervalTier target,
 autoSound SpeechSynthesizer_and_TextInterval_to_Sound (SpeechSynthesizer me, TextInterval thee, autoTextGrid *p_tg)
 {
 	try {
-		if (! thy text || thy text[0] == '\0') {
+		if (! thy text || thy text[0] == U'\0') {
 			Melder_throw (U"No text in TextInterval.");
 		}
 		autoSound him = SpeechSynthesizer_to_Sound (me, thy text, p_tg, nullptr);
@@ -465,13 +465,12 @@ autoTextGrid TextGrid_and_IntervalTier_patch (TextGrid me, IntervalTier thee, co
 }
 
 // We assume that the Sound and the SpeechSynthesizer have the same samplingFrequency
-// schakel waarschuwingen over stiltedetectie uit
 autoTextGrid SpeechSynthesizer_and_Sound_and_TextInterval_align (SpeechSynthesizer me, Sound thee, TextInterval him, double silenceThreshold, double minSilenceDuration, double minSoundingDuration) {
 	try {
 		if (thy xmin != his xmin || thy xmax != his xmax) {
 			Melder_throw (U"Domains of Sound and TextGrid must be equal.");
 		}
-		if (fabs (1.0 / thy dx - my d_samplingFrequency) > NUMfpp -> eps) {
+		if (fabs (1.0 / thy dx - my d_samplingFrequency) > 1e-9) {
 			Melder_throw (U"The sampling frequencies of the SpeechSynthesizer and the Sound must be equal.");
 		}
 		long numberOfTokens = Melder_countTokens (his text);
@@ -481,8 +480,7 @@ autoTextGrid SpeechSynthesizer_and_Sound_and_TextInterval_align (SpeechSynthesiz
 		// Remove silent intervals from start and end of sounds
 		double minPitch = 200.0, timeStep = 0.005, precision = thy dx;
 		double t1_thee, t2_thee;
-		autoSound s_thee = Sound_trimSilencesAtStartAndEnd (thee, 0.0, minPitch, timeStep,
-			silenceThreshold, minSilenceDuration, minSoundingDuration, & t1_thee, & t2_thee);
+		autoSound s_thee = Sound_trimSilencesAtStartAndEnd (thee, 0.0, minPitch, timeStep, silenceThreshold, minSilenceDuration, minSoundingDuration, & t1_thee, & t2_thee);
 		double s_thee_duration = s_thee -> xmax - s_thee -> xmin;
 		bool hasSilence_thee = fabs (t1_thee - thy xmin) > precision || fabs (t2_thee - thy xmax) > precision;
 
@@ -494,42 +492,39 @@ autoTextGrid SpeechSynthesizer_and_Sound_and_TextInterval_align (SpeechSynthesiz
 			my d_wordsPerMinute =  (long) floor (0.5 * (wordsPerMinute_rawTokens + wordsPerMinute_rawText));
 		}
 		autoTextGrid tg2;
-		autoSound s2 = SpeechSynthesizer_and_TextInterval_to_Sound (me, him, & tg2);
+		autoSound synth = SpeechSynthesizer_and_TextInterval_to_Sound (me, him, & tg2);
 		autoTextGrid silentTextGrid;
 		/*
 		 * For the synthesizer the silence threshold has to be < -30 dB, otherwise fricatives will not
-		 * be found as sounding! This is ok since silences are almost at zero amplitudes
+		 * be found as sounding! This is ok since silences are almost at zero amplitudes for synthesized sounds.
 		 * We also have to decrease the minimum silence and minimum sounding duration to catch, for example,
-		 * the final plosive "t" from the word "text"
+		 * the final plosive "t" from the synthesized sound "text"
 		 *
 		 */
-		double s2_silenceThreshold = -40.0, s2_minSilenceDuration = 0.05, s2_minSoundingDuration = 0.05;
-		double t1_s2, t2_s2;
-		autoSound s_s2 = Sound_trimSilencesAtStartAndEnd (s2.get(), 0.0, minPitch, timeStep,
-			s2_silenceThreshold, s2_minSilenceDuration, s2_minSoundingDuration, & t1_s2, & t2_s2);
-		double s_s2_duration = s_s2 -> xmax - s_s2 -> xmin;
-		bool hasSilence_s2 = fabs (t1_s2 - s2 -> xmin) > precision || fabs (t2_s2 - s2 -> xmax) > precision;
-		if (hasSilence_s2) {
-			silentTextGrid = TextGrid_extractPart (tg2.get(), t1_s2, t2_s2, true);
+		double synth_silenceThreshold = -40.0, synth_minSilenceDuration = 0.05, synth_minSoundingDuration = 0.05;
+		double t1_synth, t2_synth;
+		autoSound synth_trimmed = Sound_trimSilencesAtStartAndEnd (synth.get(), 0.0, minPitch, timeStep,
+			synth_silenceThreshold, synth_minSilenceDuration, synth_minSoundingDuration, & t1_synth, & t2_synth);
+		double synth_trimmed_duration = synth_trimmed -> xmax - synth_trimmed -> xmin;
+		bool hasSilence_synth = fabs (t1_synth - synth -> xmin) > precision || fabs (t2_synth - synth -> xmax) > precision;
+		if (hasSilence_synth) {
+			silentTextGrid = TextGrid_extractPart (tg2.get(), t1_synth, t2_synth, true);
 		}
 		double analysisWidth = 0.02, dt = 0.005, band = 0.0;
 		// compare the durations of the two sounds to get an indication of the slope constraint of the DTW
-		double slope = s_thee_duration / s_s2_duration;
-		slope = ( slope > 1.0 ? slope : 1.0 / slope );
-        int constraint = ( slope < 1.5 ? 4.0 : slope < 2.0 ? 3.0 : slope < 3.0 ? 2.0 : 1.0 );
+		double slope = s_thee_duration / synth_trimmed_duration;
+		slope = (slope > 1.0 ? slope : 1.0 / slope);
+        int constraint = (slope < 1.5 ? 4 : slope < 2.0 ? 3 : slope < 3.0 ? 2 : 1);
 		//autoMFCC m1 = Sound_to_MFCC ((hasSilence_thee ? s_thee.get() : thee),
 		//	numberOfCoefficients, analysisWidth, dt, f1_mel, fmax_mel, df_mel);
-		//autoMFCC m2 = Sound_to_MFCC ((hasSilence_s2 ? s_s2.get() : s2.get()),
+		//autoMFCC m2 = Sound_to_MFCC ((hasSilence_synth ? synth_trimmed.get() : synth.get()),
 		//	numberOfCoefficients, analysisWidth, dt, f1_mel, fmax_mel, df_mel);
 		//double wc = 1, wle = 0, wr = 0, wer = 0, dtr = 0;
 		//int matchStart = 1, matchEnd = 1, constraint = 4; // no 1/3 1/2 2/3
 		//autoDTW dtw = CCs_to_DTW (m1.get(), m2.get(), wc, wle, wr, wer, dtr, matchStart, matchEnd, constraint);
-        autoDTW dtw = Sounds_to_DTW (
-			( hasSilence_thee ? s_thee.get() : thee ),
-			( hasSilence_s2 ? s_s2.get() : s2.get() ),
+        autoDTW dtw = Sounds_to_DTW ((hasSilence_thee ? s_thee.get() : thee), (hasSilence_synth ? synth_trimmed.get() : synth.get()),
 			analysisWidth, dt, band, constraint);
-		autoTextGrid result = DTW_and_TextGrid_to_TextGrid (dtw.get(),
-			( hasSilence_s2 ? silentTextGrid.get() : tg2.get() ), precision);
+		autoTextGrid result = DTW_and_TextGrid_to_TextGrid (dtw.get(), (hasSilence_synth ? silentTextGrid.get() : tg2.get()), precision);
 		if (hasSilence_thee) {
 			if (t1_thee > thy xmin) {
 				TextGrid_setEarlierStartTime (result.get(), thy xmin, U"", U"");
@@ -720,8 +715,8 @@ autoTable IntervalTiers_to_Table_textAlignmentment (IntervalTier target, Interva
 		for (long i = 2; i <= pathLength; i++) {
 			structPairOfInteger p = edit -> warpingPath -> path[i];
 			structPairOfInteger p1 = edit -> warpingPath -> path[i - 1];
-			double targetStart = NUMundefined, targetEnd =  NUMundefined;
-			double sourceStart = NUMundefined, sourceEnd =  NUMundefined;
+			double targetStart = NUMundefined, targetEnd = NUMundefined;
+			double sourceStart = NUMundefined, sourceEnd = NUMundefined;
 			const char32 * targetText = U"", *sourceText = U"";
 			long targetInterval = p.y > 1 ? targetOrigin[p.y - 1] : 0;
 			long sourceInterval = p.x > 1 ? sourceOrigin[p.x - 1] : 0;
diff --git a/dwtools/manual_dwtools.cpp b/dwtools/manual_dwtools.cpp
index fb5f199..d7b775a 100644
--- a/dwtools/manual_dwtools.cpp
+++ b/dwtools/manual_dwtools.cpp
@@ -3783,13 +3783,15 @@ DEFINITION (U"determines the label for a silent interval in the TextGrid.") \
 TAG (U"##Sounding interval label") \
 DEFINITION (U"determines the label for a sounding interval in the TextGrid.")
 
-MAN_BEGIN (U"Sound: To TextGrid (silences)...", U"djmw", 20061205)
+MAN_BEGIN (U"Sound: To TextGrid (silences)...", U"djmw", 20160406)
 INTRO (U"A command that creates a @TextGrid in which the silent and sounding intervals of the selected @Sound are marked.")
 ENTRY (U"Settings")
 xxx_to_TextGrid_detectSilences_COMMON_PARAMETERS_HELP
 ENTRY (U"Algorithm")
-NORMAL (U"First the intensity is determined according to the @@Sound: To Intensity...@ command. "
-	"Next the silent and sounding intervas are determined as in the @@Intensity: To TextGrid (silences)...@ command.")
+NORMAL (U"First a copy of the sound is @@Sound: Filter (pass Hann band)...|bandpass filtered@ between 80 and 8000 Hz to "
+	"remove especially the low frequency noise that can have a significant influence on the intensity measurement but does not "
+	"really contribute to the sound. Next the @@Sound: To Intensity...|intensity of the filtered sound@ is determined. "
+	"Finally the silent and sounding intervals are determined @@Intensity: To TextGrid (silences)...|from the intensity curve at .")
 MAN_END
 
 MAN_BEGIN (U"Intensity: To TextGrid (silences)...", U"djmw", 20061201)
diff --git a/dwtools/praat_David_init.cpp b/dwtools/praat_David_init.cpp
index 015dc10..3c11cbc 100644
--- a/dwtools/praat_David_init.cpp
+++ b/dwtools/praat_David_init.cpp
@@ -2009,6 +2009,29 @@ DO
 	}
 END
 
+
+FORM (DTW_drawWarpY, U"DTW: Draw warp (y)", U"DTW: Draw warp (y)...")
+	REAL (U"left Horizontal range", U"0.0")
+	REAL (U"right Horizontal range", U"0.0")
+	REAL (U"left Vertical range", U"0.0")
+	REAL (U"right Vertical range", U"0.0")
+	REAL (U"Time (s)", U"0.1")
+	BOOLEAN (U"Garnish", false);
+	OK
+DO
+	autoPraatPicture picture;
+	LOOP {
+		iam (DTW);
+		DTW_drawWarpY (me, GRAPHICS,
+			GET_REAL (U"left Horizontal range"),
+			GET_REAL (U"right Horizontal range"),
+			GET_REAL (U"left Vertical range"),
+			GET_REAL (U"right Vertical range"),
+			GET_REAL (U"Time"),
+			GET_INTEGER (U"Garnish"));
+	}
+END
+
 DIRECT (DTW_getStartTimeX)
 	LOOP {
 		iam (DTW);
@@ -2138,7 +2161,7 @@ FORM (DTW_getPathY, U"DTW: Get time along path", U"DTW: Get time along path...")
 DO
 	LOOP {
 		iam (DTW);
-		Melder_information (DTW_getPathY (me, GET_REAL (U"Time")));
+		Melder_information (DTW_getYTimeFromXTime (me, GET_REAL (U"Time")));
 	}
 END
 
@@ -2149,7 +2172,7 @@ DO
 	double time = GET_REAL (U"Time at x");
 	LOOP {
 		iam (DTW);
-		Melder_information (DTW_getYTimeFromXTime (me, time), U" s (= y time at z time ", time, U")");
+		Melder_information (DTW_getYTimeFromXTime (me, time), U" s (= y time at x time ", time, U")");
 	}
 END
 
@@ -6821,7 +6844,7 @@ DO
 		iam (SpeechSynthesizer);
 		autoTextGrid tg;
 		autoTable t;
-		autoSound thee = SpeechSynthesizer_to_Sound (me, text, ( createTextGrid ? & tg : nullptr ), ( Melder_debug == -2 ? & t : nullptr ));
+		autoSound thee = SpeechSynthesizer_to_Sound (me, text, (createTextGrid ? & tg : nullptr), (Melder_debug == -2 ? & t : nullptr ));
 		praat_new (thee.move(), my name);
 		if (createTextGrid) {
 			praat_new (tg.move(), my name);
@@ -8540,22 +8563,22 @@ static autoDaata cmuAudioFileRecognizer (int nread, const char *header, MelderFi
 }
 
 void praat_CC_init (ClassInfo klas) {
-	praat_addAction1 (klas, 1, U"Paint...", 0, 1, DO_CC_paint);
-	praat_addAction1 (klas, 1, U"Draw...", 0, 1, DO_CC_drawC0);
-	praat_addAction1 (klas, 1, QUERY_BUTTON, 0, 0, 0);
+	praat_addAction1 (klas, 1, U"Paint...", nullptr, 1, DO_CC_paint);
+	praat_addAction1 (klas, 1, U"Draw...", nullptr, 1, DO_CC_drawC0);
+	praat_addAction1 (klas, 1, QUERY_BUTTON, nullptr, 0, 0);
 	praat_TimeFrameSampled_query_init (klas);
-	praat_addAction1 (klas, 1, U"Get number of coefficients...", 0, 1, DO_CC_getNumberOfCoefficients);
-	praat_addAction1 (klas, 1, U"Get value in frame...", 0, 1, DO_CC_getValueInFrame);
-	praat_addAction1 (klas, 1, U"Get c0 value in frame...", 0, 1, DO_CC_getC0ValueInFrame);
-	praat_addAction1 (klas, 1, U"Get value...", 0, praat_HIDDEN + praat_DEPTH_1, DO_CC_getValue);
-	praat_addAction1 (klas, 0, U"To Matrix", 0, 0, DO_CC_to_Matrix);
-	praat_addAction1 (klas, 2, U"To DTW...", 0, 0, DO_CCs_to_DTW);
+	praat_addAction1 (klas, 1, U"Get number of coefficients...", nullptr, 1, DO_CC_getNumberOfCoefficients);
+	praat_addAction1 (klas, 1, U"Get value in frame...", nullptr, 1, DO_CC_getValueInFrame);
+	praat_addAction1 (klas, 1, U"Get c0 value in frame...", nullptr, 1, DO_CC_getC0ValueInFrame);
+	praat_addAction1 (klas, 1, U"Get value...", nullptr, praat_HIDDEN + praat_DEPTH_1, DO_CC_getValue);
+	praat_addAction1 (klas, 0, U"To Matrix", nullptr, 0, DO_CC_to_Matrix);
+	praat_addAction1 (klas, 2, U"To DTW...", nullptr, 0, DO_CCs_to_DTW);
 }
 
 static void praat_Eigen_Matrix_project (ClassInfo klase, ClassInfo klasm); // deprecated 2014
 static void praat_Eigen_Matrix_project (ClassInfo klase, ClassInfo klasm) {
-	praat_addAction2 (klase, 1, klasm, 1, U"Project...", 0, praat_HIDDEN, DO_Eigen_and_Matrix_projectColumns);
-	praat_addAction2 (klase, 1, klasm, 1, U"To Matrix (project columns)...", 0, praat_HIDDEN, DO_Eigen_and_Matrix_projectColumns);
+	praat_addAction2 (klase, 1, klasm, 1, U"Project...", nullptr, praat_HIDDEN, DO_Eigen_and_Matrix_projectColumns);
+	praat_addAction2 (klase, 1, klasm, 1, U"To Matrix (project columns)...", nullptr, praat_HIDDEN, DO_Eigen_and_Matrix_projectColumns);
 }
 
 static void praat_Eigen_Spectrogram_project (ClassInfo klase, ClassInfo klasm);
@@ -8601,44 +8624,44 @@ static void praat_BandFilterSpectrogram_draw_init (ClassInfo klas) {
 void praat_Matrixft_query_init (ClassInfo klas);
 void praat_Matrixft_query_init (ClassInfo klas) {
 	praat_TimeFrameSampled_query_init (klas);
-	praat_addAction1 (klas, 1, U"Get time from column...", 0, praat_DEPTH_1, DO_BandFilterSpectrogram_getXofColumn);
-	praat_addAction1 (klas, 1, U"-- frequencies --", 0, praat_DEPTH_1, 0);
-	praat_addAction1 (klas, 1, U"Get lowest frequency", 0, praat_DEPTH_1, DO_BandFilterSpectrogram_getLowestFrequency);
-	praat_addAction1 (klas, 1, U"Get highest frequency", 0, praat_DEPTH_1, DO_BandFilterSpectrogram_getHighestFrequency);
-	praat_addAction1 (klas, 1, U"Get number of frequencies", 0, praat_DEPTH_1, DO_BandFilterSpectrogram_getNumberOfFrequencies);
-	praat_addAction1 (klas, 1, U"Get frequency distance", 0, praat_DEPTH_1, DO_BandFilterSpectrogram_getFrequencyDistance);
-	praat_addAction1 (klas, 1, U"Get frequency from row...", 0, praat_DEPTH_1, DO_BandFilterSpectrogram_getFrequencyOfRow);
-	praat_addAction1 (klas, 1, U"-- get value --", 0, praat_DEPTH_1, 0);
-	praat_addAction1 (klas, 1, U"Get value in cell...", 0, praat_DEPTH_1, DO_BandFilterSpectrogram_getValueInCell);
+	praat_addAction1 (klas, 1, U"Get time from column...", nullptr, praat_DEPTH_1, DO_BandFilterSpectrogram_getXofColumn);
+	praat_addAction1 (klas, 1, U"-- frequencies --", nullptr, praat_DEPTH_1, 0);
+	praat_addAction1 (klas, 1, U"Get lowest frequency", nullptr, praat_DEPTH_1, DO_BandFilterSpectrogram_getLowestFrequency);
+	praat_addAction1 (klas, 1, U"Get highest frequency", nullptr, praat_DEPTH_1, DO_BandFilterSpectrogram_getHighestFrequency);
+	praat_addAction1 (klas, 1, U"Get number of frequencies", nullptr, praat_DEPTH_1, DO_BandFilterSpectrogram_getNumberOfFrequencies);
+	praat_addAction1 (klas, 1, U"Get frequency distance", nullptr, praat_DEPTH_1, DO_BandFilterSpectrogram_getFrequencyDistance);
+	praat_addAction1 (klas, 1, U"Get frequency from row...", nullptr, praat_DEPTH_1, DO_BandFilterSpectrogram_getFrequencyOfRow);
+	praat_addAction1 (klas, 1, U"-- get value --", nullptr, praat_DEPTH_1, 0);
+	praat_addAction1 (klas, 1, U"Get value in cell...", nullptr, praat_DEPTH_1, DO_BandFilterSpectrogram_getValueInCell);
 }
 
 void praat_Matrixtype_query_init (ClassInfo klas);
 void praat_Matrixtype_query_init (ClassInfo klas) {
 	praat_TimeFrameSampled_query_init (klas);
-	praat_addAction1 (klas, 1, U"Get time from column...", 0, praat_DEPTH_1, DO_FilterBank_getXofColumn);
-	praat_addAction1 (klas, 1, U"-- frequencies --", 0, praat_DEPTH_1, 0);
-	praat_addAction1 (klas, 1, U"Get lowest frequency", 0, praat_DEPTH_1, DO_FilterBank_getLowestFrequency);
-	praat_addAction1 (klas, 1, U"Get highest frequency", 0, praat_DEPTH_1, DO_FilterBank_getHighestFrequency);
-	praat_addAction1 (klas, 1, U"Get number of frequencies", 0, praat_DEPTH_1, DO_FilterBank_getNumberOfFrequencies);
-	praat_addAction1 (klas, 1, U"Get frequency distance", 0, praat_DEPTH_1, DO_FilterBank_getFrequencyDistance);
-	praat_addAction1 (klas, 1, U"Get frequency from row...", 0, praat_DEPTH_1, DO_FilterBank_getFrequencyOfRow);
-	praat_addAction1 (klas, 1, U"-- get value --", 0, praat_DEPTH_1, 0);
-	praat_addAction1 (klas, 1, U"Get value in cell...", 0, praat_DEPTH_1, DO_FilterBank_getValueInCell);
+	praat_addAction1 (klas, 1, U"Get time from column...", nullptr, praat_DEPTH_1, DO_FilterBank_getXofColumn);
+	praat_addAction1 (klas, 1, U"-- frequencies --", nullptr, praat_DEPTH_1, 0);
+	praat_addAction1 (klas, 1, U"Get lowest frequency", nullptr, praat_DEPTH_1, DO_FilterBank_getLowestFrequency);
+	praat_addAction1 (klas, 1, U"Get highest frequency", nullptr, praat_DEPTH_1, DO_FilterBank_getHighestFrequency);
+	praat_addAction1 (klas, 1, U"Get number of frequencies", nullptr, praat_DEPTH_1, DO_FilterBank_getNumberOfFrequencies);
+	praat_addAction1 (klas, 1, U"Get frequency distance", nullptr, praat_DEPTH_1, DO_FilterBank_getFrequencyDistance);
+	praat_addAction1 (klas, 1, U"Get frequency from row...", nullptr, praat_DEPTH_1, DO_FilterBank_getFrequencyOfRow);
+	praat_addAction1 (klas, 1, U"-- get value --", nullptr, praat_DEPTH_1, 0);
+	praat_addAction1 (klas, 1, U"Get value in cell...", nullptr, praat_DEPTH_1, DO_FilterBank_getValueInCell);
 }
 
 static void praat_FilterBank_query_init (ClassInfo klas);
 static void praat_FilterBank_query_init (ClassInfo klas) {
-	praat_addAction1 (klas, 0, QUERY_BUTTON, 0, 0, 0);
+	praat_addAction1 (klas, 0, QUERY_BUTTON, nullptr, 0, 0);
 	praat_Matrixtype_query_init (klas);
-	praat_addAction1 (klas, 0, U"-- frequency scales --", 0, praat_DEPTH_1, 0);
-	praat_addAction1 (klas, 1, U"Get frequency in Hertz...", 0, praat_DEPTH_1, DO_FilterBank_getFrequencyInHertz);
-	praat_addAction1 (klas, 1, U"Get frequency in Bark...", 0, praat_DEPTH_1, DO_FilterBank_getFrequencyInBark);
-	praat_addAction1 (klas, 1, U"Get frequency in mel...", 0, praat_DEPTH_1, DO_FilterBank_getFrequencyInMel);
+	praat_addAction1 (klas, 0, U"-- frequency scales --", nullptr, praat_DEPTH_1, 0);
+	praat_addAction1 (klas, 1, U"Get frequency in Hertz...", nullptr, praat_DEPTH_1, DO_FilterBank_getFrequencyInHertz);
+	praat_addAction1 (klas, 1, U"Get frequency in Bark...", nullptr, praat_DEPTH_1, DO_FilterBank_getFrequencyInBark);
+	praat_addAction1 (klas, 1, U"Get frequency in mel...", nullptr, praat_DEPTH_1, DO_FilterBank_getFrequencyInMel);
 }
 
 static void praat_FilterBank_modify_init (ClassInfo klas);
 static void praat_FilterBank_modify_init (ClassInfo klas) {
-	// praat_addAction1 (klas, 0, MODIFY_BUTTON, 0, 0, 0); 
+	// praat_addAction1 (klas, 0, MODIFY_BUTTON, nullptr, 0, 0); 
 	praat_addAction1 (klas, 0, U"Equalize intensities...", nullptr, praat_DEPTH_1, DO_FilterBank_equalizeIntensities);
 }
 
@@ -9064,6 +9087,7 @@ void praat_uvafon_David_init () {
 	praat_addAction1 (classDTW, 0, U"Draw path...", nullptr, 1, DO_DTW_drawPath);
 	praat_addAction1 (classDTW, 0, U"Paint distances...", nullptr, 1, DO_DTW_paintDistances);
 	praat_addAction1 (classDTW, 0, U"Draw warp (x)...", nullptr, 1, DO_DTW_drawWarpX);
+	praat_addAction1 (classDTW, 0, U"Draw warp (y)...", nullptr, 1, DO_DTW_drawWarpY);
 	praat_addAction1 (classDTW, 0, QUERY_BUTTON, nullptr, 0, 0);
 	praat_addAction1 (classDTW, 1, U"Query time domains", nullptr, 1, 0);
 	praat_addAction1 (classDTW, 1, U"Get start time (x)", nullptr, 2, DO_DTW_getStartTimeX);
diff --git a/fon/Pitch_Intensity.cpp b/fon/Pitch_Intensity.cpp
index 68d9f29..ab3175b 100644
--- a/fon/Pitch_Intensity.cpp
+++ b/fon/Pitch_Intensity.cpp
@@ -73,6 +73,21 @@ void Pitch_Intensity_draw (Pitch pitch, Intensity intensity, Graphics g,
 	}
 }
 
+double Pitch_Intensity_getMean (Pitch thee, Intensity me) {
+	long numberOfValidLocalMeasurements = 0;
+	double sumOfLocalValues = 0.0;
+	for (long iframe = 1; iframe <= my nx; iframe ++) {
+		double t = Sampled_indexToX (me, iframe);
+		bool localMeasurentIsValid = Pitch_isVoiced_t (thee, t);
+		if (localMeasurentIsValid) {
+			double localValue = my z [1] [iframe];
+			sumOfLocalValues += localValue;
+			numberOfValidLocalMeasurements += 1;
+		}
+	}
+	return numberOfValidLocalMeasurements > 0 ? sumOfLocalValues / numberOfValidLocalMeasurements : NUMundefined;
+}
+
 double Pitch_Intensity_getMeanAbsoluteSlope (Pitch thee, Intensity me) {
 	long numberOfValidLocalMeasurements = 0;
 	double sumOfLocalAbsoluteSlopes = 0.0;
diff --git a/fon/Pitch_Intensity.h b/fon/Pitch_Intensity.h
index dedc5c8..b6b91ae 100644
--- a/fon/Pitch_Intensity.h
+++ b/fon/Pitch_Intensity.h
@@ -24,6 +24,8 @@
 void Pitch_Intensity_draw (Pitch pitch, Intensity intensity, Graphics g,
 	double f1, double f2, double s1, double s2, bool garnish, int connect);
 
+double Pitch_Intensity_getMean (Pitch thee, Intensity me);
+
 double Pitch_Intensity_getMeanAbsoluteSlope (Pitch pitch, Intensity intensity);
 
 /* End of file Pitch_Intensity.h */
diff --git a/fon/TextGrid_Sound.cpp b/fon/TextGrid_Sound.cpp
index 9e4529a..6d7f4e1 100644
--- a/fon/TextGrid_Sound.cpp
+++ b/fon/TextGrid_Sound.cpp
@@ -25,16 +25,19 @@
 static bool IntervalTier_check (IntervalTier me) {
 	for (long iinterval = 1; iinterval <= my intervals.size; iinterval ++) {
 		TextInterval interval = my intervals.at [iinterval];
-		if (interval -> xmin >= interval -> xmax)
+		if (interval -> xmin >= interval -> xmax) {
+			Melder_casual (U"Interval ", iinterval, U" starts at ", interval -> xmin,
+				U" but ends at ", interval -> xmax, U" seconds.");
 			return false;
+		}
 	}
 	if (my intervals.size < 2) return true;
 	for (long iinterval = 1; iinterval < my intervals.size; iinterval ++) {
 		TextInterval thisInterval = my intervals.at [iinterval];
 		TextInterval nextInterval = my intervals.at [iinterval + 1];
 		if (thisInterval -> xmax != nextInterval -> xmin) {
-			//Melder_casual (U"Interval ", iinterval, U" ends at ", thisInterval -> xmax,
-			//	U" but the next interval starts at ", nextInterval -> xmin, U" seconds.");
+			Melder_casual (U"Interval ", iinterval, U" ends at ", thisInterval -> xmax,
+				U" but the next interval starts at ", nextInterval -> xmin, U" seconds.");
 			return false;
 		}
 	}
@@ -163,32 +166,28 @@ void TextGrid_anySound_alignInterval (TextGrid me, Function anySound, long tierN
 		double silenceThreshold = -35.0, 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
-			}
+			analysis = SpeechSynthesizer_and_Sound_and_TextInterval_align
+				(synthesizer.get(), part.get(), interval, silenceThreshold, minSilenceDuration, minSoundingDuration);
 		}
 		if (analysis) {
 			/*
 			 * Clean up the analysis.
 			 */
-			Melder_assert (analysis -> xmin == interval -> xmin);
+			Melder_assert (fabs (analysis -> xmin - interval -> xmin) < 1e-12);
 			if (analysis -> xmax != interval -> xmax) {
-				analysis -> xmax = interval -> xmax;
-				((IntervalTier) analysis -> tiers->at [1]) -> xmax = interval -> xmax;
-				((IntervalTier) analysis -> tiers->at [2]) -> xmax = interval -> xmax;
-				((IntervalTier) analysis -> tiers->at [3]) -> xmax = interval -> xmax;
-				((IntervalTier) analysis -> tiers->at [4]) -> xmax = interval -> xmax;
-				((IntervalTier) analysis -> tiers->at [1]) -> intervals.at [((IntervalTier) analysis -> tiers->at [1]) -> intervals.size] -> xmax = interval -> xmax;
-				((IntervalTier) analysis -> tiers->at [2]) -> intervals.at [((IntervalTier) analysis -> tiers->at [2]) -> intervals.size] -> xmax = interval -> xmax;
-				((IntervalTier) analysis -> tiers->at [3]) -> intervals.at [((IntervalTier) analysis -> tiers->at [3]) -> intervals.size] -> xmax = interval -> xmax;
-				((IntervalTier) analysis -> tiers->at [4]) -> intervals.at [((IntervalTier) analysis -> tiers->at [4]) -> intervals.size] -> xmax = interval -> xmax;
 				//Melder_fatal (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;
+				analysis -> intervalTier_cast (3) -> xmax = interval -> xmax;
+				analysis -> intervalTier_cast (4) -> xmax = interval -> xmax;
+				analysis -> intervalTier_cast (1) -> intervals.at [analysis -> intervalTier_cast (1) -> intervals.size] -> xmax = interval -> xmax;
+				analysis -> intervalTier_cast (2) -> intervals.at [analysis -> intervalTier_cast (2) -> intervals.size] -> xmax = interval -> xmax;
+				analysis -> intervalTier_cast (3) -> intervals.at [analysis -> intervalTier_cast (3) -> intervals.size] -> xmax = interval -> xmax;
+				analysis -> intervalTier_cast (4) -> intervals.at [analysis -> intervalTier_cast (4) -> intervals.size] -> xmax = interval -> xmax;
 			}
 			Melder_assert (analysis -> tiers->size == 4);
-			Thing_cast (IntervalTier, analysisWordTier, analysis -> tiers->at [3]);
+			IntervalTier analysisWordTier = analysis -> intervalTier_cast (3);
 			if (! IntervalTier_check (analysisWordTier))
 				Melder_throw (U"Analysis word tier out of order.");
 			IntervalTier_removeEmptyIntervals (analysisWordTier, nullptr);
@@ -202,7 +201,7 @@ void TextGrid_anySound_alignInterval (TextGrid me, Function anySound, long tierN
 				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]);
+			IntervalTier analysisPhonemeTier = analysis -> intervalTier_cast (4);
 			if (! IntervalTier_check (analysisPhonemeTier))
 				Melder_throw (U"Analysis phoneme tier out of order.");
 			IntervalTier_removeEmptyIntervals (analysisPhonemeTier, analysisWordTier);
@@ -228,7 +227,7 @@ void TextGrid_anySound_alignInterval (TextGrid me, Function anySound, long tierN
 			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]);
+				Function tier = 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."
@@ -254,7 +253,7 @@ void TextGrid_anySound_alignInterval (TextGrid me, Function anySound, long tierN
 			long wordIntervalNumber = IntervalTier_hasTime (wordTier, interval -> xmin);
 			Melder_assert (wordIntervalNumber != 0);
 			if (analysis) {
-				Thing_cast (IntervalTier, analysisWordTier, analysis -> tiers->at [3]);
+				IntervalTier analysisWordTier = analysis -> intervalTier_cast (3);
 				if (! IntervalTier_check (analysisWordTier))
 					Melder_throw (U"Analysis word tier out of order (3).");
 				if (! IntervalTier_check (wordTier))
@@ -290,7 +289,7 @@ void TextGrid_anySound_alignInterval (TextGrid me, Function anySound, long tierN
 			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];
+				Function tier = 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."
@@ -306,7 +305,7 @@ void TextGrid_anySound_alignInterval (TextGrid me, Function anySound, long tierN
 					phonemeTierNumber = wordTierNumber ? wordTierNumber + 1 : tierNumber + 1);
 			}
 			Melder_assert (phonemeTierNumber >= 1 && phonemeTierNumber <= my tiers->size);
-			phonemeTier = static_cast <IntervalTier> (my tiers->at [phonemeTierNumber]);
+			phonemeTier = my intervalTier_cast (phonemeTierNumber);
 			/*
 			 * Make sure that the phoneme tier has boundaries at the edges of the interval.
 			 */
@@ -317,7 +316,7 @@ void TextGrid_anySound_alignInterval (TextGrid me, Function anySound, long tierN
 			long phonemeIntervalNumber = IntervalTier_hasTime (phonemeTier, interval -> xmin);
 			Melder_assert (phonemeIntervalNumber != 0);
 			if (analysis.get()) {
-				Thing_cast (IntervalTier, analysisPhonemeTier, analysis -> tiers->at [4]);
+				IntervalTier analysisPhonemeTier = analysis -> intervalTier_cast (4);
 				for (long ianalysisInterval = 1; ianalysisInterval <= analysisPhonemeTier -> intervals.size; ianalysisInterval ++) {
 					TextInterval analysisInterval = analysisPhonemeTier -> intervals.at [ianalysisInterval];
 					TextInterval phonemeInterval = nullptr;
@@ -394,7 +393,7 @@ void TextGrid_Sound_draw (TextGrid me, Sound sound, Graphics g, double tmin, dou
 				if (intmin >= intmax) continue;
 				if (showBoundaries && intmin > tmin && intmin < tmax) {
 					Graphics_setLineType (g, Graphics_DOTTED);
-					Graphics_line (g, intmin, -1.0, intmin, 1.0);   /* In sound part. */
+					Graphics_line (g, intmin, -1.0, intmin, 1.0);   // in sound part
 					Graphics_setLineType (g, Graphics_DRAWN);
 				}      
 				/* Draw left boundary. */
@@ -415,7 +414,7 @@ void TextGrid_Sound_draw (TextGrid me, Sound sound, Graphics g, double tmin, dou
 				if (t > tmin && t < tmax) {
 					if (showBoundaries) {
 						Graphics_setLineType (g, Graphics_DOTTED);
-						Graphics_line (g, t, -1.0, t, 1.0);   /* In sound part. */
+						Graphics_line (g, t, -1.0, t, 1.0);   // in sound part
 						Graphics_setLineType (g, Graphics_DRAWN);
 					}
 					Graphics_line (g, t, ymin, t, 0.8 * ymin + 0.2 * ymax);
diff --git a/fon/TextGrid_def.h b/fon/TextGrid_def.h
index 1a2c097..ce02807 100644
--- a/fon/TextGrid_def.h
+++ b/fon/TextGrid_def.h
@@ -91,6 +91,13 @@ oo_DEFINE_CLASS (TextGrid, Function)
 			override;
 		void v_scaleX (double xminfrom, double xmaxfrom, double xminto, double xmaxto)
 			override;
+
+		IntervalTier intervalTier_cast (int32 tierNumber) {
+			return static_cast <IntervalTier> (our tiers -> at [tierNumber]);
+		}
+		TextTier textTier_cast (int32 tierNumber) {
+			return static_cast <TextTier> (our tiers -> at [tierNumber]);
+		}
 	#endif
 
 oo_END_CLASS (TextGrid)
diff --git a/fon/manual_Script.cpp b/fon/manual_Script.cpp
index dd162f8..ed33de6 100644
--- a/fon/manual_Script.cpp
+++ b/fon/manual_Script.cpp
@@ -1382,7 +1382,7 @@ LIST_ITEM1 (U"@@Scripting 5.2. Expressions@ (numeric, string)")
 LIST_ITEM1 (U"@@Scripting 5.3. Jumps@ (if, then, elsif, else, endif)")
 LIST_ITEM1 (U"@@Scripting 5.4. Loops@ (for/endfor, while/endwhile, repeat/until)")
 LIST_ITEM1 (U"@@Scripting 5.5. Procedures@ (\\@ , procedure)")
-LIST_ITEM1 (U"@@Scripting 5.6. Arrays")
+LIST_ITEM1 (U"@@Scripting 5.6. Arrays and dictionaries")
 LIST_ITEM1 (U"@@Scripting 5.7. Including other scripts")
 LIST_ITEM1 (U"@@Scripting 5.8. Quitting@ (exitScript)")
 LIST_ITEM (U"@@Scripting 6. Communication outside the script")
@@ -2217,7 +2217,7 @@ CODE (U"#for i from 1 to n")
 CODE (U"#endfor")
 MAN_END
 
-MAN_BEGIN (U"Scripting 5. Language elements reference", U"ppgb", 20130421)
+MAN_BEGIN (U"Scripting 5. Language elements reference", U"ppgb", 20160405)
 NORMAL (U"In a Praat script, you can use variables, expressions, and functions, of numeric as well as string type, "
 	"and most of the control structures known from other procedural computer languages. "
 	"The way the distinction between numbers and strings is made, may remind you of the programming language Basic.")
@@ -2226,7 +2226,7 @@ LIST_ITEM (U"@@Scripting 5.2. Expressions@ (numeric, string)")
 LIST_ITEM (U"@@Scripting 5.3. Jumps@ (if, then, elsif, else, endif)")
 LIST_ITEM (U"@@Scripting 5.4. Loops@ (for/endfor, while/endwhile, repeat/until)")
 LIST_ITEM (U"@@Scripting 5.5. Procedures@ (\\@ , procedure)")
-LIST_ITEM (U"@@Scripting 5.6. Arrays@")
+LIST_ITEM (U"@@Scripting 5.6. Arrays and dictionaries@")
 LIST_ITEM (U"@@Scripting 5.7. Including other scripts@")
 LIST_ITEM (U"@@Scripting 5.8. Quitting@ (exit)")
 MAN_END
@@ -2650,7 +2650,7 @@ CODE (U"#endproc")
 NORMAL (U"However, this uses variable substitution, a trick better avoided.")
 MAN_END
 
-MAN_BEGIN (U"Scripting 5.6. Arrays", U"ppgb", 20140111)
+MAN_BEGIN (U"Scripting 5.6. Arrays and dictionaries", U"ppgb", 20160405)
 NORMAL (U"You can use arrays of numeric and string variables:")
 CODE (U"#for i #from 1 #to 5")
 	CODE1 (U"square [i] = i * i")
@@ -2664,6 +2664,11 @@ CODE (U"#for i #from 1 #to 5")
 	CODE1 (U"#appendInfoLine: \"The square of \", i, \" is \", square [i]")
 CODE (U"#endfor")
 NORMAL (U"You can use any number of variables in a script, but you can also use Matrix or Sound objects for arrays.")
+NORMAL (U"In the examples above, the %index into the array was always a number. "
+	" A %hash or %dictionary is an array variable where the index is a string:")
+CODE (U"age [\"John\"] = 36")
+CODE (U"age [\"Babs\"] = 39")
+CODE (U"#writeInfoLine: \"John is \", age [\"John\"], \" years old.\"")
 MAN_END
 
 MAN_BEGIN (U"Scripting 5.7. Including other scripts", U"ppgb", 20140111)
diff --git a/fon/manual_tutorials.cpp b/fon/manual_tutorials.cpp
index d4d40a2..16f6716 100644
--- a/fon/manual_tutorials.cpp
+++ b/fon/manual_tutorials.cpp
@@ -23,10 +23,15 @@
 void manual_tutorials_init (ManPages me);
 void manual_tutorials_init (ManPages me) {
 
-MAN_BEGIN (U"What's new?", U"ppgb", 20160323)
+MAN_BEGIN (U"What's new?", U"ppgb", 20160421)
 INTRO (U"Latest changes in Praat.")
 //LIST_ITEM (U"• Manual page about @@drawing a vowel triangle at .")
 
+NORMAL (U"##6.0.17# (21 April 2016)")
+LIST_ITEM (U"• TextGrid window: better automatic alignment.")
+NORMAL (U"##6.0.16# (5 April 2016)")
+LIST_ITEM (U"• Scripting: \"hashes\": variables can now be indexed with strings rather than only with numbers.")
+LIST_ITEM (U"• TextGrid window: fewer out-of-order messages in automatic alignment.")
 NORMAL (U"##6.0.15# (21 March 2016)")
 LIST_ITEM (U"• TextGrid window: removed a bug whereby Praat could do automatic alignment only on sounds sampled at 44.1 kHz.")
 LIST_ITEM (U"• TextGrid window: improved the location of the final boundary in automatic alignment.")
@@ -569,7 +574,7 @@ LIST_ITEM (U"• Scripting: support for arrays with multiple indexes.")
 LIST_ITEM (U"• Linux: made spectrogram drawing compatible with Ubuntu 10.10.")
 LIST_ITEM (U"• Linux: made sound more easily available on Ubuntu 10.10.")
 NORMAL (U"##5.2.01# (4 November 2010)")
-LIST_ITEM (U"• Scripting: support for numeric @@Scripting 5.6. Arrays|arrays at .")
+LIST_ITEM (U"• Scripting: support for numeric @@Scripting 5.6. Arrays and dictionaries|arrays at .")
 MAN_END
 
 MAN_BEGIN (U"What was new in 5.2?", U"ppgb", 20101029)
diff --git a/fon/praat_Fon.cpp b/fon/praat_Fon.cpp
index 4c5f22c..f64333e 100644
--- a/fon/praat_Fon.cpp
+++ b/fon/praat_Fon.cpp
@@ -1870,6 +1870,18 @@ DO
 		GET_REAL (U"From intensity"), GET_REAL (U"To intensity"), GET_INTEGER (U"Garnish"), 1);
 END2 }
 
+DIRECT2 (Pitch_Intensity_getMean) {
+	Pitch pitch = nullptr;
+	Intensity intensity = nullptr;
+	LOOP {
+		if (CLASS == classPitch) pitch = (Pitch) OBJECT;
+		if (CLASS == classIntensity) intensity = (Intensity) OBJECT;
+		if (pitch && intensity) break;   // OPTIMIZE
+	}
+	double value = Pitch_Intensity_getMean (pitch, intensity);
+	Melder_informationReal (value, U"dB");
+END2 }
+
 DIRECT2 (Pitch_Intensity_getMeanAbsoluteSlope) {
 	Pitch pitch = nullptr;
 	Intensity intensity = nullptr;
@@ -7216,6 +7228,7 @@ praat_addAction1 (classTransition, 0, U"Cast", nullptr, 0, nullptr);
 	praat_addAction2 (classIntensity, 1, classPitch, 1, U"Draw", nullptr, 0, nullptr);
 	praat_addAction2 (classIntensity, 1, classPitch, 1, U"Draw (phonetogram)...", nullptr, 0, DO_Pitch_Intensity_draw);
 	praat_addAction2 (classIntensity, 1, classPitch, 1, U"Speckle (phonetogram)...", nullptr, praat_HIDDEN, DO_Pitch_Intensity_speckle);   /* grandfathered 2005 */
+	praat_addAction2 (classIntensity, 1, classPitch, 1, U"Get mean", nullptr, 1, DO_Pitch_Intensity_getMean);
 	praat_addAction2 (classIntensity, 1, classPitch, 1, U"Get mean absolute slope", nullptr, 1, DO_Pitch_Intensity_getMeanAbsoluteSlope);
 	praat_addAction2 (classIntensity, 1, classPointProcess, 1, U"To IntensityTier", nullptr, 0, DO_Intensity_PointProcess_to_IntensityTier);
 	praat_addAction2 (classIntensityTier, 1, classPointProcess, 1, U"To IntensityTier", nullptr, 0, DO_IntensityTier_PointProcess_to_IntensityTier);
diff --git a/main/GNU_General_Public_License.txt b/main/GNU_General_Public_License.txt
index c92e00f..1e12414 100644
--- a/main/GNU_General_Public_License.txt
+++ b/main/GNU_General_Public_License.txt
@@ -3,7 +3,7 @@ GNU GENERAL PUBLIC LICENSE
 Version 2, June 1991 
 
 Copyright (C) 1989, 1991 Free Software Foundation, Inc.  
-59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
+51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.
diff --git a/sys/Formula.cpp b/sys/Formula.cpp
index caf3ba9..77f0cfb 100644
--- a/sys/Formula.cpp
+++ b/sys/Formula.cpp
@@ -3053,9 +3053,13 @@ static void do_indexedNumericVariable () {
 	w -= nindex;
 	for (int iindex = 1; iindex <= nindex; iindex ++) {
 		Stackel index = & theStack [w + iindex];
-		if (index -> which != Stackel_NUMBER)
-			Melder_throw (U"In indexed variables, the index has to be a number, not ", Stackel_whichText (index), U".");
-		MelderString_append (& totalVariableName, index -> number, iindex == nindex ? U"]" : U",");
+		if (index -> which == Stackel_NUMBER) {
+			MelderString_append (& totalVariableName, index -> number, iindex == nindex ? U"]" : U",");
+		} else if (index -> which == Stackel_STRING) {
+			MelderString_append (& totalVariableName, U"\"", index -> string, U"\"", iindex == nindex ? U"]" : U",");
+		} else {
+			Melder_throw (U"In indexed variables, the index has to be a number or a string, not ", Stackel_whichText (index), U".");
+		}
 	}
 	InterpreterVariable var = Interpreter_hasVariable (theInterpreter, totalVariableName.string);
 	if (! var)
@@ -3074,9 +3078,13 @@ static void do_indexedStringVariable () {
 	w -= nindex;
 	for (int iindex = 1; iindex <= nindex; iindex ++) {
 		Stackel index = & theStack [w + iindex];
-		if (index -> which != Stackel_NUMBER)
-			Melder_throw (U"In indexed variables, the index has to be a number, not ", Stackel_whichText (index), U".");
-		MelderString_append (& totalVariableName, index -> number, iindex == nindex ? U"]" : U",");
+		if (index -> which == Stackel_NUMBER) {
+			MelderString_append (& totalVariableName, index -> number, iindex == nindex ? U"]" : U",");
+		} else if (index -> which == Stackel_STRING) {
+			MelderString_append (& totalVariableName, U"\"", index -> string, U"\"", iindex == nindex ? U"]" : U",");
+		} else {
+			Melder_throw (U"In indexed variables, the index has to be a number or a string, not ", Stackel_whichText (index), U".");
+		}
 	}
 	InterpreterVariable var = Interpreter_hasVariable (theInterpreter, totalVariableName.string);
 	if (! var)
diff --git a/sys/GuiText.cpp b/sys/GuiText.cpp
index 5e68952..2ce127e 100644
--- a/sys/GuiText.cpp
+++ b/sys/GuiText.cpp
@@ -464,7 +464,7 @@ static void NativeText_getText (GuiObject widget, char32 *buffer, long length) {
 		buffer [j] = U'\0';
 		DisposeHandle (han);
 	}
-	buffer [length] = L'\0';   // superfluous?
+	buffer [length] = U'\0';   // superfluous?
 }
 #endif
 
diff --git a/sys/Interpreter.cpp b/sys/Interpreter.cpp
index be3d2dd..78ed773 100644
--- a/sys/Interpreter.cpp
+++ b/sys/Interpreter.cpp
@@ -1555,9 +1555,15 @@ void Interpreter_run (Interpreter me, char32 *text) {
 								}
 								if (*p == U'\n' || *p == U'\0')
 									Melder_throw (U"Missing closing bracket (]) in indexed variable.");
-								double numericIndexValue;
-								Interpreter_numericExpression (me, index.string, & numericIndexValue);
-								MelderString_append (& indexedVariableName, numericIndexValue);
+								struct Formula_Result result;
+								Interpreter_anyExpression (me, index.string, & result);
+								if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
+									double numericIndexValue = result.result.numericResult;
+									MelderString_append (& indexedVariableName, numericIndexValue);
+								} else if (result.expressionType == kFormula_EXPRESSION_TYPE_STRING) {
+									MelderString_append (& indexedVariableName, U"\"", result.result.stringResult, U"\"");
+									Melder_free (result.result.stringResult);
+								}
 								MelderString_appendCharacter (& indexedVariableName, *p);
 								if (*p == U']') {
 									break;
@@ -1694,8 +1700,15 @@ void Interpreter_run (Interpreter me, char32 *text) {
 								}
 								if (*p == U'\n' || *p == U'\0')
 									Melder_throw (U"Missing closing bracket (]) in indexed variable.");
-								Interpreter_numericExpression (me, index.string, & value);
-								MelderString_append (& indexedVariableName, value);
+								struct Formula_Result result;
+								Interpreter_anyExpression (me, index.string, & result);
+								if (result.expressionType == kFormula_EXPRESSION_TYPE_NUMERIC) {
+									double numericIndexValue = result.result.numericResult;
+									MelderString_append (& indexedVariableName, numericIndexValue);
+								} else if (result.expressionType == kFormula_EXPRESSION_TYPE_STRING) {
+									MelderString_append (& indexedVariableName, U"\"", result.result.stringResult, U"\"");
+									Melder_free (result.result.stringResult);
+								}
 								MelderString_appendCharacter (& indexedVariableName, *p);
 								if (*p == ']') {
 									break;
diff --git a/sys/abcio.cpp b/sys/abcio.cpp
index 722eff1..5fa06e0 100644
--- a/sys/abcio.cpp
+++ b/sys/abcio.cpp
@@ -492,9 +492,9 @@ void texputw4 (MelderFile file, const char32 *s, const char32 *s1, const char32
 	MelderFile_write (file, file -> verbose ? U" = \"" : U"\"");
 	if (s) {
 		char32 c;
-		while ((c = *s ++) != '\0') {
+		while ((c = *s ++) != U'\0') {
 			MelderFile_writeCharacter (file, c);
-			if (c == '\"') MelderFile_writeCharacter (file, c);   // double any internal quotes
+			if (c == U'\"') MelderFile_writeCharacter (file, c);   // double any internal quotes
 		}
 	}
 	MelderFile_write (file, file -> verbose ? U"\" " : U"\"");
@@ -756,7 +756,7 @@ int16 bingeti2LE (FILE *f) {
 	}
 }
 
-uint16_t bingetu2 (FILE *f) {
+uint16 bingetu2 (FILE *f) {
 	try {
 		if (binario_16bitBE && Melder_debug != 18) {
 			uint16 s;
@@ -774,7 +774,7 @@ uint16_t bingetu2 (FILE *f) {
 	}
 }
 
-uint16_t bingetu2LE (FILE *f) {
+uint16 bingetu2LE (FILE *f) {
 	try {
 		if (binario_16bitLE && Melder_debug != 18) {
 			uint16 s;
@@ -1261,7 +1261,7 @@ void binputr4 (double x, FILE *f) {
 			int sign, exponent;
 			double fMantissa, fsMantissa;
 			uint32 mantissa;
-			if (x < 0.0) { sign = 0x0100; x *= -1; }
+			if (x < 0.0) { sign = 0x0100; x *= -1.0; }
 			else sign = 0;
 			if (x == 0.0) { exponent = 0; mantissa = 0; }
 			else {
@@ -1277,7 +1277,7 @@ void binputr4 (double x, FILE *f) {
 					exponent |= sign;
 					fMantissa = ldexp (fMantissa, 24);          
 					fsMantissa = floor (fMantissa); 
-					mantissa = (uint32_t) fsMantissa & 0x007FFFFF;
+					mantissa = (uint32) fsMantissa & 0x007FFFFF;
 				}
 			}
 			bytes [0] = (uint8) (exponent >> 1);   // truncate: bits 2 through 9 (bit 9 is the sign bit)
@@ -1301,7 +1301,7 @@ void binputr4LE (double x, FILE *f) {
 			int sign, exponent;
 			double fMantissa, fsMantissa;
 			uint32 mantissa;
-			if (x < 0.0) { sign = 0x0100; x *= -1; }
+			if (x < 0.0) { sign = 0x0100; x *= -1.0; }
 			else sign = 0;
 			if (x == 0.0) { exponent = 0; mantissa = 0; }
 			else {
@@ -1317,7 +1317,7 @@ void binputr4LE (double x, FILE *f) {
 					exponent |= sign;
 					fMantissa = ldexp (fMantissa, 24);          
 					fsMantissa = floor (fMantissa); 
-					mantissa = (uint32_t) fsMantissa & 0x007FFFFF;
+					mantissa = (uint32) fsMantissa & 0x007FFFFF;
 				}
 			}
 			bytes [3] = (uint8) (exponent >> 1);
@@ -1340,7 +1340,7 @@ void binputr8 (double x, FILE *f) {
 			int sign, exponent;
 			double fMantissa, fsMantissa;
 			uint32 highMantissa, lowMantissa;
-			if (x < 0.0) { sign = 0x0800; x *= -1; }
+			if (x < 0.0) { sign = 0x0800; x *= -1.0; }
 			else sign = 0;
 			if (x == 0.0) { exponent = 0; highMantissa = 0; lowMantissa = 0; }
 			else {
@@ -1356,10 +1356,10 @@ void binputr8 (double x, FILE *f) {
 					exponent |= sign;
 					fMantissa = ldexp (fMantissa, 21);          
 					fsMantissa = floor (fMantissa); 
-					highMantissa = (uint32_t) fsMantissa & 0x000FFFFF;
+					highMantissa = (uint32) fsMantissa & 0x000FFFFF;
 					fMantissa = ldexp (fMantissa - fsMantissa, 32); 
 					fsMantissa = floor (fMantissa); 
-					lowMantissa = (uint32_t) fsMantissa;
+					lowMantissa = (uint32) fsMantissa;
 				}
 			}
 			bytes [0] = (uint8) (exponent >> 4);
@@ -1384,7 +1384,7 @@ void binputr10 (double x, FILE *f) {
 		int sign, exponent;   // these should be uint16_t, but frexp() expects an int
 		double fMantissa, fsMantissa;
 		uint32_t highMantissa, lowMantissa;
-		if (x < 0.0) { sign = 0x8000; x *= -1; }
+		if (x < 0.0) { sign = 0x8000; x *= -1.0; }
 		else sign = 0;
 		if (x == 0.0) { exponent = 0; highMantissa = 0; lowMantissa = 0; }
 		else {
@@ -1400,10 +1400,10 @@ void binputr10 (double x, FILE *f) {
 				exponent |= sign;
 				fMantissa = ldexp (fMantissa, 32);          
 				fsMantissa = floor (fMantissa); 
-				highMantissa = (uint32_t) fsMantissa;
+				highMantissa = (uint32) fsMantissa;
 				fMantissa = ldexp (fMantissa - fsMantissa, 32); 
 				fsMantissa = floor (fMantissa); 
-				lowMantissa = (uint32_t) fsMantissa;
+				lowMantissa = (uint32) fsMantissa;
 			}
 		}
 		bytes [0] = (uint8) (exponent >> 8);
@@ -1516,14 +1516,14 @@ char32 * bingetw1 (FILE *f) {
 			length = bingetu1 (f);
 			result.reset (Melder_malloc (char32, (int64) length + 1));
 			for (unsigned short i = 0; i < length; i ++) {
-				uint16 kar = bingetu2 (f);
-				if ((kar & 0xF800) == 0xD800) {
-					if (kar > 0xDBFF)
+				char32 kar = bingetu2 (f);
+				if ((kar & 0x00F800) == 0x00D800) {
+					if (kar > 0x00DBFF)
 						Melder_throw (U"Incorrect Unicode value (first surrogate member ", kar, U").");
-					uint16 kar2 = bingetu2 (f);
-					if (kar2 < 0xDC00 || kar2 > 0xDFFF)
+					char32 kar2 = bingetu2 (f);
+					if (kar2 < 0x00DC00 || kar2 > 0x00DFFF)
 						Melder_throw (U"Incorrect Unicode value (second surrogate member ", kar2, U").");
-					result [i] = (((kar & 0x3FF) << 10) | (kar2 & 0x3FF)) + 0x10000;
+					result [i] = (((kar & 0x0003FF) << 10) | (kar2 & 0x0003FF)) + 0x010000;
 				} else {
 					result [i] = kar;
 				}
@@ -1593,15 +1593,15 @@ char32 * bingetw4 (FILE *f) {
 			 */
 			length = bingetu4 (f);
 			result.reset (Melder_malloc (char32, (int64) length + 1));
-			for (uint32_t i = 0; i < length; i ++) {
-				uint16_t kar = bingetu2 (f);
-				if ((kar & 0xF800) == 0xD800) {
-					if (kar > 0xDBFF)
+			for (uint32 i = 0; i < length; i ++) {
+				char32 kar = bingetu2 (f);
+				if ((kar & 0x00F800) == 0x00D800) {
+					if (kar > 0x00DBFF)
 						Melder_throw (U"Incorrect Unicode value (first surrogate member ", kar, U").");
-					uint16_t kar2 = bingetu2 (f);
-					if (kar2 < 0xDC00 || kar2 > 0xDFFF)
+					char32 kar2 = bingetu2 (f);
+					if (kar2 < 0x00DC00 || kar2 > 0x00DFFF)
 						Melder_throw (U"Incorrect Unicode value (second surrogate member ", kar2, U").");
-					result [i] = (((kar & 0x3FF) << 10) | (kar2 & 0x3FF)) + 0x10000;
+					result [i] = (((kar & 0x0003FF) << 10) | (kar2 & 0x0003FF)) + 0x010000;
 				} else {
 					result [i] = kar;
 				}
@@ -1611,11 +1611,11 @@ char32 * bingetw4 (FILE *f) {
 			 * ASCII
 			 */
 			result.reset (Melder_malloc (char32, (int64) length + 1));
-			for (uint32_t i = 0; i < length; i ++) {
+			for (uint32 i = 0; i < length; i ++) {
 				result [i] = bingetu1 (f);
 			}
 		}
-		result [length] = L'\0';
+		result [length] = U'\0';
 		return result.transfer();
 	} catch (MelderError) {
 		Melder_throw (U"Text not read from a binary file.");
diff --git a/sys/praat_version.h b/sys/praat_version.h
index a77724e..1223757 100644
--- a/sys/praat_version.h
+++ b/sys/praat_version.h
@@ -1,5 +1,5 @@
-#define PRAAT_VERSION_STR 6.0.15
-#define PRAAT_VERSION_NUM 6015
+#define PRAAT_VERSION_STR 6.0.17
+#define PRAAT_VERSION_NUM 6017
 #define PRAAT_YEAR 2016
-#define PRAAT_MONTH March
-#define PRAAT_DAY 23
+#define PRAAT_MONTH April
+#define PRAAT_DAY 21

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/praat.git



More information about the debian-med-commit mailing list