[med-svn] [praat] 01/04: Imported Upstream version 5.4.4

Rafael Laboissière rlaboiss-guest at moszumanska.debian.org
Thu Jan 29 00:34:34 UTC 2015


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

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

commit ceb94040af5d6a9a4c712aeae79f309aefad7ea3
Author: Rafael Laboissiere <rafael at laboissiere.net>
Date:   Wed Jan 28 13:38:44 2015 -0200

    Imported Upstream version 5.4.4
---
 LPC/Cepstrogram.cpp                                |   2 +
 LPC/manual_LPC.cpp                                 |  35 +-
 LPC/praat_LPC_init.cpp                             |  11 +-
 dwsys/NUM2.cpp                                     |  25 +-
 dwsys/NUM2.h                                       |   7 +-
 dwtest/test_Discriminant.praat                     |  20 +-
 dwtools/ClassificationTable.cpp                    |  28 +-
 dwtools/ClassificationTable.h                      |   4 +-
 dwtools/Confusion.cpp                              |  22 +-
 dwtools/Confusion.h                                |   2 +
 dwtools/EEG_extensions.h                           |   2 +-
 dwtools/FilterBank.cpp                             | 654 +++++++++++++++++++-
 dwtools/FilterBank.h                               |  53 +-
 dwtools/KlattGrid.cpp                              |   4 +-
 dwtools/MFCC.cpp                                   |  10 +-
 dwtools/MFCC.h                                     |  18 +-
 dwtools/Makefile                                   |   8 +-
 dwtools/MelFilter_and_MFCC.cpp                     | 213 -------
 dwtools/MelFilter_and_MFCC.h                       |  62 --
 dwtools/Polygon_extensions.cpp                     |   2 +-
 dwtools/Sound_and_FilterBank.cpp                   | 421 -------------
 dwtools/Sound_and_Spectrogram_extensions.cpp       | 397 ++++++++++++
 ...erBank.h => Sound_and_Spectrogram_extensions.h} |  28 +-
 dwtools/Sound_extensions.cpp                       |  32 +-
 dwtools/Sound_to_MFCC.cpp                          |  11 +-
 dwtools/Sounds_to_DTW.h                            |   4 +-
 dwtools/Spectrogram_extensions.cpp                 | 639 +++++++++++++++++++
 dwtools/Spectrogram_extensions.h                   | 134 ++++
 dwtools/TableOfReal_extensions.cpp                 |  22 +-
 dwtools/TableOfReal_extensions.h                   |   4 +-
 dwtools/TextGrid_extensions.cpp                    |   6 +-
 dwtools/VowelEditor.cpp                            |  39 +-
 dwtools/VowelEditor.h                              |   9 +-
 dwtools/manual_dwtools.cpp                         | 213 +++++--
 dwtools/praat_David_init.cpp                       | 675 ++++++++++++++++++---
 external/espeak/speech.h                           |   2 +-
 external/flac/READ_ME.TXT                          |  15 +-
 external/flac/flac_FLAC_metadata.h                 |   3 -
 external/flac/flac_metadata_iterators.c            |   2 -
 external/flac/flac_stream_decoder.c                |   4 -
 external/flac/flac_stream_encoder.c                |   4 -
 fon/FunctionEditor.cpp                             |  12 +-
 fon/Sampled.cpp                                    |   6 +-
 fon/Sound.cpp                                      |  12 +-
 fon/SoundRecorder.cpp                              |   6 +-
 fon/Sound_to_Formant.cpp                           |   2 +-
 fon/Sound_to_Pitch.cpp                             |   2 +-
 fon/Spectrum.cpp                                   |  12 +-
 fon/TextGrid.cpp                                   |  22 +-
 fon/TextGridEditor.cpp                             |   2 +
 fon/TextGrid_def.h                                 |   4 +-
 fon/manual_Script.cpp                              |  41 +-
 fon/manual_tutorials.cpp                           |  19 +-
 makefiles/makefile.defs.linux.alsa                 |   4 +-
 makefiles/makefile.defs.linux.silent               |   4 +-
 makefiles/makefile.defs.linuxc.alsa                |   4 +-
 makefiles/makefile.defs.linuxs.alsa                |   4 +-
 makefiles/makefile.defs.mingw32                    |   4 +-
 makefiles/makefile.defs.mingw32-490                |   4 +-
 makefiles/makefile.defs.mingw32c                   |   4 +-
 makefiles/makefile.defs.mingw64                    |   4 +-
 makefiles/makefile.defs.mingw64-490                |   4 +-
 makefiles/makefile.defs.mingw64c                   |   4 +-
 num/NUMarrays.cpp                                  |  10 +-
 num/NUMrandom.cpp                                  |   9 +-
 sys/Collection.h                                   |   7 -
 sys/Data.cpp                                       |   2 +
 sys/Data.h                                         |   1 +
 sys/Formula.cpp                                    |  81 ++-
 sys/Graphics.cpp                                   |   6 +
 sys/GraphicsScreen.cpp                             |   9 +-
 sys/Graphics_image.cpp                             |   1 +
 sys/Graphics_linesAndAreas.cpp                     |  68 ++-
 sys/Graphics_text.cpp                              |  14 +-
 sys/GuiLabel.cpp                                   |   2 +-
 sys/GuiList.cpp                                    |   2 +-
 sys/GuiRadioButton.cpp                             |   4 +
 sys/GuiScrollBar.cpp                               |  34 +-
 sys/GuiText.cpp                                    |   8 +-
 sys/Interpreter.cpp                                |  18 +-
 sys/Strings.cpp                                    |   4 +-
 sys/TextEditor.cpp                                 |   4 +-
 sys/Thing.cpp                                      |   6 +-
 sys/UiPause.cpp                                    |   2 +-
 sys/abcio.cpp                                      |  62 +-
 sys/abcio.h                                        |   4 +-
 sys/melder.cpp                                     |   2 +-
 sys/melder.h                                       |  63 +-
 sys/melder_alloc.cpp                               |  93 +--
 sys/melder_audio.cpp                               |  50 +-
 sys/melder_audiofiles.cpp                          |  28 +-
 sys/melder_files.cpp                               |   6 +-
 sys/melder_ftoa.cpp                                |  12 +-
 sys/melder_info.cpp                                |  24 +-
 sys/melder_readtext.cpp                            | 116 ++--
 sys/melder_strings.cpp                             |   8 +-
 sys/melder_textencoding.cpp                        |  10 +-
 sys/praat.cpp                                      |  23 +-
 sys/praat_actions.cpp                              |   2 +-
 sys/praat_picture.cpp                              |   2 +-
 sys/praat_script.cpp                               |  20 +-
 sys/praat_statistics.cpp                           |   4 +-
 sys/praat_version.h                                |   8 +-
 test/fon/soundFiles.praat                          |   2 +-
 test/sys/script2.praat                             | Bin 3930 -> 3890 bytes
 105 files changed, 3490 insertions(+), 1367 deletions(-)

diff --git a/LPC/Cepstrogram.cpp b/LPC/Cepstrogram.cpp
index 4507640..309c469 100644
--- a/LPC/Cepstrogram.cpp
+++ b/LPC/Cepstrogram.cpp
@@ -258,6 +258,7 @@ PowerCepstrum PowerCepstrogram_to_PowerCepstrum_slice (PowerCepstrogram me, doub
 	}
 }
 
+PowerCepstrogram Matrix_to_PowerCepstrogram (Matrix me);
 PowerCepstrogram Matrix_to_PowerCepstrogram (Matrix me) {
 	try {
 		autoPowerCepstrogram thee = Thing_new (PowerCepstrogram);
@@ -315,6 +316,7 @@ PowerCepstrogram Sound_to_PowerCepstrogram (Sound me, double pitchFloor, double
 	}
 }
 
+Cepstrum Spectrum_to_Cepstrum_hillenbrand (Spectrum me);
 Cepstrum Spectrum_to_Cepstrum_hillenbrand (Spectrum me) {
 	try {
 		autoNUMfft_Table fftTable;
diff --git a/LPC/manual_LPC.cpp b/LPC/manual_LPC.cpp
index f88143e..d82ea75 100644
--- a/LPC/manual_LPC.cpp
+++ b/LPC/manual_LPC.cpp
@@ -475,20 +475,19 @@ TAG (L"##Use filter at time (s)")
 DEFINITION (L"determines which LPC frame will be chosen to inverse filter the sound. ")
 MAN_END
 
-MAN_BEGIN (L"MFCC", L"djmw", 20010411)
+MAN_BEGIN (L"MFCC", L"djmw", 20141022)
 INTRO (L"One of the @@types of objects@ in P\\s{RAAT}.")
 NORMAL (L"An object of type MFCC represents mel frequency cepstral coefficients "
 	"as a function of time. The coefficients are represented in frames "
-	"with constant sampling period.")
+	"at constant sampling period.")
 ENTRY (L"MFCC commands")
 NORMAL (L"Creation:")
 LIST_ITEM (L"\\bu @@Sound: To MFCC...@")
-LIST_ITEM (L"\\bu @@MelFilter: To MFCC...@")
+LIST_ITEM (L"\\bu @@MelSpectrogram: To MFCC...@")
 MAN_END
 
-MAN_BEGIN (L"MFCC: To MelFilter...", L"djmw", 20130221)
-INTRO (L"A command to reconstruct @MelFilter objects  from the selected @MFCC "
-	"objects .")
+MAN_BEGIN (L"MFCC: To MelFilter...", L"djmw", 20141023)
+INTRO (L"A #deprecated command replaced by @@MFCC: To MelSpectrogram... at .")
 ENTRY (L"Settings")
 TAG (L"##From coefficient#, ##To coefficient#")
 DEFINITION (L"the range of coefficients that will be used in the reconstruction.")
@@ -500,6 +499,20 @@ NORMAL (L"where %N represents the number of filters, %j runs from 1 to %N, and c
 	"%%fromCoefficient% and %k larger than %%toCoefficient% take zero values in the evaluation.")
 MAN_END
 
+MAN_BEGIN (L"MFCC: To MelSpectrogram...", L"djmw", 20141023)
+INTRO (L"A command to (re)construct a @MelSpectrogram object from the selected @MFCC object.")
+ENTRY (L"Settings")
+TAG (L"##From coefficient#, ##To coefficient#")
+DEFINITION (L"the range of coefficients that will be used in the reconstruction.")
+TAG (L"##Include constant term")
+DEFINITION (L"selects whether or not to include the %c__0_ coefficient in the reconstruction. As can be seen from the formula below, the contribution of the %c__0_ term is equal for each filter.")
+ENTRY (L"Details")
+NORMAL (L"The values %P__%j_ in each frame of the MelSpectrogram will be constructed by applying the inverse Discrete Cosine Transform to the corresponding frame of the MFCC object:")
+FORMULA (L"%P__%j_ = 2/N (%c__0_/2 + \\Si__%k=1_^^%N-1^ %c__%k_ cos (\\pi%k(%j-0.5)/%N))),")
+NORMAL (L"where %N represents the number of filters that were used to get the MFCC object, %j runs from 1 to %N, and coefficients %c__%k_ with %k less than "
+	"%%fromCoefficient% and %k larger than %%toCoefficient% take zero values in the evaluation.")
+MAN_END
+
 MAN_BEGIN (L"Sound: To PowerCepstrogram...", L"djmw", 20130616)
 INTRO (L"A command that creates a @@PowerCepstrogram@ from every selected @@Sound at .")
 ENTRY (L"Settings")
@@ -603,14 +616,14 @@ ENTRY (L"Algorithm")
 NORMAL (L"Burg's algorithm is described in @@Anderson (1978)@")
 MAN_END
 
-MAN_BEGIN (L"Sound: To MFCC...", L"djmw", 20010410)
+MAN_BEGIN (L"Sound: To MFCC...", L"djmw", 20141022)
 INTRO (L"A command that creates a @MFCC object from every selected @Sound "
 	"object.")
 NORMAL (L"The analysis proceeds in two steps:")
-LIST_ITEM (L"1.  We perform a filter bank analysis on a mel frequency scale "
-	"(see @@Sound: To MelFilter...@ for details).")
-LIST_ITEM (L"2.  We convert the filter values to mel frequency cepstral "
-	"coefficients (see @@MelFilter: To MFCC...@ for details).")
+LIST_ITEM (L"1.  We perform a spectrum analysis on a mel frequency scale "
+	"(see @@Sound: To MelSpectrogram...@ for details).")
+LIST_ITEM (L"2.  We convert the melspectrogram values to mel frequency cepstral "
+	"coefficients (see @@MelSpectrogram: To MFCC...@ for details).")
 MAN_END
 
 MAN_BEGIN (L"VocalTractTier", L"djmw", 20120423)
diff --git a/LPC/praat_LPC_init.cpp b/LPC/praat_LPC_init.cpp
index 643dabd..b412288 100644
--- a/LPC/praat_LPC_init.cpp
+++ b/LPC/praat_LPC_init.cpp
@@ -33,6 +33,7 @@
 #include "Cepstrogram.h"
 #include "Cepstrum_and_Spectrum.h"
 #include "DTW.h"
+#include "FilterBank.h"
 #include "Formant_extensions.h"
 #include "LPC.h"
 #include "MFCC.h"
@@ -45,7 +46,6 @@
 #include "LPC_to_Spectrogram.h"
 #include "LPC_to_Spectrum.h"
 #include "NUM2.h"
-#include "MelFilter_and_MFCC.h"
 #include "praatP.h"
 #include "Sound_and_LPC.h"
 #include "Sound_and_LPC_robust.h"
@@ -58,6 +58,7 @@
 
 static const wchar_t *DRAW_BUTTON    = L"Draw -";
 static const wchar_t *QUERY_BUTTON   = L"Query -";
+static const wchar_t *MODIFY_BUTTON   = L"Modify -";
 
 void praat_CC_init (ClassInfo klas);
 void praat_TimeFrameSampled_query_init (ClassInfo klas);
@@ -1273,9 +1274,11 @@ void praat_uvafon_LPC_init (void) {
 	praat_addAction1 (classLPC, 0, L"Draw gain...", 0, 1, DO_LPC_drawGain);
 	praat_addAction1 (classLPC, 0, L"Draw poles...", 0, 1, DO_LPC_drawPoles);
 	praat_addAction1 (classLPC, 0, QUERY_BUTTON, 0, 0, 0);
-	praat_TimeFrameSampled_query_init (classLPC);
-	praat_addAction1 (classLPC, 1, L"Get sampling interval", 0, 1, DO_LPC_getSamplingInterval);
-	praat_addAction1 (classLPC, 1, L"Get number of coefficients...", 0, 1, DO_LPC_getNumberOfCoefficients);
+		praat_TimeFrameSampled_query_init (classLPC);
+		praat_addAction1 (classLPC, 1, L"Get sampling interval", 0, 1, DO_LPC_getSamplingInterval);
+		praat_addAction1 (classLPC, 1, L"Get number of coefficients...", 0, 1, DO_LPC_getNumberOfCoefficients);
+	praat_addAction1 (classLPC, 0, MODIFY_BUTTON, 0, 0, 0);
+		praat_TimeFunction_modify_init (classLPC);
 	praat_addAction1 (classLPC, 0, L"Extract", 0, 0, 0);
 
 	praat_addAction1 (classLPC, 0, L"To Spectrum (slice)...", 0, 0, DO_LPC_to_Spectrum);
diff --git a/dwsys/NUM2.cpp b/dwsys/NUM2.cpp
index 9fe1121..2091c0e 100644
--- a/dwsys/NUM2.cpp
+++ b/dwsys/NUM2.cpp
@@ -2147,8 +2147,7 @@ int NUMburg (double x[], long n, double a[], int m, double *xms) {
 	return 1;
 }
 
-void NUMdmatrix_to_dBs (double **m, long rb, long re, long cb, long ce,
-                        double ref, double factor, double floor) {
+void NUMdmatrix_to_dBs (double **m, long rb, long re, long cb, long ce, double ref, double factor, double floor) {
 	double ref_db, factor10 = factor * 10;
 	double max = m[rb][cb], min = max;
 
@@ -2164,9 +2163,9 @@ void NUMdmatrix_to_dBs (double **m, long rb, long re, long cb, long ce,
 		}
 	}
 
-	if (max < 0 || min < 0) Melder_throw ("NUMdmatrix_to_dBs: all "
-		                                      "matrix elements must be positive.");
-
+	if (max < 0 || min < 0) {
+		Melder_throw ("NUMdmatrix_to_dBs: all matrix elements must be positive.");
+	}
 	ref_db = factor10 * log10 (ref);
 
 	for (long i = rb; i <= re; i++) {
@@ -2361,7 +2360,7 @@ int NUMgetIntersectionsWithRectangle (double x1, double y1, double x2, double y2
 }
 
 
-int NUMclipLineWithinRectangle (double xl1, double yl1, double xl2, double yl2, double xr1, double yr1,
+bool NUMclipLineWithinRectangle (double xl1, double yl1, double xl2, double yl2, double xr1, double yr1,
                                 double xr2, double yr2, double *xo1, double *yo1, double *xo2, double *yo2) {
 	int ncrossings = 0;
 	bool xswap, yswap;
@@ -2373,13 +2372,13 @@ int NUMclipLineWithinRectangle (double xl1, double yl1, double xl2, double yl2,
 	// within the rectangle
 	if (xl1 >= xr1 && xl1 <= xr2 && yl1 >= yr1 && yl1 <= yr2 &&
 	        xl2 >= xr1 && xl2 <= xr2 && yl2 >= yr1 && yl2 <= yr2) {
-		return 1;
+		return true;
 	}
 
 	// All lines that are completely outside the rectangle
 	if ( (xl1 <= xr1 && xl2 <= xr1) || (xl1 >= xr2 && xl2 >= xr2) ||
 	        (yl1 <= yr1 && yl2 <= yr1) || (yl1 >= yr2 && yl2 >= yr2)) {
-		return 0;
+		return false;
 	}
 
 	// At least line spans (part of) the rectangle.
@@ -2405,7 +2404,7 @@ int NUMclipLineWithinRectangle (double xl1, double yl1, double xl2, double yl2,
 		if (xswap) {
 			t = *xo1; *xo1 = *xo2; *xo2 = t;
 		}
-		return 1;
+		return true;
 	}
 	if (vline) {
 		if (ymin < yr1) {
@@ -2417,7 +2416,7 @@ int NUMclipLineWithinRectangle (double xl1, double yl1, double xl2, double yl2,
 		if (yswap) {
 			t = *yo1; *yo1 = *yo2; *yo2 = t;
 		}
-		return 1;
+		return true;
 	}
 
 	// Now we know that the line from (x1,y1) to (x2,y2) is neither horizontal nor vertical.
@@ -2474,7 +2473,7 @@ int NUMclipLineWithinRectangle (double xl1, double yl1, double xl2, double yl2,
 		}
 	}
 	if (ncrossings == 0) {
-		return 0;
+		return false;
 	}
 	if (ncrossings == 1 || ncrossings == 2) {
 		// if start and endpoint of line are outside rectangle and ncrossings == 1,
@@ -2482,7 +2481,7 @@ int NUMclipLineWithinRectangle (double xl1, double yl1, double xl2, double yl2,
 		if (ncrossings == 1 &&
 		        (xl1 < xr1 || xl1 > xr2 || yl1 < yr1 || yl1 > yr2) &&
 		        (xl2 < xr1 || xl2 > xr2 || yl2 < yr1 || yl2 > yr2)) {
-			return 0;
+			return true;
 		}
 
 		if ( (xc[1] > xc[2] && ! xswap) || (xc[1] < xc[2] && xswap)) {
@@ -2493,7 +2492,7 @@ int NUMclipLineWithinRectangle (double xl1, double yl1, double xl2, double yl2,
 	} else {
 		Melder_throw ("Too many crossings found.");
 	}
-	return 1;
+	return true;
 }
 
 void NUMgetEllipseBoundingBox (double a, double b, double cospsi, double *width, double *height) {
diff --git a/dwsys/NUM2.h b/dwsys/NUM2.h
index ffc8cad..9387706 100644
--- a/dwsys/NUM2.h
+++ b/dwsys/NUM2.h
@@ -946,6 +946,7 @@ double NUMformantfilter_amplitude (double fc, double bw, double f);
 
 	H(f) = 1.0 / (dq * dq + 1.0), where
 		dq = (fc * fc - f * f) / (bw * f)
+	Preconditions: f > 0 && bw > 0
 */
 
 int NUMburg (double x[], long n, double a[], int m, double *xms);
@@ -1030,12 +1031,12 @@ int NUMgetIntersectionsWithRectangle (double x1, double y1, double x2, double y2
 	The returned value is the number of intersections found and is either 0 or 1 or 2.
 */
 
-int NUMclipLineWithinRectangle (double xl1, double yl1, double xl2, double yl2, double xr1, double yr1,
+bool NUMclipLineWithinRectangle (double xl1, double yl1, double xl2, double yl2, double xr1, double yr1,
 	double xr2, double yr2, double *xo1, double *yo1, double *xo2, double *yo2);
 /*
-	Returns in (xo1, yo1) and (xo2, yo2) the coordinates of that piece of the line (xl1, yl1)..(xl2, yl2)
+	If true, then returns in (xo1, yo1) and (xo2, yo2) the coordinates of that piece of the line (xl1, yl1)..(xl2, yl2)
 	that can be drawn within the rectangle with lowerleft corner (xr1, yr1) and upperright (xr2, yr2).
-	Returns 0 if there is nothing to be drawn inside.
+	Returns false if there is nothing to be drawn inside.
 */
 
 void NUMgetEllipseBoundingBox (double a, double b, double cospsi,
diff --git a/dwtest/test_Discriminant.praat b/dwtest/test_Discriminant.praat
index e82ca2b..6277964 100644
--- a/dwtest/test_Discriminant.praat
+++ b/dwtest/test_Discriminant.praat
@@ -1,22 +1,18 @@
 # test_Discriminant.praat
-# djmw 20110518
+# djmw 20110518, 20141030
 
-printline test_Discriminant
+appendInfoLine: "test_Discriminant"
 
-t = Create TableOfReal (Pols 1973)... no
-Formula... log10(self)
+t = Create TableOfReal (Pols 1973): "no"
+Formula: "log10(self)"
 
 dis = To Discriminant
 plus t
-clas = To ClassificationTable... yes yes
-conf = To Confusion
+clas = To ClassificationTable: "yes", "yes"
+conf = To Confusion: "no"
 fc = Get fraction correct
 assert fc -0.74 < 0.00001
 
-select t
-plus dis
-plus clas
-plus conf
-Remove
+removeObject: t, dis, clas, conf
 
-printline test_Discriminant OK
+appendInfoLine: "test_Discriminant OK"
diff --git a/dwtools/ClassificationTable.cpp b/dwtools/ClassificationTable.cpp
index 1dc3332..6d08293 100644
--- a/dwtools/ClassificationTable.cpp
+++ b/dwtools/ClassificationTable.cpp
@@ -1,6 +1,6 @@
 /* ClassificationTable.cpp
  *
- * Copyright (C) 1993-2011 David Weenink
+ * Copyright (C) 1993-2011, 2014 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
@@ -28,32 +28,46 @@
 */
 
 #include "ClassificationTable.h"
+#include "Distributions_and_Strings.h"
 #include "Strings_extensions.h"
 #include "NUM2.h"
 
 Thing_implement (ClassificationTable, TableOfReal, 0);
 
-ClassificationTable ClassificationTable_create (long numberOfRows, long numberOfGroups) {
+ClassificationTable ClassificationTable_create (long numberOfRows, long numberOfClasses) {
 	try {
 		autoClassificationTable me = Thing_new (ClassificationTable);
-		TableOfReal_init (me.peek(), numberOfRows, numberOfGroups);
+		TableOfReal_init (me.peek(), numberOfRows, numberOfClasses );
 		return me.transfer();
 	} catch (MelderError) {
 		Melder_throw ("ClassificationTable not created.");
 	}
 }
 
-Confusion ClassificationTable_to_Confusion (ClassificationTable me) {
+Confusion ClassificationTable_to_Confusion (ClassificationTable me, int onlyClassLabels) {
 	try {
-		autoCategories c1 = TableOfReal_to_CategoriesRow (me);
-		autoCategories c2 = ClassificationTable_to_Categories_maximumProbability (me);
-		autoConfusion thee = Categories_to_Confusion (c1.peek(), c2.peek());
+		autoStrings responses = TableOfReal_extractColumnLabelsAsStrings (me);
+		autoStrings s2 = TableOfReal_extractRowLabelsAsStrings (me);
+		autoDistributions d2 = Strings_to_Distributions (s2.peek());
+		autoStrings stimuli = TableOfReal_extractRowLabelsAsStrings (d2.peek());
+		autoConfusion thee = Confusion_createFromStringses ((onlyClassLabels ? responses.peek() : stimuli.peek()), responses.peek());
+		Confusion_and_ClassificationTable_increase (thee.peek(), me);
 		return thee.transfer();
 	} catch (MelderError) {
 		Melder_throw (me, ": confusions cannot be calculated.");
 	}
 }
 
+void Confusion_and_ClassificationTable_increase (Confusion me, ClassificationTable thee) {
+	if (my numberOfColumns != thy numberOfColumns) {
+		Melder_throw ("The number of columns must be equal.");
+	}
+	for (long irow = 1; irow <= thy numberOfRows; irow++) {
+		long index = TableOfReal_getColumnIndexAtMaximumInRow (thee, irow);
+		Confusion_increase (me, thy rowLabels[irow], my columnLabels[index]);
+	}
+}
+
 Strings ClassificationTable_to_Strings_maximumProbability (ClassificationTable me) {
 	try {
 		autoStrings thee = Strings_createFixedLength (my numberOfRows);
diff --git a/dwtools/ClassificationTable.h b/dwtools/ClassificationTable.h
index ad14acd..be25084 100644
--- a/dwtools/ClassificationTable.h
+++ b/dwtools/ClassificationTable.h
@@ -34,10 +34,12 @@ ClassificationTable ClassificationTable_create (long numberOfRows, long numberOf
 Categories ClassificationTable_to_Categories_maximumProbability (ClassificationTable me);
 Strings ClassificationTable_to_Strings_maximumProbability (ClassificationTable me);
 
-Confusion ClassificationTable_to_Confusion (ClassificationTable me);
+Confusion ClassificationTable_to_Confusion (ClassificationTable me, int onlyClassLabels);
 
 /* Correlations between the classes (columns) */
 Correlation ClassificationTable_to_Correlation_columns (ClassificationTable me);
 
+void Confusion_and_ClassificationTable_increase (Confusion me, ClassificationTable thee);
+
 #endif /* _ClassificationTable_h_ */
 
diff --git a/dwtools/Confusion.cpp b/dwtools/Confusion.cpp
index a6a01eb..1cfeab9 100644
--- a/dwtools/Confusion.cpp
+++ b/dwtools/Confusion.cpp
@@ -1,6 +1,6 @@
 /* Confusion.cpp
  *
- * Copyright (C) 1993-2011 David Weenink
+ * Copyright (C) 1993-2011, 2014 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
@@ -60,6 +60,26 @@ void structConfusion :: v_info () {
 	MelderInfo_writeLine (L" Fraction correct: ", Melder_double (frac));
 }
 
+Confusion Confusion_createFromStringses (Strings me, Strings thee) {
+	try {
+		if (my numberOfStrings < 1 || thy numberOfStrings < 1) {
+			Melder_throw ("Empty Strings.");
+		}
+		autoConfusion him = Confusion_create (my numberOfStrings, thy numberOfStrings);
+		for (long irow = 1; irow <= my numberOfStrings; irow++) {
+			const wchar_t *label = my strings[irow];
+			TableOfReal_setRowLabel (him.peek(), irow, label);
+		}
+		for (long icol = 1; icol <= thy numberOfStrings; icol++) {
+			const wchar_t *label = thy strings[icol];
+			TableOfReal_setColumnLabel (him.peek(), icol, label);
+		}
+		return him.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": could not create Confusion with ", thee);
+	}
+}
+
 Confusion Confusion_create (long numberOfStimuli, long numberOfResponses) {
 	try {
 		autoConfusion me = Thing_new (Confusion);
diff --git a/dwtools/Confusion.h b/dwtools/Confusion.h
index 08f6833..3068e57 100644
--- a/dwtools/Confusion.h
+++ b/dwtools/Confusion.h
@@ -37,6 +37,8 @@ Confusion Confusion_create (long numberOfStimuli, long numberOfResponses);
 
 Confusion Confusion_createSimple (const wchar *labels);
 
+Confusion Confusion_createFromStringses (Strings stimulusLabels, Strings responseLabels);
+
 Confusion Categories_to_Confusion (Categories me, Categories thee);
 
 void Confusion_increase (Confusion me, const wchar_t *stimulus, const wchar_t *response);
diff --git a/dwtools/EEG_extensions.h b/dwtools/EEG_extensions.h
index 6e98470..07fd358 100644
--- a/dwtools/EEG_extensions.h
+++ b/dwtools/EEG_extensions.h
@@ -1,4 +1,4 @@
-#ifndef __extensions_h_
+#ifndef _EEG_extensions_h_
 #define _EEG_extensions_h_
 /* EEG_extensions.h
  *
diff --git a/dwtools/FilterBank.cpp b/dwtools/FilterBank.cpp
index fa6e389..1902606 100644
--- a/dwtools/FilterBank.cpp
+++ b/dwtools/FilterBank.cpp
@@ -1,6 +1,6 @@
 /* FilterBank.cpp
  *
- * Copyright (C) 1993-2012 David Weenink
+ * Copyright (C) 1993-2012, 2014 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
@@ -31,8 +31,14 @@
 #include "Eigen_and_Matrix.h"
 #include "FilterBank.h"
 #include "Matrix_extensions.h"
+#include "Sound_and_Spectrum.h"
+#include "Sound_extensions.h"
+#include "Sound_to_Pitch.h"
 #include "NUM2.h"
 
+#define MAX(m,n) ((m) > (n) ? (m) : (n))
+#define MIN(m,n) ((m) < (n) ? (m) : (n))
+
 static double scaleFrequency (double f, int scale_from, int scale_to) {
 	double fhz = NUMundefined;
 
@@ -74,7 +80,6 @@ static wchar_t const *GetFreqScaleText (int scale) {
 		return mel;
 	}
 	return error;
-
 }
 
 static int checkLimits (I, int fromFreqScale, int toFreqScale, int *fromFilter,
@@ -251,6 +256,42 @@ void FilterBank_drawFrequencyScales (I, Graphics g, int horizontalScale, double
 	}
 }
 
+void FilterBank_paint (FilterBank me, Graphics g, double xmin, double xmax, double ymin, double ymax, double minimum, double maximum, int garnish) {
+	if (xmax <= xmin) {
+		xmin = my xmin; xmax = my xmax; 
+	}
+	if (ymax <= ymin) {
+		ymin = my ymin; ymax = my ymax;
+	}
+	long ixmin, ixmax, iymin, iymax;
+	(void) Matrix_getWindowSamplesX (me, xmin - 0.49999 * my dx, xmax + 0.49999 * my dx, &ixmin, &ixmax);
+	(void) Matrix_getWindowSamplesY (me, ymin - 0.49999 * my dy, ymax + 0.49999 * my dy, &iymin, &iymax);
+	if (maximum <= minimum) {
+		(void) Matrix_getWindowExtrema (me, ixmin, ixmax, iymin, iymax, &minimum, &maximum);
+	}
+	if (maximum <= minimum) { 
+		minimum -= 1.0; maximum += 1.0;
+	}
+	if (xmin >= xmax || ymin >= ymax) {
+		return;
+	}
+	Graphics_setInner (g);
+	Graphics_setWindow (g, xmin, xmax, ymin, ymax);
+	Graphics_image (g, my z,
+			ixmin, ixmax, Sampled_indexToX   (me, ixmin - 0.5), Sampled_indexToX   (me, ixmax + 0.5),
+			iymin, iymax, SampledXY_indexToY (me, iymin - 0.5), SampledXY_indexToY (me, iymax + 0.5),
+			minimum, maximum);
+
+	Graphics_unsetInner (g);
+	if (garnish) {
+		Graphics_drawInnerBox (g);
+		Graphics_marksLeft (g, 2, 1, 1, 0);
+		Graphics_textLeft (g, 1, GetFreqScaleText (my v_getFrequencyScale ()));
+		Graphics_marksBottom (g, 2, 1, 1, 0);
+		Graphics_textBottom (g, 1, L"Time (s)");
+	}
+}
+
 void BarkFilter_drawSekeyHansonFilterFunctions (BarkFilter me, Graphics g,
         int toFreqScale, int fromFilter, int toFilter, double zmin, double zmax,
         int dbScale, double ymin, double ymax, int garnish) {
@@ -644,4 +685,613 @@ void FilterBank_and_PCA_drawComponent (I, PCA thee, Graphics g, long component,
 	Matrix_drawRows (fcopy.peek(), g, tmin, tmax, component - 0.5, component + 0.5, fmin, fmax);
 }
 
+// Convert old types to new types
+
+MelSpectrogram MelFilter_to_MelSpectrogram (MelFilter me) {
+	try {
+		autoMelSpectrogram thee = MelSpectrogram_create (my xmin, my xmax, my nx, my dx, my x1, my ymin, my ymax, my ny, my dy, my y1);
+		for (long i = 1; i <= my ny; i++) {
+			for (long j = 1; j <= my nx; j++) {
+				thy z[i][j] = 4e-10 * pow (10, my z[i][j] / 10);
+			}
+		}
+		return thee.transfer ();
+	} catch (MelderError) {
+		Melder_throw ("MelSpectrogram not created.");
+	}
+}
+
+BarkSpectrogram BarkFilter_to_BarkSpectrogram (BarkFilter me) {
+	try {
+		autoBarkSpectrogram thee = BarkSpectrogram_create (my xmin, my xmax, my nx, my dx, my x1, my ymin, my ymax, my ny, my dy, my y1);
+		for (long i = 1; i <= my ny; i++) {
+			for (long j = 1; j <= my nx; j++) {
+				thy z[i][j] = 4e-10 * pow (10, my z[i][j] / 10);
+			}
+		}
+		return thee.transfer ();
+	} catch (MelderError) {
+		Melder_throw ("BarkSpectrogram not created.");
+	}
+}
+
+Spectrogram FormantFilter_to_Spectrogram (FormantFilter me) {
+	try {
+		autoSpectrogram thee = Spectrogram_create (my xmin, my xmax, my nx, my dx, my x1, my ymin, my ymax, my ny, my dy, my y1);
+		for (long i = 1; i <= my ny; i++) {
+			for (long j = 1; j <= my nx; j++) {
+				thy z[i][j] = 4e-10 * pow (10, my z[i][j] / 10);
+			}
+		}
+		return thee.transfer ();
+	} catch (MelderError) {
+		Melder_throw ("Spectrogram not created.");
+	}
+}
+
+	/* MelFilter_and_MFCC.cpp */
+	
+static double **NUMcosinesTable (long  n) {
+	autoNUMmatrix<double> costab (1, n, 1, n);
+	for (long k = 1; k <= n; k++) {
+		for (long j = 1; j <= n; j++) {
+			costab[k][j] = cos (NUMpi * (k - 1) * (j - 0.5) / n);
+		}
+	}
+	return costab.transfer();
+}
+
+// x[1..n] : input, y[1..n] : output
+static void NUMcosineTransform (double *x, double *y, long n, double **cosinesTable) {
+	for (long k = 1; k <= n; k++) {
+		y[k] = 0;
+		for (long j = 1; j <= n; j++) {
+			y[k] += x[j] * cosinesTable[k][j];
+		}
+	}
+}
+
+// x: input, y: output
+static void NUMinverseCosineTransform (double *x, double *y, long n, double **cosinesTable) {
+	for (long j = 1; j <= n; j++) {
+		y[j] = 0.5 * x[1]; // * cosinesTable[1][j];
+		for (long k = 2; k <= n; k++) {
+			y[j] += x[k] * cosinesTable[k][j];
+		}
+		y[j] *= 2.0 / n;
+	}
+}
+
+double testCosineTransform (long n) {
+	try {
+		autoNUMvector<double> x (1, n);
+		autoNUMvector<double> y (1, n);
+		autoNUMvector<double> x2 (1, n);
+		autoNUMmatrix<double> cosinesTable (NUMcosinesTable (n), 1, 1);
+		for (long i = 1 ; i <= n; i++) {
+			x[i] = NUMrandomUniform (0, 70);
+		}
+		NUMcosineTransform (x.peek(), y.peek(), n, cosinesTable.peek());
+		NUMinverseCosineTransform (y.peek(), x2.peek(), n, cosinesTable.peek());
+		double delta = 0;
+		for (long i =1 ; i <= n; i++) {
+			double dif = x[i] - x2[i];
+			delta += dif * dif;
+		}
+		delta = sqrt (delta);
+		return delta;
+	} catch (MelderError) {
+		Melder_throw ("Test cosine transform error");
+	}
+}
+
+MFCC MelFilter_to_MFCC (MelFilter me, long numberOfCoefficients) {
+	try {
+		autoNUMmatrix<double> cosinesTable (NUMcosinesTable (my ny), 1, 1);
+		autoNUMvector<double> x (1, my ny);
+		autoNUMvector<double> y (1, my ny);
+		
+		double fmax_mel = my y1 + (my ny - 1) * my dy;
+		numberOfCoefficients = numberOfCoefficients > my ny - 1 ? my ny - 1 : numberOfCoefficients;
+		Melder_assert (numberOfCoefficients > 0);
+		// 20130220 new interpretation of maximumNumberOfCoefficients necessary for inverse transform 
+		autoMFCC thee = MFCC_create (my xmin, my xmax, my nx, my dx, my x1, my ny - 1, my ymin, my ymax);
+		for (long frame = 1; frame <= my nx; frame++) {
+			CC_Frame cf = (CC_Frame) & thy frame[frame];
+			for (long i = 1; i <= my ny; i++) {
+				x[i] = my z[i][frame];
+			}
+			NUMcosineTransform (x.peek(), y.peek(), my ny, cosinesTable.peek());
+			CC_Frame_init (cf, numberOfCoefficients);
+			for (long i = 1; i <= numberOfCoefficients; i++) {
+				cf -> c[i] = y[i + 1];
+			}
+			cf -> c0 = y[1];
+		}
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": no MFCC created.");
+	}
+}
+
+MelFilter MFCC_to_MelFilter (MFCC me, long first, long last) {
+	try {
+		long nf = my maximumNumberOfCoefficients + 1;
+		autoNUMmatrix<double> cosinesTable (NUMcosinesTable (nf), 1, 1);
+		autoNUMvector<double> x (1, nf);
+		autoNUMvector<double> y (1, nf);
+
+		if (first >= last) {
+			first = 0; last = nf - 1;
+		}
+
+		if (first < 0 || last > nf - 1) {
+			Melder_throw ("MFCC_to_MelFilter: coefficients must be in interval [0,", my maximumNumberOfCoefficients, "].");
+		}
+		double df = (my fmax - my fmin) / (nf + 1);
+		autoMelFilter thee = MelFilter_create (my xmin, my xmax, my nx, my dx, my x1, my fmin, my fmax, nf, df, df);
+
+		for (long frame = 1; frame <= my nx; frame++) {
+			CC_Frame cf = (CC_Frame) & my frame[frame];
+			long iend = MIN (last, cf -> numberOfCoefficients);
+			x[1] = first == 0 ? cf -> c0 : 0;
+			for (long i = 1; i <= my maximumNumberOfCoefficients; i++) {
+				x[i + 1] = i < first || i > iend ? 0 : cf -> c[i];
+			}
+			NUMinverseCosineTransform (x.peek(), y.peek(), nf, cosinesTable.peek());
+			for (long i = 1; i <= nf; i++) {
+				thy z[i][frame] = y[i];
+			}
+		}
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": no MelFilter created.");
+	}
+}
+
+
+MelFilter MFCC_to_MelFilter2 (MFCC me, long first_cc, long last_cc, double f1_mel, double df_mel) {
+	try {
+		int use_c0 = 0;
+		long nf = ((my fmax - my fmin) / df_mel + 0.5);
+		double fmin = MAX (f1_mel - df_mel, 0), fmax = f1_mel + (nf + 1) * df_mel;
+
+		if (nf < 1) {
+			Melder_throw ("MFCC_to_MelFilter: the position of the first filter, the distance between the filters, "
+			"and, the maximum do not result in a positive number of filters.");
+		}
+
+		// Default values
+
+		if (first_cc == 0) {
+			first_cc = 1;
+			use_c0 = 1;
+		}
+		if (last_cc == 0) {
+			last_cc = my maximumNumberOfCoefficients;
+		}
+
+		// Be strict
+
+		if (last_cc < first_cc || first_cc < 1 || last_cc > my maximumNumberOfCoefficients) {
+			Melder_throw ("MFCC_to_MelFilter: coefficients must be in interval [1,", my maximumNumberOfCoefficients, "].");
+		}
+		autoNUMmatrix<double> dct (NUMcosinesTable (first_cc, last_cc, nf), first_cc, 1); // TODO ??
+		//if ((dct = NUMcosinesTable (first_cc, last_cc, nf)) == NULL) return NULL;
+
+		autoMelFilter thee = MelFilter_create (my xmin, my xmax, my nx, my dx, my x1, fmin, fmax, nf, df_mel, f1_mel);
+
+		for (long frame = 1; frame <= my nx; frame++) {
+			CC_Frame cf = (CC_Frame) & my frame[frame];
+			long ie = MIN (last_cc, cf -> numberOfCoefficients);
+			for (long j = 1; j <= nf; j++) {
+				double t = 0;
+				for (long i = first_cc; i <= ie; i++) {
+					t += cf -> c[i] * dct[i][j];
+				}
+
+				// The inverse CT has a factor 1/N
+
+				t /= nf;
+				if (use_c0) {
+					t +=  cf -> c0;
+				}
+				thy z[j][frame] = t;
+			}
+		}
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": no MelFilter created.");
+	}
+}
+
+/* Sound_and_FilterBank.cpp */
+
+// prototypes
+Sound FilterBank_as_Sound (FilterBank me);
+
+/*
+	The gaussian(x) = (exp(-48*((i-(n+1)/2)/(n+1))^2)-exp(-12))/(1-exp(-12));
+	For power we need the area under the square of this window:
+		Integrate (gaussian(i)^2,i=1..n) =
+
+	(sqrt(Pi)*sqrt(3)*sqrt(2)*erf(2*(n-1)*sqrt(3)*sqrt(2)/(n+1))*(n+1)+
+		24*exp(-24)*(n-1)+
+	-4*sqrt(Pi)*sqrt(3)*exp(-12)*erf(2*(n-1)*sqrt(3)/(n+1))*(n+1))/
+	(24 * (-1+exp(-12))^2)
+	To compare with the rectangular window we need to divide this by the
+	window width (n-1) x 1^2.
+*/
+static double gaussian_window_squared_correction (long n) {
+	double e12 = exp (-12), denum = (e12 - 1) * (e12 - 1) * 24 * (n - 1);
+	double sqrt3 = sqrt (3), sqrt2 = sqrt (2), sqrtpi = sqrt (NUMpi);
+	double arg1 = 2 * sqrt3 * (n - 1) / (n + 1), arg2 = arg1 * sqrt2;
+	double p2 = sqrtpi * sqrt3 * sqrt2 * (1 - NUMerfcc (arg2)) * (n + 1);
+	double p1 = 4 * sqrtpi * sqrt3 * e12 * (1 - NUMerfcc (arg1)) * (n + 1);
+
+	return (p2 - p1 + 24 * (n - 1) * e12 * e12) / denum;
+}
+
+static Matrix Sound_to_spectralpower (Sound me) {
+	try {
+		autoSpectrum s = Sound_to_Spectrum (me, TRUE);
+		autoMatrix thee = Matrix_create (s -> xmin, s -> xmax, s -> nx, s -> dx, s -> x1, 1, 1, 1, 1, 1);
+		double scale = 2.0 * s -> dx / (my xmax - my xmin);
+
+		// factor '2' because of positive and negative frequencies
+		// s -> dx : width of frequency bin
+		// my xmax - my xmin : duration of sound
+
+		double *z = thy z[1], *re = s -> z[1], *im = s -> z[2];
+		for (long i = 1; i <= s -> nx; i++) {
+			z[i] = scale * (re[i] * re[i] + im[i] * im [i]);
+		}
+
+		// Frequency bins at 0 Hz and nyquist don't count for two.
+
+		z[1] *= 0.5;
+		z[s -> nx] *= 0.5;
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": no Matrix with spectral power created.");
+	}
+}
+
+static int Sound_into_BarkFilter_frame (Sound me, BarkFilter thee, long frame) {
+	autoMatrix pv = Sound_to_spectralpower (me);
+	long nf = pv -> nx;
+	autoNUMvector<double> z (1, nf);
+
+	for (long j = 1; j <= nf; j++) {
+		z[j] = HZTOBARK (pv -> x1 + (j - 1) * pv -> dx);
+	}
+
+	for (long i = 1; i <= thy ny; i++) {
+		double p = 0;
+		double z0 = thy y1 + (i - 1) * thy dy;
+		double *pow = pv -> z[1]; // TODO ??
+		for (long j = 1; j <= nf; j++) {
+			// Sekey & Hanson filter is defined in the power domain.
+			// We therefore multiply the power with a (and not a^2).
+			// integral (F(z),z=0..25) = 1.58/9
+
+			double a = NUMsekeyhansonfilter_amplitude (z0, z[j]);
+			p += a * pow[j] ;
+		}
+		thy z[i][frame] = p;
+	}
+	return 1;
+}
+
+BarkFilter Sound_to_BarkFilter (Sound me, double analysisWidth, double dt, double f1_bark, double fmax_bark, double df_bark) {
+	try {
+		double t1, nyquist = 0.5 / my dx, samplingFrequency = 2 * nyquist;
+		double windowDuration = 2 * analysisWidth; /* gaussian window */
+		double zmax = NUMhertzToBark2 (nyquist);
+		double fmin_bark = 0;
+		long nt, frameErrorCount = 0;
+
+		// Check defaults.
+
+		if (f1_bark <= 0) {
+			f1_bark = 1;
+		}
+		if (fmax_bark <= 0) {
+			fmax_bark = zmax;
+		}
+		if (df_bark <= 0) {
+			df_bark = 1;
+		}
+
+		fmax_bark = MIN (fmax_bark, zmax);
+		long nf = floor ( (fmax_bark - f1_bark) / df_bark + 0.5);
+		if (nf <= 0) {
+			Melder_throw ("The combination of filter parameters is not valid.");
+		}
+
+		Sampled_shortTermAnalysis (me, windowDuration, dt, & nt, & t1);
+		autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency);
+		autoSound window = Sound_createGaussian (windowDuration, samplingFrequency);
+		autoBarkFilter thee = BarkFilter_create (my xmin, my xmax, nt, dt, t1,
+		                      fmin_bark, fmax_bark, nf, df_bark, f1_bark);
+
+		autoMelderProgress progess (L"BarkFilter analysis");
+
+		for (long i = 1; i <= nt; i++) {
+			double t = Sampled_indexToX (thee.peek(), i);
+
+			Sound_into_Sound (me, sframe.peek(), t - windowDuration / 2);
+
+			Sounds_multiply (sframe.peek(), window.peek());
+
+			if (! Sound_into_BarkFilter_frame (sframe.peek(), thee.peek(), i)) {
+				frameErrorCount++;
+			}
+
+			if ( (i % 10) == 1) {
+				Melder_progress ( (double) i / nt,  L"BarkFilter analysis: frame ",
+					Melder_integer (i), L" from ", Melder_integer (nt), L".");
+			}
+		}
+
+		if (frameErrorCount > 0) {
+			Melder_warning (L"Analysis results of ", Melder_integer (frameErrorCount), L" frame(s) out of ",
+				Melder_integer (nt), L" will be suspect.");
+		}
+
+		double ref = FilterBank_DBREF * gaussian_window_squared_correction (window -> nx);
+
+		NUMdmatrix_to_dBs (thy z, 1, thy ny, 1, thy nx, ref, FilterBank_DBFAC, FilterBank_DBFLOOR);
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": no BarkFilter created.");
+	}
+}
+
+static int Sound_into_MelFilter_frame (Sound me, MelFilter thee, long frame) {
+	autoMatrix pv = Sound_to_spectralpower (me);
+
+	double z1 = pv -> x1;
+	double dz = pv -> dx;
+	long nf = pv -> nx;
+	double df = thy dy;
+	for (long i = 1; i <= thy ny; i++) {
+		double p = 0;
+		double fc_mel = thy y1 + (i - 1) * df;
+		double fc_hz = MELTOHZ (fc_mel);
+		double fl_hz = MELTOHZ (fc_mel - df);
+		double fh_hz =  MELTOHZ (fc_mel + df);
+		double *pow = pv -> z[1];
+		for (long j = 1; j <= nf; j++) {
+			// Bin with a triangular filter the power (=amplitude-squared)
+
+			double f = z1 + (j - 1) * dz;
+			double a = NUMtriangularfilter_amplitude (fl_hz, fc_hz, fh_hz, f);
+			p += a * pow[j];
+		}
+		thy z[i][frame] = p;
+	}
+	return 1;
+}
+
+MelFilter Sound_to_MelFilter (Sound me, double analysisWidth, double dt, double f1_mel, double fmax_mel, double df_mel) {
+	try {
+		double t1, samplingFrequency = 1 / my dx, nyquist = 0.5 * samplingFrequency;
+		double windowDuration = 2 * analysisWidth; /* gaussian window */
+		double fmin_mel = 0;
+		double fbottom = HZTOMEL (100.0), fceiling = HZTOMEL (nyquist);
+		long nt, frameErrorCount = 0;
+
+		// Check defaults.
+
+		if (fmax_mel <= 0 || fmax_mel > fceiling) {
+			fmax_mel = fceiling;
+		}
+		if (fmax_mel <= f1_mel) {
+			f1_mel = fbottom; fmax_mel = fceiling;
+		}
+		if (f1_mel <= 0) {
+			f1_mel = fbottom;
+		}
+		if (df_mel <= 0) {
+			df_mel = 100.0;
+		}
+
+		// Determine the number of filters.
+
+		long nf = floor ((fmax_mel - f1_mel) / df_mel + 0.5);
+		fmax_mel = f1_mel + nf * df_mel;
+
+		Sampled_shortTermAnalysis (me, windowDuration, dt, &nt, &t1);
+		autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency);
+		autoSound window = Sound_createGaussian (windowDuration, samplingFrequency);
+		autoMelFilter thee = MelFilter_create (my xmin, my xmax, nt, dt, t1, fmin_mel, fmax_mel, nf, df_mel, f1_mel);
+
+		autoMelderProgress progress (L"MelFilters analysis");
+
+		for (long i = 1; i <= nt; i++) {
+			double t = Sampled_indexToX (thee.peek(), i);
+			Sound_into_Sound (me, sframe.peek(), t - windowDuration / 2);
+			Sounds_multiply (sframe.peek(), window.peek());
+			if (! Sound_into_MelFilter_frame (sframe.peek(), thee.peek(), i)) {
+				frameErrorCount++;
+			}
+			if ( (i % 10) == 1) {
+				Melder_progress ((double) i / nt, L"Frame ", Melder_integer (i), L" out of ", Melder_integer (nt), L".");
+			}
+		}
+
+		if (frameErrorCount) {
+			Melder_warning (L"Analysis results of ", Melder_integer (frameErrorCount),
+			L" frame(s) out of ", Melder_integer (nt), L" will be suspect.");
+		}
+
+		// Window correction.
+
+		double ref = FilterBank_DBREF * gaussian_window_squared_correction (window -> nx);
+
+		NUMdmatrix_to_dBs (thy z, 1, thy ny, 1, thy nx, ref, FilterBank_DBFAC, FilterBank_DBFLOOR);
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": no MelFilter created.");
+	}
+}
+
+/*
+	Analog formant filter response :
+	H(f) = i f B / (f1^2 - f^2 + i f B)
+*/
+static int Sound_into_FormantFilter_frame (Sound me, FormantFilter thee, long frame, double bw) {
+	Melder_assert (bw > 0);
+	autoMatrix pv = Sound_to_spectralpower (me);
+	double z1 = pv -> x1;
+	double dz = pv -> dx;
+	long nf = pv -> nx;
+
+	for (long i = 1; i <= thy ny; i++) {
+		double p = 0;
+		double fc = thy y1 + (i - 1) * thy dy;
+		double *pow = pv -> z[1];
+		for (long j = 1; j <= nf; j++) {
+			// H(f) = ifB / (fc^2 - f^2 + ifB)
+			// H(f)| = fB / sqrt ((fc^2 - f^2)^2 + f^2B^2)
+			//|H(f)|^2 = f^2B^2 / ((fc^2 - f^2)^2 + f^2B^2)
+			//         = 1 / (((fc^2 - f^2) /fB)^2 + 1)
+
+			double f = z1 + (j - 1) * dz;
+			double a = NUMformantfilter_amplitude (fc, bw, f);
+			p += a * pow[j];
+		}
+		thy z[i][frame] = p;
+	}
+	return 1;
+}
+
+FormantFilter Sound_to_FormantFilter (Sound me, double analysisWidth,
+                                      double dt, double f1_hz, double fmax_hz, double df_hz, double relative_bw,
+                                      double minimumPitch, double maximumPitch) {
+	try {
+		double floor = 80, ceiling = 600;
+		if (minimumPitch >= maximumPitch) {
+			minimumPitch = floor; maximumPitch = ceiling;
+		}
+		if (minimumPitch <= 0) {
+			minimumPitch = floor;
+		}
+		if (maximumPitch <= 0) {
+			maximumPitch = ceiling;
+		}
+
+		autoPitch thee = Sound_to_Pitch (me, dt, minimumPitch, maximumPitch);
+		autoFormantFilter ff = Sound_and_Pitch_to_FormantFilter (me, thee.peek(), analysisWidth, dt,
+		                       f1_hz, fmax_hz, df_hz, relative_bw);
+		return ff.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": no FormantFilter created.");
+	}
+}
+
+FormantFilter Sound_and_Pitch_to_FormantFilter (Sound me, Pitch thee, double analysisWidth, double dt,
+        double f1_hz, double fmax_hz, double df_hz, double relative_bw) {
+	try {
+		double t1, windowDuration = 2 * analysisWidth; /* gaussian window */
+		double nyquist = 0.5 / my dx, samplingFrequency = 2 * nyquist, fmin_hz = 0;
+		long nt, f0_undefined = 0;
+
+		if (my xmin > thy xmin || my xmax > thy xmax) Melder_throw
+			("The domain of the Sound is not included in the domain of the Pitch.");
+
+		double f0_median = Pitch_getQuantile (thee, thy xmin, thy xmax, 0.5, kPitch_unit_HERTZ);
+
+		if (f0_median == NUMundefined || f0_median == 0) {
+			f0_median = 100;
+			Melder_warning (L"Pitch values undefined. Bandwith fixed to 100 Hz. ");
+		}
+
+		if (f1_hz <= 0) {
+			f1_hz = 100;
+		}
+		if (fmax_hz <= 0) {
+			fmax_hz = nyquist;
+		}
+		if (df_hz <= 0) {
+			df_hz = f0_median / 2;
+		}
+		if (relative_bw <= 0) {
+			relative_bw = 1.1;
+		}
+
+		fmax_hz = MIN (fmax_hz, nyquist);
+		long nf = floor ( (fmax_hz - f1_hz) / df_hz + 0.5);
+
+		Sampled_shortTermAnalysis (me, windowDuration, dt, &nt, &t1);
+		autoFormantFilter him = FormantFilter_create (my xmin, my xmax, nt, dt, t1,
+		                        fmin_hz, fmax_hz, nf, df_hz, f1_hz);
+
+		// Temporary objects
+
+		autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency);
+		autoSound window = Sound_createGaussian (windowDuration, samplingFrequency);
+		autoMelderProgress progress (L"Sound & Pitch: To FormantFilter");
+		for (long i = 1; i <= nt; i++) {
+			double t = Sampled_indexToX (him.peek(), i);
+			double b, f0 = Pitch_getValueAtTime (thee, t, kPitch_unit_HERTZ, 0);
+
+			if (f0 == NUMundefined || f0 == 0) {
+				f0_undefined++; f0 = f0_median;
+			}
+			b = relative_bw * f0;
+			Sound_into_Sound (me, sframe.peek(), t - windowDuration / 2);
+			Sounds_multiply (sframe.peek(), window.peek());
+
+			Sound_into_FormantFilter_frame (sframe.peek(), him.peek(), i, b);
+
+			if ( (i % 10) == 1) {
+				Melder_progress ( (double) i / nt, L"Frame ", Melder_integer (i), L" out of ",
+				                   Melder_integer (nt), L".");
+			}
+		}
+
+		double ref = FilterBank_DBREF * gaussian_window_squared_correction (window -> nx);
+		NUMdmatrix_to_dBs (his z, 1, his ny, 1, his nx, ref, FilterBank_DBFAC, FilterBank_DBFLOOR);
+		return him.transfer();
+	} catch (MelderError) {
+		Melder_throw ("FormantFilter not created from Pitch & FormantFilter.");
+	}
+}
+
+Sound FilterBank_as_Sound (FilterBank me) {
+	try {
+		autoSound thee = Sound_create (my ny, my xmin, my xmax, my nx, my dx, my x1);
+		for (long i = 1; i <= my ny; i++) {
+			for (long j = 1; j <= my nx; j++)
+				thy z[i][j] = my z[i][j];
+		}
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": no Sound created.");
+	}
+}
+
+Sound FilterBanks_crossCorrelate (FilterBank me, FilterBank thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain) {
+	try {
+		autoSound cc = Sounds_crossCorrelate ((Sound) me, (Sound) thee, scaling, signalOutsideTimeDomain);
+		return cc.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, " and ", thee, " not cross-correlated.");
+	}
+}
+
+Sound FilterBanks_convolve (FilterBank me, FilterBank thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain) {
+	try {
+		autoSound cc = Sounds_convolve ((Sound) me, (Sound) thee, scaling, signalOutsideTimeDomain);
+		return cc.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, " and ", thee, " not convolved.");
+	}
+}
+
+#undef MAX
+#undef MIN
+
 /* End of file Filterbank.cpp */
diff --git a/dwtools/FilterBank.h b/dwtools/FilterBank.h
index 8f9ab39..d1db412 100644
--- a/dwtools/FilterBank.h
+++ b/dwtools/FilterBank.h
@@ -2,7 +2,7 @@
 #define _FilterBank_h_
 /* FilterBank.h
  *
- * Copyright (C) 1993-2011 David Weenink
+ * Copyright (C) 1993-2011, 2014 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
@@ -31,6 +31,8 @@
 #include "MFCC.h"
 #include "Intensity.h"
 #include "PCA.h"
+#include "Pitch.h"
+#include "Spectrogram_extensions.h"
 
 #define HZTOBARK(x) NUMhertzToBark2(x)
 #define HZTOMEL(x)	NUMhertzToMel2(x)
@@ -48,6 +50,10 @@
 #define FilterBank_BARK  2
 #define FilterBank_MEL   3
 
+// FilterBank, BarkFilter, MelFilter are deprecated as op october 2014.
+// New types are BandFilterSpectrogram, BarkSpectrogram and MelSpectrogram.
+// This interface is maintained because older scripts stiil have to work.
+
 Thing_define (FilterBank, Matrix) {
 	// new methods:
 	public:
@@ -85,6 +91,8 @@ void BarkFilter_drawSekeyHansonFilterFunctions (BarkFilter me, Graphics g,
 void FilterBank_drawTimeSlice (I, Graphics g, double t, double fmin, double fmax,
 	double min, double max, const wchar_t *xlabel, int garnish);
 
+void FilterBank_paint (FilterBank me, Graphics g, double xmin, double xmax, double ymin, double ymax, double minimum, double maximum, int garnish);
+
 BarkFilter BarkFilter_create (double tmin, double tmax, long nt, double dt,
 	double t1, double fmin, double fmax, long nf, double df, long f1);
 
@@ -153,4 +161,47 @@ Intensity FilterBank_to_Intensity (I);
 void FilterBank_and_PCA_drawComponent (I, PCA thee, Graphics g, long component, double dblevel,
 	double frequencyOffset, double scale, double tmin, double tmax, double fmin, double fmax);
 
+// Convert old types to new types
+
+MelSpectrogram MelFilter_to_MelSpectrogram (MelFilter me);
+BarkSpectrogram BarkFilter_to_BarkSpectrogram (BarkFilter me);
+Spectrogram FormantFilter_to_Spectrogram (FormantFilter me);
+
+
+MFCC MelFilter_to_MFCC (MelFilter me, long numberOfCoefficients);
+/*
+Calculates the Cosine Transform of the filterbank values.
+*/
+
+MelFilter MFCC_to_MelFilter (MFCC me, long firstCoefficient, long lastCoefficient);
+/*
+Calculates the Inverse CT of cepstral coefficients.
+*/
+
+BarkFilter Sound_to_BarkFilter (Sound me, double analysisWidth, double dt,
+	double f1_bark, double fmax_bark, double df_bark);
+/*
+	Filtering with filters on a Bark scale as defined by
+		Andrew Sekey & Brian Hanson (1984), "Improved 1-Bark bandwidth
+		"auditory filter", Jasa 75, 1902-1904.
+	Although not explicitely stated the filter function is defined in the
+	power domain.
+	10 log F(z) = 15.8 + 7.5(z + 0.5) - 17.5 * sqrt(1 + (z + 0.5)^2)
+*/
+
+MelFilter Sound_to_MelFilter (Sound me, double analysisWidth, double dt,
+	double f1_mel, double fmax_mel, double df_mel);
+
+FormantFilter Sound_to_FormantFilter (Sound me, double analysisWidth,
+	double dt, double f1_hz, double fmax_hz, double df_hz, double relative_bw,
+	double minimumPitch, double maximumPitch);
+
+FormantFilter Sound_and_Pitch_to_FormantFilter (Sound me, Pitch thee,
+	double analysisWidth, double dt, double f1_hz, double fmax_hz,
+	double df_hz, double relative_bw);
+
+Sound FilterBanks_crossCorrelate (FilterBank me, FilterBank thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain);
+Sound FilterBanks_convolve (FilterBank me, FilterBank thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain);
+
+
 #endif /* _FilterBank_h_ */
diff --git a/dwtools/KlattGrid.cpp b/dwtools/KlattGrid.cpp
index fd8fcb1..db9b269 100644
--- a/dwtools/KlattGrid.cpp
+++ b/dwtools/KlattGrid.cpp
@@ -1551,8 +1551,8 @@ static Sound Sound_VocalTractGrid_CouplingGrid_filter_cascade (Sound me, VocalTr
 			if (oral_formant_warning) {
 				MelderString_append (&warning, L"\tOral formants: one or more are missing.\n");
 			}
-            Melder_print (L"\nWarning:\n");
-			Melder_print (warning.string);
+			MelderInfo_write (L"\nWarning:\n", warning.string);
+			MelderInfo_drain ();
 		}
 		return him.transfer();
 	} catch (MelderError) {
diff --git a/dwtools/MFCC.cpp b/dwtools/MFCC.cpp
index bd9a09f..c466e76 100644
--- a/dwtools/MFCC.cpp
+++ b/dwtools/MFCC.cpp
@@ -2,7 +2,7 @@
  *
  * Mel Frequency Cepstral Coefficients class.
  *
- * Copyright (C) 1993-2013 David Weenink
+ * Copyright (C) 1993-2014 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
@@ -24,14 +24,15 @@
  * djmw 20110304 Thing_new
 */
 
-#include "MelFilter_and_MFCC.h"
+#include "MFCC.h"
+#include "Spectrogram_extensions.h"
 #include "NUM2.h"
 
 
 Thing_implement (MFCC, CC, 1);
 
 MFCC MFCC_create (double tmin, double tmax, long nt, double dt, double t1,
-                  long maximumNumberOfCoefficients, double fmin_mel, double fmax_mel) {
+	  long maximumNumberOfCoefficients, double fmin_mel, double fmax_mel) {
 	try {
 		autoMFCC me = Thing_new (MFCC);
 		CC_init (me.peek(), tmin, tmax, nt, dt, t1, maximumNumberOfCoefficients, fmin_mel, fmax_mel);
@@ -157,7 +158,7 @@ static double CC_Frames_distance (CC_Frame me, CC_Frame thee, bool includeEnergy
 Matrix MFCC_to_Matrix_features (MFCC me, double windowLength, bool includeEnergy) {
 	try {
 		long nw = windowLength / my dx / 2;
-		autoMelFilter him = MFCC_to_MelFilter (me, 0, 0);
+		autoMelSpectrogram him = MFCC_to_MelSpectrogram (me, 0, 0, 1);
 		autoMatrix thee = Matrix_create (my xmin, my xmax, my nx, my dx, my x1, 1, 4, 4, 1, 1);
 		thy z[1][1] = thy z[1][my nx] = 0;  // first & last frame
 		for (long iframe = 2; iframe <= my nx - 1; iframe++) {
@@ -215,7 +216,6 @@ Matrix MFCC_to_Matrix_features (MFCC me, double windowLength, bool includeEnergy
 	} catch (MelderError) {
 		Melder_throw (me, ": no features calculated.");
 	}
-
 }
 
 /* End of file MFCC.cpp */
diff --git a/dwtools/MFCC.h b/dwtools/MFCC.h
index 732a2e4..b7b2770 100644
--- a/dwtools/MFCC.h
+++ b/dwtools/MFCC.h
@@ -35,9 +35,23 @@ Thing_define (MFCC, CC) {
 };
 
 /*
+	Three slightly "different" definitions of MFCC
+	1. Davis & Mermelstein
+		MFCC[i] = SUM (j=1..N, f[j] * cos (i(j-1/2)pi/N)), i = 1..N-1
+	2. Vergin & O'Shaughnessy
+		MFCC[i] = SUM (j=0..N-1, f[j] * cos (i(j+1/2)pi/N)), i = 0..N-1
+	3. HTK-book
+		MFCC[i] = sqrt(2/n) SUM (j=1..N, f[j] * cos (i(j-1/2)pi/N)), i = 0..N-1
+
+	The f[j]'s are the MelSpectrogram values converted to dB.
+	We follow the definition of Davis and Mermelstein:
+    	MFCC[i] = SUM (j=1..N, f[j] * cos (i(j-1/2)pi/N)), i=1..N-1,
+*/
+
+/*
 	Interpretation:
-	Mel frequency cepstral coefficients as a function of time.
-	c0 represents the average filter output (dB's).
+	Mel frequency cepstral coefficient vectors as a function of time.
+	c0 represents the sum of the dB filter outputs.
 */
 
 MFCC MFCC_create (double tmin, double tmax, long nt, double dt, double t1,
diff --git a/dwtools/Makefile b/dwtools/Makefile
index 7ae8089..f8c3771 100644
--- a/dwtools/Makefile
+++ b/dwtools/Makefile
@@ -1,6 +1,6 @@
 # Makefile of the library "dwtools"
 # David Weenink, 22 February 2010
-# djmw 20140409 Latest modification
+# djmw 20141019 Latest modification
 
 include ../makefile.defs
 
@@ -27,7 +27,7 @@ OBJECTS = Activation.o AffineTransform.o \
 	LFCC.o LongSound_extensions.o \
 	KlattGrid.o KlattGridEditors.o KlattTable.o \
 	Ltas_extensions.o \
-	MelFilter_and_MFCC.o MFCC.o \
+	MFCC.o \
 	manual_DataModeler.o manual_dwtools.o manual_BSS.o manual_HMM.o \
 	manual_KlattGrid.o manual_MDS.o manual_Permutation.o \
 	Minimizers.o \
@@ -39,10 +39,10 @@ OBJECTS = Activation.o AffineTransform.o \
 	Proximity.o \
 	Resonator.o \
 	Sampled2.o \
-	Sound_and_FilterBank.o Sound_and_PCA.o Sound_extensions.o \
+	Sound_and_Spectrogram_extensions.o Sound_and_PCA.o Sound_extensions.o \
 	Sound_to_MFCC.o Sounds_to_DTW.o \
 	Sound_to_Pitch2.o Sound_to_SPINET.o SPINET.o SPINET_to_Pitch.o \
-	Spectrum_extensions.o SSCP.o Strings_extensions.o \
+	Spectrogram_extensions.o Spectrum_extensions.o SSCP.o Strings_extensions.o \
 	SpeechSynthesizer.o SpeechSynthesizer_and_TextGrid.o\
 	Table_extensions.o TableOfReal_and_SVD.o\
 	TableOfReal_extensions.o \
diff --git a/dwtools/MelFilter_and_MFCC.cpp b/dwtools/MelFilter_and_MFCC.cpp
deleted file mode 100644
index b58ffb2..0000000
--- a/dwtools/MelFilter_and_MFCC.cpp
+++ /dev/null
@@ -1,213 +0,0 @@
-/* MelFilter_and_MFCC.cpp
- *
- * Copyright (C) 1993-2013 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
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/*
- djmw 2001
- djmw 20020813 GPL header
- djmw 20130207 Latest modification
-*/
-
-#include "MelFilter_and_MFCC.h"
-#include "NUM2.h"
-
-#define MAX(m,n) ((m) > (n) ? (m) : (n))
-#define MIN(m,n) ((m) < (n) ? (m) : (n))
-
-static double **NUMcosinesTable (long  n) {
-	autoNUMmatrix<double> costab (1, n, 1, n);
-	for (long k = 1; k <= n; k++) {
-		for (long j = 1; j <= n; j++) {
-			costab[k][j] = cos (NUMpi * (k - 1) * (j - 0.5) / n);
-		}
-	}
-	return costab.transfer();
-}
-
-// x[1..n] : input
-// y[1..n] : output
-static void NUMcosineTransform (double *x, double *y, long n, double **cosinesTable) {
-	for (long k = 1; k <= n; k++) {
-		y[k] = 0;
-		for (long j = 1; j <= n; j++) {
-			y[k] += x[j] * cosinesTable[k][j];
-		}
-	}
-}
-
-
-// x: input
-// y: output
-static void NUMinverseCosineTransform (double *x, double *y, long n, double **cosinesTable) {
-	for (long j = 1; j <= n; j++) {
-		y[j] = 0.5 * x[1] * cosinesTable[1][j];
-		for (long k = 2; k <= n; k++) {
-			y[j] += x[k] * cosinesTable[k][j];
-		}
-		y[j] *= 2.0 / n;
-	}
-}
-
-double testCosineTransform (long n) {
-	try {
-		autoNUMvector<double> x (1, n);
-		autoNUMvector<double> y (1, n);
-		autoNUMvector<double> x2 (1, n);
-		autoNUMmatrix<double> cosinesTable (NUMcosinesTable (n), 1, 1);
-		for (long i = 1 ; i <= n; i++) {
-			x[i] = NUMrandomUniform (0, 70);
-		}
-		NUMcosineTransform (x.peek(), y.peek(), n, cosinesTable.peek());
-		NUMinverseCosineTransform (y.peek(), x2.peek(), n, cosinesTable.peek());
-		double delta = 0;
-		for (long i =1 ; i <= n; i++) {
-			double dif = x[i] - x2[i];
-			delta += dif * dif;
-		}
-		delta = sqrt (delta);
-		return delta;
-	} catch (MelderError) {
-		Melder_throw ("Test cosine transform error");
-	}
-}
-
-MFCC MelFilter_to_MFCC (MelFilter me, long numberOfCoefficients) {
-	try {
-		autoNUMmatrix<double> cosinesTable (NUMcosinesTable (my ny), 1, 1);
-		autoNUMvector<double> x (1, my ny);
-		autoNUMvector<double> y (1, my ny);
-		
-		double fmax_mel = my y1 + (my ny - 1) * my dy;
-		numberOfCoefficients = numberOfCoefficients > my ny - 1 ? my ny - 1 : numberOfCoefficients;
-		Melder_assert (numberOfCoefficients > 0);
-		// 20130220 new interpretation of maximumNumberOfCoefficients necessary for inverse transform 
-		autoMFCC thee = MFCC_create (my xmin, my xmax, my nx, my dx, my x1, my ny - 1, my ymin, my ymax);
-		for (long frame = 1; frame <= my nx; frame++) {
-			CC_Frame cf = (CC_Frame) & thy frame[frame];
-			for (long i = 1; i <= my ny; i++) {
-				x[i] = my z[i][frame];
-			}
-			NUMcosineTransform (x.peek(), y.peek(), my ny, cosinesTable.peek());
-			CC_Frame_init (cf, numberOfCoefficients);
-			for (long i = 1; i <= numberOfCoefficients; i++) {
-				cf -> c[i] = y[i + 1];
-			}
-			cf -> c0 = y[1];
-		}
-		return thee.transfer();
-	} catch (MelderError) {
-		Melder_throw (me, ": no MFCC created.");
-	}
-}
-
-MelFilter MFCC_to_MelFilter (MFCC me, long first, long last) {
-	try {
-		long nf = my maximumNumberOfCoefficients + 1;
-		autoNUMmatrix<double> cosinesTable (NUMcosinesTable (nf), 1, 1);
-		autoNUMvector<double> x (1, nf);
-		autoNUMvector<double> y (1, nf);
-
-		if (first >= last) {
-			first = 0; last = nf - 1;
-		}
-
-		if (first < 0 || last > nf - 1) {
-			Melder_throw ("MFCC_to_MelFilter: coefficients must be in interval [0,", my maximumNumberOfCoefficients, "].");
-		}
-		double df = (my fmax - my fmin) / (nf + 1);
-		autoMelFilter thee = MelFilter_create (my xmin, my xmax, my nx, my dx, my x1, my fmin, my fmax, nf, df, df);
-
-		for (long frame = 1; frame <= my nx; frame++) {
-			CC_Frame cf = (CC_Frame) & my frame[frame];
-			long iend = MIN (last, cf -> numberOfCoefficients);
-			x[1] = first == 0 ? cf -> c0 : 0;
-			for (long i = 1; i <= my maximumNumberOfCoefficients; i++) {
-				x[i + 1] = i < first || i > iend ? 0 : cf -> c[i];
-			}
-			NUMinverseCosineTransform (x.peek(), y.peek(), nf, cosinesTable.peek());
-			for (long i = 1; i <= nf; i++) {
-				
-				thy z[i][frame] = y[i];
-			}
-		}
-		return thee.transfer();
-	} catch (MelderError) {
-		Melder_throw (me, ": no MelFilter created.");
-	}
-}
-
-
-MelFilter MFCC_to_MelFilter2 (MFCC me, long first_cc, long last_cc, double f1_mel, double df_mel) {
-	try {
-		int use_c0 = 0;
-		long nf = ((my fmax - my fmin) / df_mel + 0.5);
-		double fmin = MAX (f1_mel - df_mel, 0), fmax = f1_mel + (nf + 1) * df_mel;
-
-		if (nf < 1) {
-			Melder_throw ("MFCC_to_MelFilter: the position of the first filter, the distance between the filters, "
-			"and, the maximum do not result in a positive number of filters.");
-		}
-
-		// Default values
-
-		if (first_cc == 0) {
-			first_cc = 1;
-			use_c0 = 1;
-		}
-		if (last_cc == 0) {
-			last_cc = my maximumNumberOfCoefficients;
-		}
-
-		// Be strict
-
-		if (last_cc < first_cc || first_cc < 1 || last_cc > my maximumNumberOfCoefficients) {
-			Melder_throw ("MFCC_to_MelFilter: coefficients must be in interval [1,", my maximumNumberOfCoefficients, "].");
-		}
-		autoNUMmatrix<double> dct (NUMcosinesTable (first_cc, last_cc, nf), first_cc, 1); // TODO ??
-		//if ((dct = NUMcosinesTable (first_cc, last_cc, nf)) == NULL) return NULL;
-
-		autoMelFilter thee = MelFilter_create (my xmin, my xmax, my nx, my dx, my x1, fmin, fmax, nf, df_mel, f1_mel);
-
-		for (long frame = 1; frame <= my nx; frame++) {
-			CC_Frame cf = (CC_Frame) & my frame[frame];
-			long ie = MIN (last_cc, cf -> numberOfCoefficients);
-			for (long j = 1; j <= nf; j++) {
-				double t = 0;
-				for (long i = first_cc; i <= ie; i++) {
-					t += cf -> c[i] * dct[i][j];
-				}
-
-				// The inverse CT has a factor 1/N
-
-				t /= nf;
-				if (use_c0) {
-					t +=  cf -> c0;
-				}
-				thy z[j][frame] = t;
-			}
-		}
-		return thee.transfer();
-	} catch (MelderError) {
-		Melder_throw (me, ": no MelFilter created.");
-	}
-}
-
-#undef MAX
-#undef MIN
-
-/* End of file MelFilter_and_MFCC.cpp */
diff --git a/dwtools/MelFilter_and_MFCC.h b/dwtools/MelFilter_and_MFCC.h
deleted file mode 100644
index 17ea3ff..0000000
--- a/dwtools/MelFilter_and_MFCC.h
+++ /dev/null
@@ -1,62 +0,0 @@
-#ifndef _MelFilter_and_MFCC_h_
-#define _MelFilter_and_MFCC_h_
-/* MelFilter_and_MFCC.h
- *
- * Copyright (C) 1993-2013 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
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/*
- djmw 2001
- djmw 20020813 GPL header
- djmw 20110307 Latest modification
-*/
-
-#include "FilterBank.h"
-#include "MFCC.h"
-
-/*
-Three slightly "different" definitions of MFCC
-1. Davis & Mermelstein
-	MFCC[i] = SUM (j=1..N, f[j] * cos (i(j-1/2)pi/N)), i=1..N-1
-2. Vergin & O'Shaughnessy
-	MFCC[i] = SUM (j=0..N-1, f[j] * cos (i(j+1/2)pi/N)), i = 0..N-1
-3. HTK-book
-	MFCC[i] = sqrt(2/n) SUM (j=1..N, f[j] * cos (i(j-1/2)pi/N))
-    i = 0..N-1
-
-The f[j]'s are the filterbank outputs in dB.
-We follow the definition of Davis and Mermelstein:
-    	MFCC[i] = SUM (j=1..N, f[j] * cos (i(j-1/2)pi/N)), i=1..N-1,
-however, we also calculate the zero coefficient as
-    	MFCC[0] = SUM (j=1..N, f[j]) / N.
-The pure cosine transform MFCC[0] would be the sum instead of the average.
-This average is more convenient in the inverse transform (from MFCC to
-MelFilter).
-*/
-
-
-MFCC MelFilter_to_MFCC (MelFilter me, long numberOfCoefficients);
-/*
-Calculates the Cosine Transform of the filterbank values.
-*/
-
-MelFilter MFCC_to_MelFilter (MFCC me, long firstCoefficient, long lastCoefficient);
-/*
-Calculates the Inverse CT of cepstral coefficients.
-*/
-
-#endif /* MelFilter_and_MFCC.h */
diff --git a/dwtools/Polygon_extensions.cpp b/dwtools/Polygon_extensions.cpp
index abf05f4..bfd4b50 100644
--- a/dwtools/Polygon_extensions.cpp
+++ b/dwtools/Polygon_extensions.cpp
@@ -581,7 +581,7 @@ protected:
 };
 Thing_implement (Vertices, DLL, 0);
 
-#define VERTEX(n) ((Vertex) (n -> data))
+#define VERTEX(n) ((Vertex) ((n) -> data))
 
 int structVertices :: s_compare (I, thou) {
 	iam (DLLNode); thouart (DLLNode);
diff --git a/dwtools/Sound_and_FilterBank.cpp b/dwtools/Sound_and_FilterBank.cpp
deleted file mode 100644
index 0bd004a..0000000
--- a/dwtools/Sound_and_FilterBank.cpp
+++ /dev/null
@@ -1,421 +0,0 @@
-/* Sound_and_FilterBank.cpp
- *
- * Copyright (C) 1993-2013 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
- * the Free Software Foundation; either version 2 of the License, or (at
- * your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-/*
- djmw 20010718
- djmw 20020813 GPL header.
- djmw 20041124 Changed call to Sound_to_Spectrum.
- djmw 20070103 Sound interface changes
- djmw 20071107 Errors/warnings text changes
- djmw 20071202 Melder_warning<n>
-*/
-
-#include "Sound_and_FilterBank.h"
-#include "Sound_extensions.h"
-#include "Sound_and_Spectrum.h"
-#include "Sound_to_Pitch.h"
-#include "Vector.h"
-#include "NUM2.h"
-
-#define MIN(m,n) ((m) < (n) ? (m) : (n))
-// prototypes
-Sound FilterBank_as_Sound (FilterBank me);
-
-/*
-	The gaussian(x) = (exp(-48*((i-(n+1)/2)/(n+1))^2)-exp(-12))/(1-exp(-12));
-	For power we need the area under the square of this window:
-		Integrate (gaussian(i)^2,i=1..n) =
-
-	(sqrt(Pi)*sqrt(3)*sqrt(2)*erf(2*(n-1)*sqrt(3)*sqrt(2)/(n+1))*(n+1)+
-		24*exp(-24)*(n-1)+
-	-4*sqrt(Pi)*sqrt(3)*exp(-12)*erf(2*(n-1)*sqrt(3)/(n+1))*(n+1))/
-	(24 * (-1+exp(-12))^2)
-	To compare with the rectangular window we need to divide this by the
-	window width (n-1) x 1^2.
-*/
-static double gaussian_window_squared_correction (long n) {
-	double e12 = exp (-12), denum = (e12 - 1) * (e12 - 1) * 24 * (n - 1);
-	double sqrt3 = sqrt (3), sqrt2 = sqrt (2), sqrtpi = sqrt (NUMpi);
-	double arg1 = 2 * sqrt3 * (n - 1) / (n + 1), arg2 = arg1 * sqrt2;
-	double p2 = sqrtpi * sqrt3 * sqrt2 * (1 - NUMerfcc (arg2)) * (n + 1);
-	double p1 = 4 * sqrtpi * sqrt3 * e12 * (1 - NUMerfcc (arg1)) * (n + 1);
-
-	return (p2 - p1 + 24 * (n - 1) * e12 * e12) / denum;
-}
-
-static Matrix Sound_to_spectralpower (Sound me) {
-	try {
-		autoSpectrum s = Sound_to_Spectrum (me, TRUE);
-		autoMatrix thee = Matrix_create (s -> xmin, s -> xmax, s -> nx, s -> dx, s -> x1, 1, 1, 1, 1, 1);
-		double scale = 2.0 * s -> dx / (my xmax - my xmin);
-
-		// factor '2' because of positive and negative frequencies
-		// s -> dx : width of frequency bin
-		// my xmax - my xmin : duration of sound
-
-		double *z = thy z[1], *re = s -> z[1], *im = s -> z[2];
-		for (long i = 1; i <= s -> nx; i++) {
-			z[i] = scale * (re[i] * re[i] + im[i] * im [i]);
-		}
-
-		// Frequency bins at 0 Hz and nyquist don't count for two.
-
-		z[1] *= 0.5;
-		z[s -> nx] *= 0.5;
-		return thee.transfer();
-	} catch (MelderError) {
-		Melder_throw (me, ": no Matrix with spectral power created.");
-	}
-}
-
-static int Sound_into_BarkFilter_frame (Sound me, BarkFilter thee, long frame) {
-	autoMatrix pv = Sound_to_spectralpower (me);
-	long nf = pv -> nx;
-	autoNUMvector<double> z (1, nf);
-
-	for (long j = 1; j <= nf; j++) {
-		z[j] = HZTOBARK (pv -> x1 + (j - 1) * pv -> dx);
-	}
-
-	for (long i = 1; i <= thy ny; i++) {
-		double p = 0;
-		double z0 = thy y1 + (i - 1) * thy dy;
-		double *pow = pv -> z[1]; // TODO ??
-		for (long j = 1; j <= nf; j++) {
-			// Sekey & Hanson filter is defined in the power domain.
-			// We therefore multiply the power with a (and not a^2).
-			// integral (F(z),z=0..25) = 1.58/9
-
-			double a = NUMsekeyhansonfilter_amplitude (z0, z[j]);
-			p += a * pow[j] ;
-		}
-		thy z[i][frame] = p;
-	}
-	return 1;
-}
-
-BarkFilter Sound_to_BarkFilter (Sound me, double analysisWidth, double dt, double f1_bark, double fmax_bark, double df_bark) {
-	try {
-		double t1, nyquist = 0.5 / my dx, samplingFrequency = 2 * nyquist;
-		double windowDuration = 2 * analysisWidth; /* gaussian window */
-		double zmax = NUMhertzToBark2 (nyquist);
-		double fmin_bark = 0;
-		long nt, frameErrorCount = 0;
-
-		// Check defaults.
-
-		if (f1_bark <= 0) {
-			f1_bark = 1;
-		}
-		if (fmax_bark <= 0) {
-			fmax_bark = zmax;
-		}
-		if (df_bark <= 0) {
-			df_bark = 1;
-		}
-
-		fmax_bark = MIN (fmax_bark, zmax);
-		long nf = floor ( (fmax_bark - f1_bark) / df_bark + 0.5);
-		if (nf <= 0) {
-			Melder_throw ("The combination of filter parameters is not valid.");
-		}
-
-		Sampled_shortTermAnalysis (me, windowDuration, dt, & nt, & t1);
-		autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency);
-		autoSound window = Sound_createGaussian (windowDuration, samplingFrequency);
-		autoBarkFilter thee = BarkFilter_create (my xmin, my xmax, nt, dt, t1,
-		                      fmin_bark, fmax_bark, nf, df_bark, f1_bark);
-
-		autoMelderProgress progess (L"BarkFilter analysis");
-
-		for (long i = 1; i <= nt; i++) {
-			double t = Sampled_indexToX (thee.peek(), i);
-
-			Sound_into_Sound (me, sframe.peek(), t - windowDuration / 2);
-
-			Sounds_multiply (sframe.peek(), window.peek());
-
-			if (! Sound_into_BarkFilter_frame (sframe.peek(), thee.peek(), i)) {
-				frameErrorCount++;
-			}
-
-			if ( (i % 10) == 1) {
-				Melder_progress ( (double) i / nt,  L"BarkFilter analysis: frame ",
-					Melder_integer (i), L" from ", Melder_integer (nt), L".");
-			}
-		}
-
-		if (frameErrorCount > 0) {
-			Melder_warning (L"Analysis results of ", Melder_integer (frameErrorCount), L" frame(s) out of ",
-				Melder_integer (nt), L" will be suspect.");
-		}
-
-		double ref = FilterBank_DBREF * gaussian_window_squared_correction (window -> nx);
-
-		NUMdmatrix_to_dBs (thy z, 1, thy ny, 1, thy nx, ref, FilterBank_DBFAC, FilterBank_DBFLOOR);
-		return thee.transfer();
-	} catch (MelderError) {
-		Melder_throw (me, ": no BarkFilter created.");
-	}
-}
-
-static int Sound_into_MelFilter_frame (Sound me, MelFilter thee, long frame) {
-	autoMatrix pv = Sound_to_spectralpower (me);
-
-	double z1 = pv -> x1;
-	double dz = pv -> dx;
-	long nf = pv -> nx;
-	double df = thy dy;
-	for (long i = 1; i <= thy ny; i++) {
-		double p = 0;
-		double fc_mel = thy y1 + (i - 1) * df;
-		double fc_hz = MELTOHZ (fc_mel);
-		double fl_hz = MELTOHZ (fc_mel - df);
-		double fh_hz =  MELTOHZ (fc_mel + df);
-		double *pow = pv -> z[1];
-		for (long j = 1; j <= nf; j++) {
-			// Bin with a triangular filter the power (=amplitude-squared)
-
-			double f = z1 + (j - 1) * dz;
-			double a = NUMtriangularfilter_amplitude (fl_hz, fc_hz, fh_hz, f);
-			p += a * pow[j];
-		}
-		thy z[i][frame] = p;
-	}
-	return 1;
-}
-
-MelFilter Sound_to_MelFilter (Sound me, double analysisWidth, double dt, double f1_mel, double fmax_mel, double df_mel) {
-	try {
-		double t1, samplingFrequency = 1 / my dx, nyquist = 0.5 * samplingFrequency;
-		double windowDuration = 2 * analysisWidth; /* gaussian window */
-		double fmin_mel = 0;
-		double fbottom = HZTOMEL (100.0), fceiling = HZTOMEL (nyquist);
-		long nt, frameErrorCount = 0;
-
-		// Check defaults.
-
-		if (fmax_mel <= 0 || fmax_mel > fceiling) {
-			fmax_mel = fceiling;
-		}
-		if (fmax_mel <= f1_mel) {
-			f1_mel = fbottom; fmax_mel = fceiling;
-		}
-		if (f1_mel <= 0) {
-			f1_mel = fbottom;
-		}
-		if (df_mel <= 0) {
-			df_mel = 100.0;
-		}
-
-		// Determine the number of filters.
-
-		long nf = floor ((fmax_mel - f1_mel) / df_mel + 0.5);
-		fmax_mel = f1_mel + nf * df_mel;
-
-		Sampled_shortTermAnalysis (me, windowDuration, dt, &nt, &t1);
-		autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency);
-		autoSound window = Sound_createGaussian (windowDuration, samplingFrequency);
-		autoMelFilter thee = MelFilter_create (my xmin, my xmax, nt, dt, t1, fmin_mel, fmax_mel, nf, df_mel, f1_mel);
-
-		autoMelderProgress progress (L"MelFilters analysis");
-
-		for (long i = 1; i <= nt; i++) {
-			double t = Sampled_indexToX (thee.peek(), i);
-			Sound_into_Sound (me, sframe.peek(), t - windowDuration / 2);
-			Sounds_multiply (sframe.peek(), window.peek());
-			if (! Sound_into_MelFilter_frame (sframe.peek(), thee.peek(), i)) {
-				frameErrorCount++;
-			}
-			if ( (i % 10) == 1) {
-				Melder_progress ((double) i / nt, L"Frame ", Melder_integer (i), L" out of ", Melder_integer (nt), L".");
-			}
-		}
-
-		if (frameErrorCount) {
-			Melder_warning (L"Analysis results of ", Melder_integer (frameErrorCount),
-			L" frame(s) out of ", Melder_integer (nt), L" will be suspect.");
-		}
-
-		// Window correction.
-
-		double ref = FilterBank_DBREF * gaussian_window_squared_correction (window -> nx);
-
-		NUMdmatrix_to_dBs (thy z, 1, thy ny, 1, thy nx, ref, FilterBank_DBFAC, FilterBank_DBFLOOR);
-		return thee.transfer();
-	} catch (MelderError) {
-		Melder_throw (me, ": no MelFilter created.");
-	}
-}
-
-/*
-	Analog formant filter response :
-	H(f) = i f B / (f1^2 - f^2 + i f B)
-*/
-static int Sound_into_FormantFilter_frame (Sound me, FormantFilter thee, long frame, double bw) {
-	Melder_assert (bw > 0);
-	autoMatrix pv = Sound_to_spectralpower (me);
-	double z1 = pv -> x1;
-	double dz = pv -> dx;
-	long nf = pv -> nx;
-
-	for (long i = 1; i <= thy ny; i++) {
-		double p = 0;
-		double fc = thy y1 + (i - 1) * thy dy;
-		double *pow = pv -> z[1];
-		for (long j = 1; j <= nf; j++) {
-			// H(f) = ifB / (fc^2 - f^2 + ifB)
-			// H(f)| = fB / sqrt ((fc^2 - f^2)^2 + f^2B^2)
-			//|H(f)|^2 = f^2B^2 / ((fc^2 - f^2)^2 + f^2B^2)
-			//         = 1 / (((fc^2 - f^2) /fB)^2 + 1)
-
-			double f = z1 + (j - 1) * dz;
-			double a = NUMformantfilter_amplitude (fc, bw, f);
-			p += a * pow[j];
-		}
-		thy z[i][frame] = p;
-	}
-	return 1;
-}
-
-FormantFilter Sound_to_FormantFilter (Sound me, double analysisWidth,
-                                      double dt, double f1_hz, double fmax_hz, double df_hz, double relative_bw,
-                                      double minimumPitch, double maximumPitch) {
-	try {
-		double floor = 80, ceiling = 600;
-		if (minimumPitch >= maximumPitch) {
-			minimumPitch = floor; maximumPitch = ceiling;
-		}
-		if (minimumPitch <= 0) {
-			minimumPitch = floor;
-		}
-		if (maximumPitch <= 0) {
-			maximumPitch = ceiling;
-		}
-
-		autoPitch thee = Sound_to_Pitch (me, dt, minimumPitch, maximumPitch);
-		autoFormantFilter ff = Sound_and_Pitch_to_FormantFilter (me, thee.peek(), analysisWidth, dt,
-		                       f1_hz, fmax_hz, df_hz, relative_bw);
-		return ff.transfer();
-	} catch (MelderError) {
-		Melder_throw (me, ": no FormantFilter created.");
-	}
-}
-
-FormantFilter Sound_and_Pitch_to_FormantFilter (Sound me, Pitch thee, double analysisWidth, double dt,
-        double f1_hz, double fmax_hz, double df_hz, double relative_bw) {
-	try {
-		double t1, windowDuration = 2 * analysisWidth; /* gaussian window */
-		double nyquist = 0.5 / my dx, samplingFrequency = 2 * nyquist, fmin_hz = 0;
-		long nt, f0_undefined = 0;
-
-		if (my xmin > thy xmin || my xmax > thy xmax) Melder_throw
-			("The domain of the Sound is not included in the domain of the Pitch.");
-
-		double f0_median = Pitch_getQuantile (thee, thy xmin, thy xmax, 0.5, kPitch_unit_HERTZ);
-
-		if (f0_median == NUMundefined || f0_median == 0) {
-			f0_median = 100;
-			Melder_warning (L"Pitch values undefined. Bandwith fixed to 100 Hz. ");
-		}
-
-		if (f1_hz <= 0) {
-			f1_hz = 100;
-		}
-		if (fmax_hz <= 0) {
-			fmax_hz = nyquist;
-		}
-		if (df_hz <= 0) {
-			df_hz = f0_median / 2;
-		}
-		if (relative_bw <= 0) {
-			relative_bw = 1.1;
-		}
-
-		fmax_hz = MIN (fmax_hz, nyquist);
-		long nf = floor ( (fmax_hz - f1_hz) / df_hz + 0.5);
-
-		Sampled_shortTermAnalysis (me, windowDuration, dt, &nt, &t1);
-		autoFormantFilter him = FormantFilter_create (my xmin, my xmax, nt, dt, t1,
-		                        fmin_hz, fmax_hz, nf, df_hz, f1_hz);
-
-		// Temporary objects
-
-		autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency);
-		autoSound window = Sound_createGaussian (windowDuration, samplingFrequency);
-		autoMelderProgress progress (L"Sound & Pitch: To FormantFilter");
-		for (long i = 1; i <= nt; i++) {
-			double t = Sampled_indexToX (him.peek(), i);
-			double b, f0 = Pitch_getValueAtTime (thee, t, kPitch_unit_HERTZ, 0);
-
-			if (f0 == NUMundefined || f0 == 0) {
-				f0_undefined++; f0 = f0_median;
-			}
-			b = relative_bw * f0;
-			Sound_into_Sound (me, sframe.peek(), t - windowDuration / 2);
-			Sounds_multiply (sframe.peek(), window.peek());
-
-			Sound_into_FormantFilter_frame (sframe.peek(), him.peek(), i, b);
-
-			if ( (i % 10) == 1) {
-				Melder_progress ( (double) i / nt, L"Frame ", Melder_integer (i), L" out of ",
-				                   Melder_integer (nt), L".");
-			}
-		}
-
-		double ref = FilterBank_DBREF * gaussian_window_squared_correction (window -> nx);
-		NUMdmatrix_to_dBs (his z, 1, his ny, 1, his nx, ref, FilterBank_DBFAC, FilterBank_DBFLOOR);
-		return him.transfer();
-	} catch (MelderError) {
-		Melder_throw ("FormantFilter not created from Pitch & FormantFilter.");
-	}
-}
-
-Sound FilterBank_as_Sound (FilterBank me) {
-	try {
-		autoSound thee = Sound_create (my ny, my xmin, my xmax, my nx, my dx, my x1);
-		for (long i = 1; i <= my ny; i++) {
-			for (long j = 1; j <= my nx; j++)
-				thy z[i][j] = my z[i][j];
-		}
-		return thee.transfer();
-	} catch (MelderError) {
-		Melder_throw (me, ": no Sound created.");
-	}
-}
-
-Sound FilterBanks_crossCorrelate (FilterBank me, FilterBank thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain) {
-	try {
-		autoSound cc = Sounds_crossCorrelate ((Sound) me, (Sound) thee, scaling, signalOutsideTimeDomain);
-		return cc.transfer();
-	} catch (MelderError) {
-		Melder_throw (me, " and ", thee, " not cross-correlated.");
-	}
-}
-
-Sound FilterBanks_convolve (FilterBank me, FilterBank thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain) {
-	try {
-		autoSound cc = Sounds_convolve ((Sound) me, (Sound) thee, scaling, signalOutsideTimeDomain);
-		return cc.transfer();
-	} catch (MelderError) {
-		Melder_throw (me, " and ", thee, " not convolved.");
-	}
-}
-
-/* End of file Sound_and_FilterBank.cpp */
diff --git a/dwtools/Sound_and_Spectrogram_extensions.cpp b/dwtools/Sound_and_Spectrogram_extensions.cpp
new file mode 100644
index 0000000..6208c3e
--- /dev/null
+++ b/dwtools/Sound_and_Spectrogram_extensions.cpp
@@ -0,0 +1,397 @@
+/* Sound_and_FilterBank.cpp
+ *
+ * Copyright (C) 1993-2014 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
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ djmw 20010718
+ djmw 20020813 GPL header.
+ djmw 20041124 Changed call to Sound_to_Spectrum.
+ djmw 20070103 Sound interface changes
+ djmw 20071107 Errors/warnings text changes
+ djmw 20071202 Melder_warning<n>
+*/
+
+#include "Sound_and_Spectrogram_extensions.h"
+#include "Sound_extensions.h"
+#include "Sound_and_Spectrum.h"
+#include "Sound_to_Pitch.h"
+#include "Vector.h"
+#include "NUM2.h"
+
+#define MIN(m,n) ((m) < (n) ? (m) : (n))
+// prototypes
+Sound BandFilterSpectrogram_as_Sound (BandFilterSpectrogram me, int to_dB);
+
+/*
+	The gaussian(x) = (exp(-48*((i-(n+1)/2)/(n+1))^2)-exp(-12))/(1-exp(-12));
+	For power we need the area under the square of this window:
+	Integrate (gaussian(i)^2,i=1..n) =
+		(sqrt(Pi)*sqrt(3)*sqrt(2)*erf(2*(n-1)*sqrt(3)*sqrt(2)/(n+1))*(n+1) + 24*exp(-24)*(n-1)+
+		-4*sqrt(Pi)*sqrt(3)*exp(-12)*erf(2*(n-1)*sqrt(3)/(n+1))*(n+1))/ (24 * (-1+exp(-12))^2),
+	where erf(x) = 1 - erfc(x) and n is the windowLength in samples.
+	To compare with the rectangular window we need to divide this by the window width (n -1) x 1^2.
+*/
+static void _Spectrogram_windowCorrection (Spectrogram me, long numberOfSamples_window) {
+	double windowFactor = 1;
+	if (numberOfSamples_window > 1) {
+		double e12 = exp (-12);
+		double denum = (e12 - 1) * (e12 - 1) * 24 * (numberOfSamples_window - 1);
+		double arg1 = 2 * NUMsqrt3 * (numberOfSamples_window - 1) / (numberOfSamples_window + 1);
+		double arg2 = arg1 * NUMsqrt2;
+		double p2 = NUMsqrtpi * NUMsqrt3 * NUMsqrt2 * (1 - NUMerfcc (arg2)) * (numberOfSamples_window + 1);
+		double p1 = 4 * NUMsqrtpi * NUMsqrt3 * e12 * (1 - NUMerfcc (arg1)) * (numberOfSamples_window + 1);
+		windowFactor =  (p2 - p1 + 24 * (numberOfSamples_window - 1) * e12 * e12) / denum;
+	}
+	for (long i = 1; i <= my ny; i++) {
+		for (long j = 1; j <= my nx; j++) {
+			my z[i][j] /= windowFactor;
+		}
+	}
+}
+
+static Spectrum Sound_to_Spectrum_power (Sound me) {
+	try {
+		autoSpectrum thee = Sound_to_Spectrum (me, TRUE);
+		double scale = 2.0 * thy dx / (my xmax - my xmin);
+
+		// factor '2' because we combine positive and negative frequencies
+		// thy dx : width of frequency bin
+		// my xmax - my xmin : duration of sound
+
+		double *re = thy z[1], *im = thy z[2];
+		for (long i = 1; i <= thy nx; i++) {
+			double power = scale * (re[i] * re[i] + im[i] * im [i]);
+			re[i] = power; im[i] = 0;
+		}
+
+		// Correction of frequency bins at 0 Hz and nyquist: don't count for two.
+
+		re[1] *= 0.5; re[thy nx] *= 0.5;
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": no Spectrum with spectral power created.");
+	}
+}
+
+static void Sound_into_BarkSpectrogram_frame (Sound me, BarkSpectrogram thee, long frame) {
+	autoSpectrum him = Sound_to_Spectrum_power (me);
+	long numberOfFrequencies = his nx;
+	autoNUMvector<double> z (1, numberOfFrequencies);
+
+	for (long ifreq = 1; ifreq <= numberOfFrequencies; ifreq++) {
+		double fhz = his x1 + (ifreq - 1) * his dx;
+		z[ifreq] = thy v_hertzToFrequency (fhz);
+	}
+
+	for (long i = 1; i <= thy ny; i++) {
+		double p = 0;
+		double z0 = thy y1 + (i - 1) * thy dy;
+		double *pow = his z[1]; // TODO ??
+		for (long ifreq = 1; ifreq <= numberOfFrequencies; ifreq++) {
+			// Sekey & Hanson filter is defined in the power domain.
+			// We therefore multiply the power with a (and not a^2).
+			// integral (F(z),z=0..25) = 1.58/9
+
+			double a = NUMsekeyhansonfilter_amplitude (z0, z[ifreq]);
+			p += a * pow[ifreq] ;
+		}
+		thy z[i][frame] = p;
+	}
+}
+
+BarkSpectrogram Sound_to_BarkSpectrogram (Sound me, double analysisWidth, double dt, double f1_bark, double fmax_bark, double df_bark) {
+	try {
+		double nyquist = 0.5 / my dx, samplingFrequency = 2 * nyquist;
+		double windowDuration = 2 * analysisWidth; /* gaussian window */
+		double zmax = NUMhertzToBark2 (nyquist);
+		double fmin_bark = 0;
+
+		// Check defaults.
+
+		if (f1_bark <= 0) {
+			f1_bark = 1;
+		}
+		if (fmax_bark <= 0) {
+			fmax_bark = zmax;
+		}
+		if (df_bark <= 0) {
+			df_bark = 1;
+		}
+
+		fmax_bark = MIN (fmax_bark, zmax);
+		long numberOfFilters = floor ( (fmax_bark - f1_bark) / df_bark + 0.5);
+		if (numberOfFilters <= 0) {
+			Melder_throw ("The combination of filter parameters is not valid.");
+		}
+
+		long numberOfFrames; double t1;
+		Sampled_shortTermAnalysis (me, windowDuration, dt, & numberOfFrames, & t1);
+		autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency);
+		autoSound window = Sound_createGaussian (windowDuration, samplingFrequency);
+		autoBarkSpectrogram thee = BarkSpectrogram_create (my xmin, my xmax, numberOfFrames, dt, t1, fmin_bark, fmax_bark, numberOfFilters, df_bark, f1_bark);
+
+		autoMelderProgress progess (L"BarkSpectrogram analysis");
+
+		for (long iframe = 1; iframe <= numberOfFrames; iframe++) {
+			double t = Sampled_indexToX (thee.peek(), iframe);
+
+			Sound_into_Sound (me, sframe.peek(), t - windowDuration / 2);
+			Sounds_multiply (sframe.peek(), window.peek());
+			Sound_into_BarkSpectrogram_frame (sframe.peek(), thee.peek(), iframe);
+
+			if ((iframe % 10) == 1) {
+				Melder_progress ( (double) iframe / numberOfFrames,  L"BarkSpectrogram analysis: frame ",
+					Melder_integer (iframe), L" from ", Melder_integer (numberOfFrames), L".");
+			}
+		}
+		
+		_Spectrogram_windowCorrection ((Spectrogram) thee.peek(), window -> nx);
+
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": no BarkSpectrogram created.");
+	}
+}
+
+static void Sound_into_MelSpectrogram_frame (Sound me, MelSpectrogram thee, long frame) {
+	autoSpectrum him = Sound_to_Spectrum_power (me);
+
+	for (long ifilter = 1; ifilter <= thy ny; ifilter++) {
+		double power = 0;
+		double fc_mel = thy y1 + (ifilter - 1) * thy dy;
+		double fc_hz = thy v_frequencyToHertz (fc_mel);
+		double fl_hz = thy v_frequencyToHertz (fc_mel - thy dy);
+		double fh_hz =  thy v_frequencyToHertz (fc_mel + thy dy);
+		long ifrom, ito;
+		Sampled_getWindowSamples (him.peek(), fl_hz, fh_hz, &ifrom, &ito);
+		for (long i = ifrom; i <= ito; i++) {
+			// Bin with a triangular filter the power (=amplitude-squared)
+
+			double f = his x1 + (i - 1) * his dx;
+			double a = NUMtriangularfilter_amplitude (fl_hz, fc_hz, fh_hz, f);
+			power += a * his z[1][i];
+		}
+		thy z[ifilter][frame] = power;
+	}
+}
+
+MelSpectrogram Sound_to_MelSpectrogram (Sound me, double analysisWidth, double dt, double f1_mel, double fmax_mel, double df_mel) {
+	try {
+		double t1, samplingFrequency = 1 / my dx, nyquist = 0.5 * samplingFrequency;
+		double windowDuration = 2 * analysisWidth; /* gaussian window */
+		double fmin_mel = 0;
+		double fbottom = NUMhertzToMel2 (100.0), fceiling = NUMhertzToMel2 (nyquist);
+		long numberOfFrames;
+
+		// Check defaults.
+
+		if (fmax_mel <= 0 || fmax_mel > fceiling) {
+			fmax_mel = fceiling;
+		}
+		if (fmax_mel <= f1_mel) {
+			f1_mel = fbottom; fmax_mel = fceiling;
+		}
+		if (f1_mel <= 0) {
+			f1_mel = fbottom;
+		}
+		if (df_mel <= 0) {
+			df_mel = 100.0;
+		}
+
+		// Determine the number of filters.
+
+		long numberOfFilters = floor ((fmax_mel - f1_mel) / df_mel + 0.5);
+		fmax_mel = f1_mel + numberOfFilters * df_mel;
+
+		Sampled_shortTermAnalysis (me, windowDuration, dt, &numberOfFrames, &t1);
+		autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency);
+		autoSound window = Sound_createGaussian (windowDuration, samplingFrequency);
+		autoMelSpectrogram thee = MelSpectrogram_create (my xmin, my xmax, numberOfFrames, dt, t1, fmin_mel, fmax_mel, numberOfFilters, df_mel, f1_mel);
+
+		autoMelderProgress progress (L"MelSpectrograms analysis");
+
+		for (long iframe = 1; iframe <= numberOfFrames; iframe++) {
+			double t = Sampled_indexToX (thee.peek(), iframe);
+			Sound_into_Sound (me, sframe.peek(), t - windowDuration / 2);
+			Sounds_multiply (sframe.peek(), window.peek());
+			Sound_into_MelSpectrogram_frame (sframe.peek(), thee.peek(), iframe);
+			
+			if ((iframe % 10) == 1) {
+				Melder_progress ((double) iframe / numberOfFrames, L"Frame ", Melder_integer (iframe), L" out of ", Melder_integer (numberOfFrames), L".");
+			}
+		}
+		
+		_Spectrogram_windowCorrection ((Spectrogram) thee.peek(), window -> nx);
+
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": no MelSpectrogram created.");
+	}
+}
+
+/*
+	Analog formant filter response :
+	H(f) = i f B / (f1^2 - f^2 + i f B)
+*/
+static int Sound_into_Spectrogram_frame (Sound me, Spectrogram thee, long frame, double bw) {
+	Melder_assert (bw > 0);
+	autoSpectrum him = Sound_to_Spectrum_power (me);
+
+	for (long ifilter = 1; ifilter <= thy ny; ifilter++) {
+		double p = 0;
+		double fc = thy y1 + (ifilter - 1) * thy dy;
+		double *pow = his z[1];
+		for (long ifreq = 1; ifreq <= his nx; ifreq++) {
+			// H(f) = ifB / (fc^2 - f^2 + ifB)
+			// H(f)| = fB / sqrt ((fc^2 - f^2)^2 + f^2B^2)
+			//|H(f)|^2 = f^2B^2 / ((fc^2 - f^2)^2 + f^2B^2)
+			//         = 1 / (((fc^2 - f^2) /fB)^2 + 1)
+
+			double f = his x1 + (ifreq - 1) * his dx;
+			double a = NUMformantfilter_amplitude (fc, bw, f);
+			p += a * pow[ifreq];
+		}
+		thy z[ifilter][frame] = p;
+	}
+	return 1;
+}
+
+Spectrogram Sound_to_Spectrogram_pitchDependent (Sound me, double analysisWidth, double dt, double f1_hz, double fmax_hz, double df_hz, double relative_bw, double minimumPitch, double maximumPitch) {
+	try {
+		double floor = 80, ceiling = 600;
+		if (minimumPitch >= maximumPitch) {
+			minimumPitch = floor; maximumPitch = ceiling;
+		}
+		if (minimumPitch <= 0) {
+			minimumPitch = floor;
+		}
+		if (maximumPitch <= 0) {
+			maximumPitch = ceiling;
+		}
+
+		autoPitch thee = Sound_to_Pitch (me, dt, minimumPitch, maximumPitch);
+		autoSpectrogram ff = Sound_and_Pitch_to_Spectrogram (me, thee.peek(), analysisWidth, dt, f1_hz, fmax_hz, df_hz, relative_bw);
+		return ff.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": no Spectrogram created.");
+	}
+}
+
+Spectrogram Sound_and_Pitch_to_Spectrogram (Sound me, Pitch thee, double analysisWidth, double dt, double f1_hz, double fmax_hz, double df_hz, double relative_bw) {
+	try {
+		double t1, windowDuration = 2 * analysisWidth; /* gaussian window */
+		double nyquist = 0.5 / my dx, samplingFrequency = 2 * nyquist, fmin_hz = 0;
+		long numberOfFrames, f0_undefined = 0;
+
+		if (my xmin > thy xmin || my xmax > thy xmax) Melder_throw
+			("The domain of the Sound is not included in the domain of the Pitch.");
+
+		double f0_median = Pitch_getQuantile (thee, thy xmin, thy xmax, 0.5, kPitch_unit_HERTZ);
+
+		if (f0_median == NUMundefined || f0_median == 0) {
+			f0_median = 100;
+			Melder_warning (L"Pitch values undefined. Bandwith fixed to 100 Hz. ");
+		}
+
+		if (f1_hz <= 0) {
+			f1_hz = 100;
+		}
+		if (fmax_hz <= 0) {
+			fmax_hz = nyquist;
+		}
+		if (df_hz <= 0) {
+			df_hz = f0_median / 2;
+		}
+		if (relative_bw <= 0) {
+			relative_bw = 1.1;
+		}
+
+		fmax_hz = MIN (fmax_hz, nyquist);
+		long numberOfFilters = floor ( (fmax_hz - f1_hz) / df_hz + 0.5);
+
+		Sampled_shortTermAnalysis (me, windowDuration, dt, &numberOfFrames, &t1);
+		autoSpectrogram him = Spectrogram_create (my xmin, my xmax, numberOfFrames, dt, t1, fmin_hz, fmax_hz, numberOfFilters, df_hz, f1_hz);
+
+		// Temporary objects
+
+		autoSound sframe = Sound_createSimple (1, windowDuration, samplingFrequency);
+		autoSound window = Sound_createGaussian (windowDuration, samplingFrequency);
+		autoMelderProgress progress (L"Sound & Pitch: To FormantFilter");
+		for (long iframe = 1; iframe <= numberOfFrames; iframe++) {
+			double t = Sampled_indexToX (him.peek(), iframe);
+			double b, f0 = Pitch_getValueAtTime (thee, t, kPitch_unit_HERTZ, 0);
+
+			if (f0 == NUMundefined || f0 == 0) {
+				f0_undefined++; f0 = f0_median;
+			}
+			b = relative_bw * f0;
+			Sound_into_Sound (me, sframe.peek(), t - windowDuration / 2);
+			Sounds_multiply (sframe.peek(), window.peek());
+
+			Sound_into_Spectrogram_frame (sframe.peek(), him.peek(), iframe, b);
+
+			if ((iframe % 10) == 1) {
+				Melder_progress ( (double) iframe / numberOfFrames, L"Frame ", Melder_integer (iframe), L" out of ", 
+					Melder_integer (numberOfFrames), L".");
+			}
+		}
+		
+		_Spectrogram_windowCorrection (him.peek(), window -> nx);
+
+		return him.transfer();
+	} catch (MelderError) {
+		Melder_throw ("FormantFilter not created from Pitch & FormantFilter.");
+	}
+}
+
+Sound BandFilterSpectrogram_as_Sound (BandFilterSpectrogram me, int unit) {
+	try {
+		autoSound thee = Sound_create (my ny, my xmin, my xmax, my nx, my dx, my x1);
+		for (long i = 1; i <= my ny; i++) {
+			for (long j = 1; j <= my nx; j++)
+				thy z[i][j] = my v_getValueAtSample (j, i, unit);
+		}
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": no Sound created.");
+	}
+}
+
+Sound BandFilterSpectrograms_crossCorrelate (BandFilterSpectrogram me, BandFilterSpectrogram thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain) {
+	try {
+		autoSound sme = BandFilterSpectrogram_as_Sound (me, 1) ; // to dB
+		autoSound sthee = BandFilterSpectrogram_as_Sound (thee, 1) ;
+		autoSound cc = Sounds_crossCorrelate (sme.peek(), sthee.peek(), scaling, signalOutsideTimeDomain);
+		return cc.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, " and ", thee, " not cross-correlated.");
+	}
+}
+
+Sound BandFilterSpectrograms_convolve (BandFilterSpectrogram me, BandFilterSpectrogram thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain) {
+	try {
+		autoSound sme = BandFilterSpectrogram_as_Sound (me, 1) ; // to dB
+		autoSound sthee = BandFilterSpectrogram_as_Sound (thee, 1) ;
+		autoSound cc = Sounds_convolve (sme.peek(), sthee.peek(), scaling, signalOutsideTimeDomain);
+		return cc.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, " and ", thee, " not convolved.");
+	}
+}
+
+/* End of file Sound_and_Spectrogram_extensions.cpp */
diff --git a/dwtools/Sound_and_FilterBank.h b/dwtools/Sound_and_Spectrogram_extensions.h
similarity index 58%
rename from dwtools/Sound_and_FilterBank.h
rename to dwtools/Sound_and_Spectrogram_extensions.h
index 9e19bdc..a1917e2 100644
--- a/dwtools/Sound_and_FilterBank.h
+++ b/dwtools/Sound_and_Spectrogram_extensions.h
@@ -1,8 +1,8 @@
-#ifndef _Sound_and_FilterBank_h_
-#define _Sound_and_FilterBank_h_
-/* Sound_and_FilterBank.h
+#ifndef _Sound_and_Spectrogram_extensions_h_
+#define _Sound_and_Spectrogram_extensions_h_
+/* Sound_and_Spectrogram_extensions.h
  *
- * Copyright (C) 1993-2012 David Weenink
+ * Copyright (C) 2014 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
@@ -20,16 +20,14 @@
  */
 
 /*
- djmw 20010404
- djmw 20020813 GPL header
- djmw 20120508 Latest modification
+ djmw 20140914
 */
 
-#include "FilterBank.h"
+#include "Spectrogram_extensions.h"
 #include "Pitch.h"
 #include "Sound.h"
 
-BarkFilter Sound_to_BarkFilter (Sound me, double analysisWidth, double dt,
+BarkSpectrogram Sound_to_BarkSpectrogram (Sound me, double analysisWidth, double dt,
 	double f1_bark, double fmax_bark, double df_bark);
 /*
 	Filtering with filters on a Bark scale as defined by
@@ -40,19 +38,19 @@ BarkFilter Sound_to_BarkFilter (Sound me, double analysisWidth, double dt,
 	10 log F(z) = 15.8 + 7.5(z + 0.5) - 17.5 * sqrt(1 + (z + 0.5)^2)
 */
 
-MelFilter Sound_to_MelFilter (Sound me, double analysisWidth, double dt,
+MelSpectrogram Sound_to_MelSpectrogram (Sound me, double analysisWidth, double dt,
 	double f1_mel, double fmax_mel, double df_mel);
 
-FormantFilter Sound_to_FormantFilter (Sound me, double analysisWidth,
+Spectrogram Sound_to_Spectrogram_pitchDependent (Sound me, double analysisWidth,
 	double dt, double f1_hz, double fmax_hz, double df_hz, double relative_bw,
 	double minimumPitch, double maximumPitch);
 
-FormantFilter Sound_and_Pitch_to_FormantFilter (Sound me, Pitch thee,
+Spectrogram Sound_and_Pitch_to_Spectrogram (Sound me, Pitch thee,
 	double analysisWidth, double dt, double f1_hz, double fmax_hz,
 	double df_hz, double relative_bw);
 
-Sound FilterBanks_crossCorrelate (FilterBank me, FilterBank thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain);
-Sound FilterBanks_convolve (FilterBank me, FilterBank thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain);
+Sound BandFilterSpectrograms_crossCorrelate (BandFilterSpectrogram me, BandFilterSpectrogram thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain);
+Sound BandFilterSpectrograms_convolve (BandFilterSpectrogram me, BandFilterSpectrogram thee, enum kSounds_convolve_scaling scaling, enum kSounds_convolve_signalOutsideTimeDomain signalOutsideTimeDomain);
 
 
-#endif /* _Sound_and_FilterBank_h_ */
+#endif /* _Sound_and_Spectrogram_extensions_h_ */
diff --git a/dwtools/Sound_extensions.cpp b/dwtools/Sound_extensions.cpp
index 205eded..35916af 100644
--- a/dwtools/Sound_extensions.cpp
+++ b/dwtools/Sound_extensions.cpp
@@ -69,11 +69,6 @@
 #include "Manipulation.h"
 #include "NUM2.h"
 
-/*
-#include <pulse/simple.h>
-#include <pulse/error.h>
-*/
-
 #define MAX_T  0.02000000001   /* Maximum interval between two voice pulses (otherwise voiceless). */
 
 static void PitchTier_modifyExcursionRange (PitchTier me, double tmin, double tmax, double multiplier, double fref_Hz) {
@@ -205,7 +200,7 @@ static void u2write (Sound me, FILE *f, int littleEndian, long *nClip) {
 
 static void u2read (Sound me, FILE *f, int littleEndian) {
 	double *s = my z[1];
-	unsigned int (*get) (FILE *) = littleEndian ? bingetu2LE : bingetu2;
+	uint16_t (*get) (FILE *) = littleEndian ? bingetu2LE : bingetu2;
 	for (long i = 1; i <= my nx; i++) {
 		s[i] = get (f) / 32768.0 - 1.0;
 	}
@@ -751,10 +746,10 @@ static void NUMgammatoneFilter4 (double *x, double *y, long n, double centre_fre
 		               Melder_double (centre_frequency), Melder_double (bandwidth),
 		               Melder_double (dt), Melder_double (gain));
 		for (long i = 0; i <= 4; i++) {
-			Melder_casual ("a[%d] = %ls", i, Melder_double (a[i]));
+			Melder_casual ("a[%ld] = %ls", i, Melder_double (a[i]));
 		}
 		for (long i = 0; i <= 8; i++) {
-			Melder_casual ("b[%d] = %ls", i, Melder_double (b[i]));
+			Melder_casual ("b[%ld] = %ls", i, Melder_double (b[i]));
 		}
 	}
 	/*
@@ -2277,25 +2272,4 @@ void Sound_playAsFrequencyShifted (Sound me, double shiftBy, double newSamplingF
 	}
 }
 
-/*
-void Sound_playAsPulse (Sound me, long fromTime, long toTime)  {
-	 static const pa_sample_spec ss = {
-		.format = PA_SAMPLE_S16LE,
-		.rate = 44100,
-		.channels = 2
-	};
-	long ixmin, ixmax, numberOfChannels = my ny > 2 ? 2 : my ny;
-	long numberOfSamples = Sampled_getWindowSamples (me, fromTime, toTime, &ixmin, &ixmax);
-	if (numberOfSamples < 1) {
-		return;
-	}
-	long playBufferSize = 1000 * numberOfChannels, indexInPlaybuffer = 1;
-	autoNUMvector<float> buffer (1, numberOfChannels * playBufferSize);
-	pa_simple *s = NULL;
-	int ret = 1;
-	int error;
-
-}
-*/
-
 /* End of file Sound_extensions.cpp */
diff --git a/dwtools/Sound_to_MFCC.cpp b/dwtools/Sound_to_MFCC.cpp
index 667e772..81f0aa9 100644
--- a/dwtools/Sound_to_MFCC.cpp
+++ b/dwtools/Sound_to_MFCC.cpp
@@ -1,6 +1,6 @@
 /* Sound_to_MFCC.cpp
  *
- * Copyright (C) 1993-2011 David Weenink
+ * Copyright (C) 1993-2011, 2014 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
@@ -23,13 +23,12 @@
 */
 
 #include "Sound_to_MFCC.h"
-#include "Sound_and_FilterBank.h"
+#include "Sound_and_Spectrogram_extensions.h"
 
-MFCC Sound_to_MFCC (Sound me, long numberOfCoefficients, double analysisWidth,
-                    double dt, double f1_mel, double fmax_mel, double df_mel) {
+MFCC Sound_to_MFCC (Sound me, long numberOfCoefficients, double analysisWidth, double dt, double f1_mel, double fmax_mel, double df_mel) {
 	try {
-		autoMelFilter mf = Sound_to_MelFilter (me, analysisWidth, dt, f1_mel, fmax_mel, df_mel);
-		autoMFCC mfcc = MelFilter_to_MFCC (mf.peek(), numberOfCoefficients);
+		autoMelSpectrogram mf = Sound_to_MelSpectrogram (me, analysisWidth, dt, f1_mel, fmax_mel, df_mel);
+		autoMFCC mfcc = MelSpectrogram_to_MFCC (mf.peek(), numberOfCoefficients);
 		return mfcc.transfer();
 	} catch (MelderError) {
 		Melder_throw (me, ": no MFCC created.");
diff --git a/dwtools/Sounds_to_DTW.h b/dwtools/Sounds_to_DTW.h
index 43a901d..c3ae5af 100644
--- a/dwtools/Sounds_to_DTW.h
+++ b/dwtools/Sounds_to_DTW.h
@@ -1,8 +1,8 @@
-#ifndef _Sound_to_DTW_h_
+#ifndef _Sounds_to_DTW_h_
 #define _Sounds_to_DTW_h_
 /* Sounds_to_DTW.h
  *
- * Copyright (C) 2012 David Weenink
+ * Copyright (C) 2012, 2014 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
diff --git a/dwtools/Spectrogram_extensions.cpp b/dwtools/Spectrogram_extensions.cpp
new file mode 100644
index 0000000..3e391c7
--- /dev/null
+++ b/dwtools/Spectrogram_extensions.cpp
@@ -0,0 +1,639 @@
+/* Spectrogram_extensions.cpp
+ *
+ * Copyright (C) 2014 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
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ djmw 20140913
+*/
+
+#include "Eigen_and_Matrix.h"
+#include "Spectrogram_extensions.h"
+#include "Matrix_extensions.h"
+#include "NUM2.h"
+
+Thing_implement (BandFilterSpectrogram, Matrix, 2);
+
+void structBandFilterSpectrogram :: v_info () {
+	structData :: v_info ();
+	MelderInfo_writeLine (L"Time domain:");
+	MelderInfo_writeLine (L"   Start time: ", Melder_double (xmin), L" seconds");
+	MelderInfo_writeLine (L"   End time: ", Melder_double (xmax), L" seconds");
+	MelderInfo_writeLine (L"   Total duration: ", Melder_double (xmax - xmin), L" seconds");
+	MelderInfo_writeLine (L"Time sampling:");
+	MelderInfo_writeLine (L"   Number of time slices (frames): ", Melder_integer (nx));
+	MelderInfo_writeLine (L"   Time step (frame distance): ", Melder_double (dx), L" seconds");
+	MelderInfo_writeLine (L"   First time slice (frame centre) at: ", Melder_double (x1), L" seconds");
+}
+
+void structBarkSpectrogram :: v_info () {
+	structBandFilterSpectrogram :: v_info ();
+	MelderInfo_writeLine (L"Frequency domain:");
+	MelderInfo_writeLine (L"   Lowest frequency: ", Melder_double (ymin), L" ", v_getFrequencyUnit ());
+	MelderInfo_writeLine (L"   Highest frequency: ", Melder_double (ymax), L" ", v_getFrequencyUnit ());
+	MelderInfo_writeLine (L"   Total bandwidth: ", Melder_double (ymax - ymin), L" ", v_getFrequencyUnit ());
+	MelderInfo_writeLine (L"Frequency sampling:");
+	MelderInfo_writeLine (L"   Number of frequency bands (bins): ", Melder_integer (ny));
+	MelderInfo_writeLine (L"   Frequency step (bin width): ", Melder_double (dy), L" ", v_getFrequencyUnit ());
+	MelderInfo_writeLine (L"   First frequency band around (bin centre at): ", Melder_double (y1), L" ", v_getFrequencyUnit ());
+}
+
+void structMelSpectrogram :: v_info () {
+	structBandFilterSpectrogram :: v_info ();
+	MelderInfo_writeLine (L"Frequency domain:");
+	MelderInfo_writeLine (L"   Lowest frequency: ", Melder_double (ymin), L" ", v_getFrequencyUnit ());
+	MelderInfo_writeLine (L"   Highest frequency: ", Melder_double (ymax), L" ", v_getFrequencyUnit ());
+	MelderInfo_writeLine (L"   Total bandwidth: ", Melder_double (ymax - ymin), L" ", v_getFrequencyUnit ());
+	MelderInfo_writeLine (L"Frequency sampling:");
+	MelderInfo_writeLine (L"   Number of frequency bands (bins): ", Melder_integer (ny));
+	MelderInfo_writeLine (L"   Frequency step (bin width): ", Melder_double (dy), L" ", v_getFrequencyUnit ());
+	MelderInfo_writeLine (L"   First frequency band around (bin centre at): ", Melder_double (y1), L" ", v_getFrequencyUnit ());
+}
+
+// Preconditions: 1 <= iframe <= nx; 1 <= irow <= ny
+double structBandFilterSpectrogram :: v_getValueAtSample (long iframe, long ifreq, int units) {
+	double val = NUMundefined;
+	if (units == 0) {
+		val = z[ifreq][iframe];
+	} else if (z[ifreq][iframe] > 0) {
+		val = 10 * log10 (z[ifreq][iframe] / 4e-10); // power values
+	}
+	return val;
+}
+
+Thing_implement (BarkSpectrogram, BandFilterSpectrogram, 1);
+
+// dbs = scaleFactor * log10 (value/reference);
+// if (dbs < floor_db) { dbs = floor_dB }
+Matrix Spectrogram_to_Matrix_dB (Spectrogram me, double reference, double scaleFactor, double floor_dB) {
+	try {
+		autoMatrix thee = Matrix_create (my xmin, my xmax, my nx, my dx, my x1, my ymin, my ymax, my ny, my dy, my y1);
+		for (long i = 1; i <= my ny; i++) {
+			for (long j = 1; j <= my nx; j++) {
+				double val = floor_dB;
+				if (my z[i][j] > 0) {
+					val = scaleFactor * log10 (my z[i][j] / reference);
+				} else if (my z[i][j] < 0) {
+					Melder_throw ("Negative power in Spectrogram.");
+				}
+				if (val < floor_dB) {
+					val = floor_dB;
+				}
+				thy z[i][j] = val;
+			}
+		}
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw ("Matrix with dB values not created.");
+	}
+}
+
+static double **NUMcosinesTable (long  n) {
+	autoNUMmatrix<double> costab (1, n, 1, n);
+	for (long k = 1; k <= n; k++) {
+		for (long j = 1; j <= n; j++) {
+			costab[k][j] = cos (NUMpi * (k - 1) * (j - 0.5) / n);
+		}
+	}
+	return costab.transfer();
+}
+// x[1..n] : input
+// y[1..n] : output
+static void NUMcosineTransform (double *x, double *y, long n, double **cosinesTable) {
+	for (long k = 1; k <= n; k++) {
+		y[k] = 0;
+		for (long j = 1; j <= n; j++) {
+			y[k] += x[j] * cosinesTable[k][j];
+		}
+	}
+}
+
+
+// x: input
+// y: output
+static void NUMinverseCosineTransform (double *x, double *y, long n, double **cosinesTable) {
+	for (long j = 1; j <= n; j++) {
+		y[j] = 0.5 * x[1] * cosinesTable[1][j];
+		for (long k = 2; k <= n; k++) {
+			y[j] += x[k] * cosinesTable[k][j];
+		}
+		y[j] *= 2.0 / n;
+	}
+}
+
+/* Precondition: 1. CC object has been created but individual frames not yest initialized
+ *              2. Domains and number of frames conform
+ * Steps:
+ * 1. transform power-spectra to dB-spectra
+ * 2. cosine transform of dB-spectrum
+*/
+void BandFilterSpectrogram_into_CC (BandFilterSpectrogram me, CC thee, long numberOfCoefficients) {
+	autoNUMmatrix<double> cosinesTable (NUMcosinesTable (my ny), 1, 1);
+	autoNUMvector<double> x (1, my ny);
+	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 
+	for (long frame = 1; frame <= my nx; frame++) {
+		CC_Frame ccframe = (CC_Frame) & thy frame[frame];
+		for (long i = 1; i <= my ny; i++) {
+			x[i] = my v_getValueAtSample (frame, i, 1); // z[i][frame];
+		}
+		NUMcosineTransform (x.peek(), y.peek(), my ny, cosinesTable.peek());
+		CC_Frame_init (ccframe, numberOfCoefficients);
+		for (long i = 1; i <= numberOfCoefficients; i++) {
+			ccframe -> c[i] = y[i + 1];
+		}
+		ccframe -> c0 = y[1];
+	}
+}
+
+// Preconditions: Domains and number of frames conform
+//                0 <= first <= last <= my ny-1
+void CC_into_BandFilterSpectrogram (CC me, BandFilterSpectrogram thee, long first, long last, bool use_c0) {
+	long nf = my maximumNumberOfCoefficients + 1;
+	autoNUMmatrix<double> cosinesTable (NUMcosinesTable (nf), 1, 1);
+	autoNUMvector<double> x (1, nf);
+	autoNUMvector<double> y (1, nf);
+	for (long frame = 1; frame <= my nx; frame++) {
+		CC_Frame ccframe = (CC_Frame) & my frame[frame];
+		long iend = last < ccframe -> numberOfCoefficients ? last : ccframe -> numberOfCoefficients;
+		x[1] = use_c0 ? ccframe -> c0 : 0;
+		for (long i = 1; i <= my maximumNumberOfCoefficients; i++) {
+			x[i + 1] = i < first || i > iend ? 0 : ccframe -> c[i];
+		}
+		NUMinverseCosineTransform (x.peek(), y.peek(), nf, cosinesTable.peek());
+		for (long i = 1; i <= nf; i++) {
+			thy z[i][frame] = BandFilterSpectrogram_DBREF * pow (10, y[i] / BandFilterSpectrogram_DBFAC);
+		}
+	}
+}
+
+MelSpectrogram MFCC_to_MelSpectrogram (MFCC me, long first, long last, bool c0) {
+	try {
+		if (first == 0 && last == 0) { // defaults
+			first = 1; last = my maximumNumberOfCoefficients;
+		}
+		if (first < 1) {
+			first = 1;
+		}
+		if (last > my maximumNumberOfCoefficients) {
+			last = my maximumNumberOfCoefficients;
+		}
+		if (first > last) {
+			first = 1; last = my maximumNumberOfCoefficients;
+		}
+		double df = (my fmax - my fmin) / (my maximumNumberOfCoefficients + 1 + 1);
+		autoMelSpectrogram thee = MelSpectrogram_create (my xmin, my xmax, my nx, my dx, my x1, my fmin, my fmax, my maximumNumberOfCoefficients + 1, df, df);
+		CC_into_BandFilterSpectrogram (me, thee.peek(), first, last, c0);
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, "MelSpectrogram not created.");
+	}
+}
+
+MFCC MelSpectrogram_to_MFCC (MelSpectrogram me, long numberOfCoefficients) {
+	try {
+		if (numberOfCoefficients <= 0) {
+			numberOfCoefficients = my ny - 1;
+		}
+		numberOfCoefficients = numberOfCoefficients > my ny - 1 ? my ny - 1 : numberOfCoefficients;
+		autoMFCC thee = MFCC_create (my xmin, my xmax, my nx, my dx, my x1, my ny - 1, my ymin, my ymax);
+		BandFilterSpectrogram_into_CC (me, thee.peek(), numberOfCoefficients);
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": MFCC not created.");
+	}
+}
+
+BarkSpectrogram BarkSpectrogram_create (double tmin, double tmax, long nt, double dt, double t1, double fmin, double fmax, long nf, double df, long f1) {
+	try {
+		autoBarkSpectrogram me = Thing_new (BarkSpectrogram);
+		Matrix_init (me.peek(), tmin, tmax, nt, dt, t1, fmin, fmax, nf, df, f1);
+		return me.transfer();
+	} catch (MelderError) {
+		Melder_throw ("BarkSpectrogram not created.");
+	}
+}
+
+double BandFilterSpectrogram_getFrequencyInHertz (BandFilterSpectrogram me, double f) {
+	return my v_frequencyToHertz (f);
+}
+
+// xmin, xmax in hz versus bark/mel or lin
+void BandFilterSpectrogram_drawFrequencyScale (BandFilterSpectrogram me, Graphics g, double xmin, double xmax, double ymin, double ymax, int garnish) {
+	if (xmin < 0 || xmax < 0 || ymin < 0 || ymax < 0) {
+		Melder_warning (L"Frequencies must be >= 0.");
+		return;
+	}
+
+	// scale is in hertz
+	if (xmin >= xmax) { // autoscaling
+		xmin = 0;
+		xmax = my v_frequencyToHertz (my ymax);
+	}
+
+	if (ymin >= ymax) { // autoscaling
+		ymin = my ymin;
+		ymax = my ymax;
+	}
+
+	long n = 2000;
+
+	Graphics_setInner (g);
+	Graphics_setWindow (g, xmin, xmax, ymin, ymax);
+
+	double dx = (xmax - xmin) / (n - 1);
+	double x1 = xmin, y1 = my v_hertzToFrequency (x1);
+	for (long i = 2; i <= n;  i++) {
+		double x2 = x1 + dx, y2 = my v_hertzToFrequency (x2);
+		if (NUMdefined (y1) && NUMdefined (y2)) {
+			double xo1, yo1, xo2, yo2;
+			if (NUMclipLineWithinRectangle (x1, y1, x2, y2, xmin, ymin, xmax, ymax, &xo1, &yo1, &xo2, &yo2)) {
+				Graphics_line (g, xo1, yo1, xo2, yo2);
+			}
+		}
+		x1 = x2; y1 = y2;
+	}
+	Graphics_unsetInner (g);
+
+	if (garnish) {
+		Graphics_drawInnerBox (g);
+		Graphics_marksLeft (g, 2, 1, 1, 0);
+		autoMelderString verticalText;
+		MelderString_append (&verticalText, L"Frequency (", my v_getFrequencyUnit (), L")");
+		Graphics_textLeft (g, 1, verticalText.string);
+		Graphics_marksBottom (g, 2, 1, 1, 0);
+		Graphics_textBottom (g, 1, L"Frequency (Hz)");
+	}
+}
+
+void BandFilterSpectrogram_paintImage (BandFilterSpectrogram me, Graphics g, double xmin, double xmax, double ymin, double ymax, double minimum, double maximum, int garnish) {
+	if (xmax <= xmin) {
+		xmin = my xmin; xmax = my xmax; 
+	}
+	if (ymax <= ymin) {
+		ymin = my ymin; ymax = my ymax;
+	}
+	long ixmin, ixmax, iymin, iymax;
+	(void) Matrix_getWindowSamplesX (me, xmin - 0.49999 * my dx, xmax + 0.49999 * my dx, &ixmin, &ixmax);
+	(void) Matrix_getWindowSamplesY (me, ymin - 0.49999 * my dy, ymax + 0.49999 * my dy, &iymin, &iymax);
+	autoMatrix thee = Spectrogram_to_Matrix_dB ((Spectrogram) me, 4e-10, 10, -100);
+	if (maximum <= minimum) {
+		(void) Matrix_getWindowExtrema (thee.peek(), ixmin, ixmax, iymin, iymax, &minimum, &maximum);
+	}
+	if (maximum <= minimum) { 
+		minimum -= 1.0; maximum += 1.0;
+	}
+	if (xmin >= xmax || ymin >= ymax) {
+		return;
+	}
+	Graphics_setInner (g);
+	Graphics_setWindow (g, xmin, xmax, ymin, ymax);
+	Graphics_image (g, thy z,
+			ixmin, ixmax, Sampled_indexToX   (thee.peek(), ixmin - 0.5), Sampled_indexToX   (thee.peek(), ixmax + 0.5),
+			iymin, iymax, SampledXY_indexToY (thee.peek(), iymin - 0.5), SampledXY_indexToY (thee.peek(), iymax + 0.5),
+			minimum, maximum);
+
+	Graphics_unsetInner (g);
+	if (garnish) {
+		Graphics_drawInnerBox (g);
+		Graphics_marksLeft (g, 2, 1, 1, 0);
+		autoMelderString yText;
+		MelderString_append (&yText, L"Frequency (", my v_getFrequencyUnit (), L")");
+		Graphics_textLeft (g, 1, yText.string);
+		Graphics_marksBottom (g, 2, 1, 1, 0);
+		Graphics_textBottom (g, 1, L"Time (s)");
+	}
+}
+
+void BandFilterSpectrogram_drawSpectrumAtNearestTimeSlice (BandFilterSpectrogram me, Graphics g, double time, double fmin, double fmax, double dBmin, double dBmax, int garnish) {
+	if (time < my xmin || time > my xmax) {
+		return;
+	}
+	if (fmin == 0 && fmax == 0) { // autoscaling
+		fmin = my ymin; fmax = my ymax;
+	}
+	if (fmax <= fmin) {
+		fmin = my ymin; fmax = my ymax;
+	}
+	long icol = Matrix_xToNearestColumn (me, time);
+	icol = icol < 1 ? 1 : (icol > my nx ? my nx : icol);
+	autoNUMvector<double> spectrum (1, my ny);
+	for (long i = 1; i <= my ny; i++) {
+		spectrum[i] = my v_getValueAtSample (icol, i, 1); // dB's
+	}
+	long iymin, iymax;
+	if (Matrix_getWindowSamplesY (me, fmin, fmax, &iymin, &iymax) < 2) { // too few values
+		return;
+	}
+	if (dBmin == dBmax) { // autoscaling
+		dBmin = spectrum[iymin]; dBmax = dBmin;
+		for (long i = iymin + 1; i <= iymax; i++) {
+			if (spectrum[i] < dBmin) {
+				dBmin = spectrum[i];
+			} else if (spectrum[i] > dBmax) {
+				dBmax = spectrum[i];
+			}
+		}
+		if (dBmin == dBmax) { 
+			dBmin -= 1; dBmax += 1;
+		}
+	}
+	Graphics_setWindow (g, fmin, fmax, dBmin, dBmax);
+	Graphics_setInner (g);
+
+	double x1 = my y1 + (iymin -1) * my dy, y1 = spectrum[iymin];
+	for (long i = iymin + 1; i <= iymax - 1; i++) {
+		double x2 = my y1 + (i -1) * my dy, y2 = spectrum[i];
+		double xo1, yo1, xo2, yo2;
+		if (NUMclipLineWithinRectangle (x1, y1, x2, y2, fmin, dBmin, fmax, dBmax, &xo1, &yo1, &xo2, &yo2)) {
+			Graphics_line (g, xo1, yo1, xo2, yo2);
+		}
+		x1 = x2; y1 = y2;
+	}
+	Graphics_unsetInner (g);
+
+	if (garnish) {
+		Graphics_drawInnerBox (g);
+		Graphics_marksBottom (g, 2, 1, 1, 0);
+		Graphics_marksLeft (g, 2, 1, 1, 0);
+		Graphics_textLeft (g, 1, L"Power (dB)");
+		autoMelderString xText;
+		MelderString_append (&xText, L"Frequency (", my v_getFrequencyUnit (), L")");
+		Graphics_textBottom (g, 1, xText.string);
+	}
+}
+
+void BarkSpectrogram_drawSekeyHansonFilterFunctions (BarkSpectrogram me, Graphics g, bool xIsHertz, int fromFilter, int toFilter, double zmin, double zmax, bool yscale_dB, double ymin, double ymax, int garnish) {
+	double xmin = zmin, xmax = zmax;
+	if (zmin >= zmax) {
+		zmin = my ymin; zmax = my ymax;
+		xmin = xIsHertz ? my v_frequencyToHertz (zmin) : zmin;
+		xmax = xIsHertz ? my v_frequencyToHertz (zmax) : zmax;
+	}
+	if (xIsHertz) {
+		zmin = my v_hertzToFrequency (xmin); zmax = my v_hertzToFrequency (xmax);
+	}
+	if (ymin >= ymax) {
+		ymin = yscale_dB ? -60 : 0;
+		ymax = yscale_dB ? 0 : 1;
+	}
+	fromFilter = fromFilter <= 0 ? 1 : fromFilter;
+	toFilter = toFilter <= 0 || toFilter > my ny ? my ny : toFilter;
+	if (fromFilter > toFilter) {
+		fromFilter = 1; toFilter = my ny;
+	}
+	long n = xIsHertz ? 1000 : 500;
+	autoNUMvector<double> xz (1, n), xhz (1,n), y (1, n);
+
+	Graphics_setInner (g);
+	Graphics_setWindow (g, xmin, xmax, ymin, ymax);
+
+	double dz = (zmax - zmin) / (n - 1);
+	for (long iz = 1; iz <= n; iz++) {
+		double f = zmin + (iz - 1) * dz;
+		xz[iz] = f;
+		xhz[iz] = my v_frequencyToHertz (f); // just in case we need the linear scale
+	}
+	for (long ifilter = fromFilter; ifilter <= toFilter; ifilter++) {
+		double zMid = Matrix_rowToY (me, ifilter);
+		for (long iz = 1; iz <= n; iz++) {
+			double z = xz[iz] - (zMid - 0.215);
+			double amp = 7 - 7.5 * z - 17.5 * sqrt (0.196 + z * z);
+			y[iz] = yscale_dB ? amp : pow (10, amp / 10);
+		}
+		// the drawing
+		double x1 = xIsHertz ? xhz[1] : xz[1], y1 = y[1];
+		for (long iz = 2; iz <= n; iz++) {
+			double x2 = xIsHertz ? xhz[iz] : xz[iz], y2 = y[iz];
+			if (NUMdefined (x1) && NUMdefined (x2)) {
+				double xo1, yo1, xo2, yo2;
+				if (NUMclipLineWithinRectangle (x1, y1, x2, y2, xmin, ymin, xmax, ymax, &xo1, &yo1, &xo2, &yo2)) {
+					Graphics_line (g, xo1, yo1, xo2, yo2);
+				}
+			}
+			x1 = x2; y1 = y2;
+		}
+	}	
+	Graphics_unsetInner (g);
+
+	if (garnish) {
+		double distance = yscale_dB ? 10 : 0.5;
+		Graphics_drawInnerBox (g);
+		Graphics_marksBottom (g, 2, 1, 1, 0);
+		Graphics_marksLeftEvery (g, 1, distance, 1, 1, 0);
+		Graphics_textLeft (g, 1, yscale_dB ? L"Amplitude (dB)" : L"Amplitude");
+		autoMelderString xText;
+		MelderString_append (&xText, L"Frequency (", xIsHertz ? L"Hz" : my v_getFrequencyUnit (), L")");
+		Graphics_textBottom (g, 1, xText.string);
+	}
+}
+
+Thing_implement (MelSpectrogram, BandFilterSpectrogram, 2);
+
+MelSpectrogram MelSpectrogram_create (double tmin, double tmax, long nt, double dt, double t1, double fmin, double fmax, long nf, double df, double f1) {
+	try {
+		autoMelSpectrogram me = Thing_new (MelSpectrogram);
+		Matrix_init (me.peek(), tmin, tmax, nt, dt, t1, fmin, fmax, nf, df, f1);
+		return me.transfer();
+	} catch (MelderError) {
+		Melder_throw ("MelSpectrogram not created.");
+	}
+}
+
+void BandFilterSpectrogram_drawTimeSlice (I, Graphics g, double t, double fmin,
+                               double fmax, double min, double max, const wchar_t *xlabel, int garnish) {
+	iam (Matrix);
+	Matrix_drawSliceY (me, g, t, fmin, fmax, min, max);
+	if (garnish) {
+		Graphics_drawInnerBox (g);
+		Graphics_marksBottom (g, 2, 1, 1, 0);
+		Graphics_marksLeft (g, 2, 1, 1, 0);
+		if (xlabel) {
+			Graphics_textBottom (g, 0, xlabel);
+		}
+	}
+}
+
+void MelSpectrogram_drawTriangularFilterFunctions (MelSpectrogram me, Graphics g, bool xIsHertz, int fromFilter, int toFilter, double zmin, double zmax, bool yscale_dB, double ymin, double ymax, int garnish) {
+	double xmin = zmin, xmax = zmax;
+	if (zmin >= zmax) {
+		zmin = my ymin; zmax = my ymax; // mel
+		xmin = xIsHertz ? my v_frequencyToHertz (zmin) : zmin;
+		xmax = xIsHertz ? my v_frequencyToHertz (zmax) : zmax;
+	}
+	if (xIsHertz) {
+		zmin = my v_hertzToFrequency (xmin); zmax = my v_hertzToFrequency (xmax);
+	}
+
+	if (ymin >= ymax) {
+		ymin = yscale_dB ? -60 : 0;
+		ymax = yscale_dB ? 0 : 1;
+	}
+	fromFilter = fromFilter <= 0 ? 1 : fromFilter;
+	toFilter = toFilter <= 0 || toFilter > my ny ? my ny : toFilter;
+	if (fromFilter > toFilter) {
+		fromFilter = 1; toFilter = my ny;
+	}
+	
+	long n = xIsHertz ? 1000 : 500;
+	autoNUMvector<double> xz (1, n), xhz (1,n), y (1, n);
+
+	Graphics_setInner (g);
+	Graphics_setWindow (g, xmin, xmax, ymin, ymax);
+	
+	double dz = (zmax - zmin) / (n - 1);
+	for (long iz = 1; iz <= n; iz++) {
+		double f = zmin + (iz - 1) * dz;
+		xz[iz] = f;
+		xhz[iz] = my v_frequencyToHertz (f); // just in case we need the linear scale
+	}
+	
+	for (long ifilter = fromFilter; ifilter <= toFilter; ifilter++) {
+		double zc = Matrix_rowToY (me, ifilter), zl = zc - my dy, zh = zc + my dy;
+		double xo1, yo1, xo2, yo2;
+		if (yscale_dB) {
+			for (long iz = 1; iz <= n; iz++) {
+				double z = xz[iz];
+				double amp = NUMtriangularfilter_amplitude (zl, zc, zh, z);
+				y[iz] = yscale_dB ? (amp > 0 ? 20 * log10 (amp) : ymin - 10) : amp;
+			}
+			double x1 = xIsHertz ? xhz[1] : xz[1], y1 = y[1];
+			if (NUMdefined (y1)) {
+				for (long iz = 1; iz <= n; iz++) {
+					double x2 = xIsHertz ? xhz[iz] : xz[iz], y2 = y[iz];
+					if (NUMdefined (y2)) {
+						if (NUMclipLineWithinRectangle (x1, y1, x2, y2, xmin, ymin, xmax, ymax, &xo1, &yo1, &xo2, &yo2)) {
+							Graphics_line (g, xo1, yo1, xo2, yo2);
+						}
+					}
+					x1 = x2; y1 = y2;
+				}
+			}
+		} else {
+			double x1 = xIsHertz ? my v_frequencyToHertz (zl) : zl;
+			double x2 = xIsHertz ? my v_frequencyToHertz (zc) : zc;
+			if (NUMclipLineWithinRectangle (x1, 0, x2, 1, xmin, ymin, xmax, ymax, &xo1, &yo1, &xo2, &yo2)) {
+				Graphics_line (g, xo1, yo1, xo2, yo2);
+			}
+			double x3 = xIsHertz ? my v_frequencyToHertz (zh) : zh;
+			if (NUMclipLineWithinRectangle (x2, 1, x3, 0, xmin, ymin, xmax, ymax, &xo1, &yo1, &xo2, &yo2)) {
+				Graphics_line (g, xo1, yo1, xo2, yo2);
+			}
+		}
+	}
+
+	Graphics_unsetInner (g);
+
+	if (garnish) {
+		Graphics_drawInnerBox (g);
+		Graphics_marksBottom (g, 2, 1, 1, 0);
+		Graphics_marksLeftEvery (g, 1, yscale_dB ? 10 : 0.5, 1, 1, 0);
+		Graphics_textLeft (g, 1, yscale_dB ? L"Amplitude (dB)" : L"Amplitude");
+		autoMelderString bottomText;
+		MelderString_append (&bottomText, L"Frequency (", (xIsHertz ? L"Hz" : my v_getFrequencyUnit ()), L")");
+		Graphics_textBottom (g, 1, bottomText.string);
+	}
+}
+
+Matrix BandFilterSpectrogram_to_Matrix (BandFilterSpectrogram me, int to_dB) {
+	try {
+		int units = to_dB ? 1 : 0;
+		autoMatrix thee = Matrix_create (my xmin, my xmax, my nx, my dx, my x1, my ymin, my ymax, my ny, my dy, my y1);
+		for (long i = 1; i <= my ny; i++) {
+			for (long j = 1; j <= my nx; j++) {
+				thy z[i][j] = my v_getValueAtSample (j, i, units);
+			}
+		}
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": not converted to Matrix.");
+	}
+}
+
+BarkSpectrogram Matrix_to_BarkSpectrogram (I) {
+	iam (Matrix);
+	try {
+		autoBarkSpectrogram thee = BarkSpectrogram_create (my xmin, my xmax, my nx, my dx, my x1,
+			my ymin, my ymax, my ny, my dy, my y1);
+		NUMmatrix_copyElements (my z, thy z, 1, my ny, 1, my nx);
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": not converted to BarkSpectrogram.");
+	}
+}
+
+MelSpectrogram Matrix_to_MelSpectrogram (Matrix me) {
+	try {
+		autoMelSpectrogram thee = MelSpectrogram_create (my xmin, my xmax, my nx, my dx, my x1, my ymin, my ymax, my ny, my dy, my y1);
+		NUMmatrix_copyElements (my z, thy z, 1, my ny, 1, my nx);
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": not converted to MelSpectrogram.");
+	}
+}
+
+Intensity BandFilterSpectrogram_to_Intensity (BandFilterSpectrogram me) {
+	try {
+		autoIntensity thee = Intensity_create (my xmin, my xmax, my nx, my dx, my x1);
+		for (long j = 1; j <= my nx; j++) {
+			double p = 0;
+			for (long i = 1; i <= my ny; i++) {
+				p += my z[i][j]; // we add power
+			}
+			thy z[1][j] = BandFilterSpectrogram_DBFAC * log10 (p / BandFilterSpectrogram_DBREF);
+		}
+		return thee.transfer();
+	} catch (MelderError) {
+		Melder_throw (me, ": Intensity not created.");
+	}
+}
+
+void BandFilterSpectrogram_equalizeIntensities (BandFilterSpectrogram me, double intensity_db) {
+	for (long j = 1; j <= my nx; j++) {
+		double p = 0;
+		for (long i = 1; i <= my ny; i++) {
+			p += my z[i][j];
+		}
+		double delta_db = intensity_db - BandFilterSpectrogram_DBFAC * log10 (p / BandFilterSpectrogram_DBREF);
+		double factor = pow (10, delta_db / 10);
+		for (long i = 1; i <= my ny; i++) {
+			my z[i][j] *= factor;
+		}
+	}
+}
+
+void BandFilterSpectrogram_and_PCA_drawComponent (BandFilterSpectrogram me, PCA thee, Graphics g, long component, double dblevel,
+                                       double frequencyOffset, double scale, double tmin, double tmax, double fmin, double fmax) {
+	if (component < 1 || component > thy numberOfEigenvalues) {
+		Melder_throw ("Component too large.");
+	}
+
+	// Scale Intensity
+
+	autoBandFilterSpectrogram fcopy = (BandFilterSpectrogram) Data_copy (me);
+	BandFilterSpectrogram_equalizeIntensities (fcopy.peek(), dblevel);
+	autoMatrix mdb = Spectrogram_to_Matrix_dB ((Spectrogram) fcopy.peek(), BandFilterSpectrogram_DBREF, BandFilterSpectrogram_DBFAC, BandFilterSpectrogram_DBFLOOR);
+	autoMatrix him = Eigen_and_Matrix_project (thee, mdb.peek(), component);
+	for (long j = 1; j <= my nx; j++) {
+		his z[component][j] = frequencyOffset + scale * his z[component][j];
+	}
+	Matrix_drawRows (him.peek(), g, tmin, tmax, component - 0.5, component + 0.5, fmin, fmax);
+}
+
+/* End of file Spectrogram_extensions.cpp */
diff --git a/dwtools/Spectrogram_extensions.h b/dwtools/Spectrogram_extensions.h
new file mode 100644
index 0000000..c294148
--- /dev/null
+++ b/dwtools/Spectrogram_extensions.h
@@ -0,0 +1,134 @@
+#ifndef _Spectrogram_extensions_h_
+#define _Spectrogram_extensions_h_
+/* Spectrogram_extensions.h
+ *
+ * Copyright (C) 2014 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
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ djmw 20140913
+*/
+
+#include "Intensity.h"
+#include "MFCC.h"
+#include "PCA.h"
+#include "Spectrogram.h"
+#include "Spectrum.h"
+#include "TableOfReal.h"
+#include "NUM2.h"
+
+#define HZTOBARK(x) NUMhertzToBark2(x)
+#define HZTOMEL(x)	NUMhertzToMel2(x)
+#define BARKTOHZ(x) NUMbarkToHertz2(x)
+#define MELTOHZ(x)	NUMmelToHertz2(x)
+
+#define BARKTOMEL(x) HZTOMEL(BARKTOHZ(x))
+#define MELTOBARK(x) HZTOBARK(MELTOHZ(x))
+
+#define BandFilterSpectrogram_DBREF 4e-10
+#define BandFilterSpectrogram_DBFAC 10
+#define BandFilterSpectrogram_DBFLOOR -100
+
+#define BandFilterSpectrogram_HERTZ 1
+#define BandFilterSpectrogram_BARK  2
+#define BandFilterSpectrogram_MEL   3
+
+Thing_define (BandFilterSpectrogram, Matrix) {
+	// new methods:
+	public:
+		virtual void v_info ();
+		virtual double v_getValueAtSample (long icol, long irow, int units);
+		virtual double v_frequencyToHertz (double f) { return f; }
+		virtual double v_hertzToFrequency (double hertz) { return hertz; }
+		virtual const wchar_t *v_getFrequencyUnit () { return L"Hz"; }
+};
+
+Thing_define (BarkSpectrogram, BandFilterSpectrogram) {
+	// overridden methods:
+	public:
+		virtual void v_info ();
+		virtual double v_frequencyToHertz (double f) { return NUMbarkToHertz2 (f); }
+		virtual double v_hertzToFrequency (double hertz) { return NUMhertzToBark2 (hertz); }
+		virtual const wchar_t *v_getFrequencyUnit () { return L"bark"; }
+};
+
+Thing_define (MelSpectrogram, BandFilterSpectrogram) {
+	// overridden methods:
+	public:
+		virtual void v_info ();
+		virtual double v_frequencyToHertz (double f) { return NUMmelToHertz2 (f); }
+		virtual double v_hertzToFrequency (double hertz) { return NUMhertzToMel2 (hertz); }
+		virtual const wchar_t *v_getFrequencyUnit () { return L"mel"; }
+};
+
+/*
+Interpretation:
+	xmin, xmax, x1, dx, nx like Sampled.
+	ymin, ymax lowest and highest frequencies in Barks / Mel.
+    y1 mid of first filter (bark/mel).
+    dy distance between filters (bark/mel).
+    ny the number of filters.
+ */
+
+double BandFilterSpectrogram_getFrequencyInHertz (BandFilterSpectrogram me, double f);
+
+void BandFilterSpectrogram_equalizeIntensities (BandFilterSpectrogram me, double intensity_db);
+
+Matrix BandFilterSpectrogram_to_Matrix (BandFilterSpectrogram me, int to_dB);
+
+Intensity BandFilterSpectrogram_to_Intensity (BandFilterSpectrogram me);
+
+void BandFilterSpectrogram_drawFrequencyScale (BandFilterSpectrogram me, Graphics g, double xmin, double xmax, double ymin, double ymax, int garnish);
+
+void BarkSpectrogram_drawSekeyHansonFilterFunctions (BarkSpectrogram me, Graphics g, bool xIsHertz, int fromFilter, int toFilter, double zmin, double zmax, bool yscale_dB, double ymin, double ymax, int garnish);
+
+void BandFilterSpectrogram_drawSpectrumAtNearestTimeSlice (BandFilterSpectrogram me, Graphics g, double time, double fmin, double fmax, double dBmin, double dBmax, int garnish);
+
+void BandFilterSpectrogram_paintImage (BandFilterSpectrogram me, Graphics g, double xmin, double xmax, double ymin, double ymax, double minimum, double maximum, int garnish);
+
+BarkSpectrogram BarkSpectrogram_create (double tmin, double tmax, long nt, double dt,
+	double t1, double fmin, double fmax, long nf, double df, long f1);
+
+BarkSpectrogram Matrix_to_BarkSpectrogram (Matrix me);
+
+/*
+Interpretation:
+	xmin, xmax, x1, dx, nx like Sampled.
+	ymin, ymax lowest and highest frequencies in mels.
+    y1 mid of first filter (mels).
+    dy distance between filters (mel).
+    ny the number of filters.
+ */
+
+MelSpectrogram MelSpectrogram_create (double tmin, double tmax, long nt, double dt,
+	double t1, double fmin, double fmax, long nf, double df, double f1);
+
+MelSpectrogram Matrix_to_MelSpectrogram (Matrix me);
+
+void MelSpectrogram_drawTriangularFilterFunctions (MelSpectrogram me, Graphics g, bool xIsHertz, int fromFilter, int toFilter, double zmin, double zmax, bool yscale_dB, double ymin, double ymax, int garnish);
+
+MFCC MelSpectrogram_to_MFCC (MelSpectrogram me, long numberOfCoefficients);
+MelSpectrogram MFCC_to_MelSpectrogram (MFCC me, long first, long last, bool c0);
+
+void BandFilterSpectrogram_and_PCA_drawComponent (BandFilterSpectrogram me, PCA thee, Graphics g, long component, double dblevel,
+	double frequencyOffset, double scale, double tmin, double tmax, double fmin, double fmax);
+
+Matrix Spectrogram_to_Matrix_dB (Spectrogram me, double reference, double scaleFactor, double floor_dB);
+// dbs = scaleFactor * log10 (value/reference);
+// if (dbs < floor_db) { dbs = floor_dB }
+
+#endif /* _Spectrogram_extensions_h_ */
diff --git a/dwtools/TableOfReal_extensions.cpp b/dwtools/TableOfReal_extensions.cpp
index bce5824..5e78281 100644
--- a/dwtools/TableOfReal_extensions.cpp
+++ b/dwtools/TableOfReal_extensions.cpp
@@ -1,6 +1,6 @@
 /* TableOfReal_extensions.cpp
  *
- * Copyright (C) 1993-2012 David Weenink
+ * Copyright (C) 1993-2012, 2014 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
@@ -74,6 +74,26 @@
 TableOfReal TableOfReal_and_TableOfReal_columnCorrelations (I, thou, int center, int normalize);
 TableOfReal TableOfReal_and_TableOfReal_rowCorrelations (I, thou, int center, int normalize);
 
+long TableOfReal_getColumnIndexAtMaximumInRow (TableOfReal me, long rowNumber) {
+	long columnNumber = 0;
+	if (rowNumber > 0 && rowNumber <= my numberOfRows) {
+		double max = my data[rowNumber][1];
+		columnNumber = 1;
+		for (long icol = 2; icol <= my numberOfColumns; icol++) {
+			if (my data[rowNumber][icol] > max) {
+				max = my data[rowNumber][icol]; columnNumber = icol;
+			}
+		}
+	}
+	return columnNumber;
+}
+
+const wchar_t *TableOfReal_getColumnLabelAtMaximumInRow (TableOfReal me, long rowNumber) {
+	long columnNumber = TableOfReal_getColumnIndexAtMaximumInRow (me, rowNumber);
+	return my v_getColStr (columnNumber);
+}
+
+
 int TableOfReal_areAllCellsDefined (I, long rb, long re, long cb, long ce) {
 	iam (TableOfReal);
 
diff --git a/dwtools/TableOfReal_extensions.h b/dwtools/TableOfReal_extensions.h
index 1a2214e..332f1e5 100644
--- a/dwtools/TableOfReal_extensions.h
+++ b/dwtools/TableOfReal_extensions.h
@@ -2,7 +2,7 @@
 #define _TableOfReal_extensions_h_
 /* TableOfReal_extensions.h
  *
- * Copyright (C) 1993-2012 David Weenink
+ * Copyright (C) 1993-2012, 2014 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
@@ -61,6 +61,8 @@ TableOfReal TableOfReal_createFromVanNieropData_25females (int include_levels);
 TableOfReal TableOfReal_createFromWeeninkData (int option); /* M W C */
 
 void TableOfReal_getColumnExtrema (I, long col, double *min, double *max);
+long TableOfReal_getColumnIndexAtMaximumInRow (TableOfReal me, long rowNumber);
+const wchar_t *TableOfReal_getColumnLabelAtMaximumInRow (TableOfReal me, long rowNumber);
 
 void TableOfReal_drawRowsAsHistogram (I, Graphics g, const wchar_t *rows, long colb, long cole,
 	double ymin, double ymax, double xoffsetFraction, double interbarFraction,
diff --git a/dwtools/TextGrid_extensions.cpp b/dwtools/TextGrid_extensions.cpp
index b435fa4..d3c2cf2 100644
--- a/dwtools/TextGrid_extensions.cpp
+++ b/dwtools/TextGrid_extensions.cpp
@@ -774,13 +774,13 @@ void TextGrids_append_inline (TextGrid me, TextGrid thee, bool preserveTimes)
 TextGrid TextGrids_to_TextGrid_appendContinuous (Collection me, bool preserveTimes) {
 	try {
 		if (my size == 1) {
-			return (TextGrid) Data_copy ((Data) my item[1]);
+			return Data_copy ((TextGrid) my item[1]);
 		}
-		autoTextGrid thee = (TextGrid) Data_copy ((Data) my item[1]);
+		autoTextGrid thee = Data_copy ((TextGrid) my item[1]);
 		for (long igrid = 2; igrid <= my size; igrid++) {
 			TextGrids_append_inline (thee.peek(), (TextGrid) my item[igrid], preserveTimes);
 		}
-		if (not preserveTimes) Function_shiftXBy ((Function) thee.peek(), -thy xmin);
+		if (not preserveTimes) Function_shiftXBy (thee.peek(), -thy xmin);
 		return thee.transfer();
 	} catch (MelderError) {
 		Melder_throw ("No aligned TextGrid created from Collection.");
diff --git a/dwtools/VowelEditor.cpp b/dwtools/VowelEditor.cpp
index e749693..7703695 100644
--- a/dwtools/VowelEditor.cpp
+++ b/dwtools/VowelEditor.cpp
@@ -489,6 +489,7 @@ static void FormantTier_drawF1F2Trajectory (FormantTier me, Graphics g, double f
 	double glw = Graphics_inqLineWidth (g), x1, x1p, y1, y1p, t1;
 	Graphics_Colour colour = Graphics_inqColour (g);
 	long nfp = my points -> size;
+	trace ("number of points %ld", nfp);
 	FormantPoint fp = (FormantPoint) my points -> item[1];
 	FormantPoint fpn = (FormantPoint) my points -> item[nfp];
 	double tm, markLength = 0.01;
@@ -515,19 +516,22 @@ static void FormantTier_drawF1F2Trajectory (FormantTier me, Graphics g, double f
 			double fraction = (tm - t1) / (t2 - t1);
 			double dx = x2 - x1, dy = y2 - y1;
 			double xm = x1 + fraction * dx, ym = y1 + fraction * dy;
-			double xl1 = dy * markLength / sqrt (dx * dx + dy * dy), xl2 = - xl1;
-			double yl1 = dx * markLength / sqrt (dx * dx + dy * dy), yl2 = - yl1;
-
-			if (dx * dy > 0) {
-				xl1 = -fabs (xl1); yl1 = fabs (yl1);
-				xl2 = fabs (xl1); yl2 = -fabs (yl1);
-			} else if (dx * dy < 0) {
-				xl1 = -fabs (xl1); yl1 = -fabs (yl1);
-				xl2 = fabs (xl1); yl2 = fabs (yl1);
+			double d = sqrt (dx * dx + dy * dy);
+			if (d > 0.0) {
+				double xl1 = dy * markLength / d, xl2 = - xl1;
+				double yl1 = dx * markLength / d, yl2 = - yl1;
+
+				if (dx * dy > 0) {
+					xl1 = -fabs (xl1); yl1 = fabs (yl1);
+					xl2 = fabs (xl1); yl2 = -fabs (yl1);
+				} else if (dx * dy < 0) {
+					xl1 = -fabs (xl1); yl1 = -fabs (yl1);
+					xl2 = fabs (xl1); yl2 = fabs (yl1);
+				}
+				Graphics_setLineWidth (g, 1);
+				trace ("%.17g %.17g %.17g %.17g %.17g %.17g", xm, ym, xl1, xl2, yl1, yl2);
+				Graphics_line (g, xm + xl1, ym + yl1, xm + xl2, ym + yl2);
 			}
-			Graphics_setLineWidth (g, 1);
-			Graphics_line (g, xm + xl1, ym + yl1, xm + xl2, ym + yl2);
-
 			imark++;
 		}
 		x1p = x1; y1p = y1;
@@ -1281,9 +1285,12 @@ static void gui_button_cb_reverse (I, GuiButtonEvent event) {
 /* Main drawing routine: it's been called after every call to Graphics_updateWs (g) */
 static void gui_drawingarea_cb_expose (I, GuiDrawingAreaExposeEvent event) {
 	iam (VowelEditor);
+	Melder_assert (me != NULL);
 	(void) event;
+	Melder_assert (my vowel != NULL);
 	double ts = my vowel -> xmin, te = my vowel -> xmax;
 	FormantTier ft = (FormantTier) my vowel -> ft;
+	Melder_assert (ft != NULL);
 	static MelderString statusInfo = { 0 };
 	if (my g == 0) {
 		return;    // Could be the case in the very beginning.
@@ -1310,6 +1317,9 @@ static void gui_drawingarea_cb_expose (I, GuiDrawingAreaExposeEvent event) {
 	Graphics_setGrey (my g, 0);
 
 	VowelEditor_drawBackground (me, my g);
+	Melder_assert (me != NULL);
+	Melder_assert (my vowel != NULL);
+	Melder_assert (my vowel -> ft != NULL);
 	FormantTier_drawF1F2Trajectory (my vowel -> ft, my g, my f1min, my f1max, my f2min, my f2max, my markTraceEvery, my width);
 }
 
@@ -1330,6 +1340,7 @@ static void gui_drawingarea_cb_resize__ (I, GuiDrawingAreaResizeEvent event) {
 
 static void gui_drawingarea_cb_resize (I, GuiDrawingAreaResizeEvent event) {
 	iam (VowelEditor);
+	Melder_assert (me != NULL);
 	if (my g == NULL) {
 		return;    // Could be the case in the very beginning.
 	}
@@ -1603,12 +1614,15 @@ static Sound VowelEditor_createTarget (VowelEditor me) {
 
 VowelEditor VowelEditor_create (const wchar_t *title, Data data) {
 	try {
+		trace ("enter");
 		autoVowelEditor me = Thing_new (VowelEditor);
+		Melder_assert (me.peek() != NULL);
 		Editor_init (me.peek(), 0, 0, prefs.shellWidth, prefs.shellHeight, title, data);
 #if motif
 		Melder_assert (XtWindow (my drawingArea -> d_widget));
 #endif
 		my g = Graphics_create_xmdrawingarea (my drawingArea);
+		Melder_assert (my g != NULL);
 		Graphics_setFontSize (my g, 12);
 		my setPublicationCallback (cb_publish, NULL);
 
@@ -1655,6 +1669,7 @@ VowelEditor VowelEditor_create (const wchar_t *title, Data data) {
 		//event.widget = my drawingArea;
 		//gui_drawingarea_cb_resize (me, & event);
 		updateWidgets (me.peek());
+		trace ("exit");
 		return me.transfer();
 	} catch (MelderError) {
 		Melder_throw ("VowelEditor not created.");
diff --git a/dwtools/VowelEditor.h b/dwtools/VowelEditor.h
index 98d7fb9..4fe9b74 100644
--- a/dwtools/VowelEditor.h
+++ b/dwtools/VowelEditor.h
@@ -72,10 +72,11 @@ Thing_define (VowelEditor, Editor) {
 		GuiLabel startInfo, endInfo;
 		structVowelEditor_F1F2Grid grid;
 	// overridden methods:
-		void v_destroy ();
-		void v_createChildren ();
-		void v_createMenus ();
-		void v_createHelpMenuItems (EditorMenu menu);
+		virtual void v_destroy ();
+		virtual bool v_scriptable () { return false; }
+		virtual void v_createChildren ();
+		virtual void v_createMenus ();
+		virtual void v_createHelpMenuItems (EditorMenu menu);
 };
 
 VowelEditor VowelEditor_create (const wchar_t *title, Data data);
diff --git a/dwtools/manual_dwtools.cpp b/dwtools/manual_dwtools.cpp
index 0c726a8..5d7d0b4 100644
--- a/dwtools/manual_dwtools.cpp
+++ b/dwtools/manual_dwtools.cpp
@@ -173,7 +173,7 @@ NORMAL (L"is:")
 FORMULA (L"#%x = #%A^^-1^ - #%A^^-1^#%t.")
 MAN_END
 
-MAN_BEGIN (L"Band filtering in the frequency domain", L"djmw", 20010404)
+MAN_BEGIN (L"band filtering in the frequency domain", L"djmw", 20010404)
 INTRO (L"We describe how band filtering in the frequency domain is performed.")
 NORMAL (L"We start with a @Sound and end with a filter bank representation of "
 	"this sound. We assume a standard analysis context: a sound divided into "
@@ -255,20 +255,42 @@ LIST_ITEM (L"\\bu the whisker lines outside the rectangle connect %%q25% with %%
 MAN_END
 
 
-MAN_BEGIN (L"BarkFilter", L"djmw", 20010404)
-INTRO (L"One of the @@types of objects@ in P\\s{RAAT}.")
+MAN_BEGIN (L"BarkFilter", L"djmw", 20141023)
+INTRO (L"A #deprecated @@types of objects|type of object@ in P\\s{RAAT}. It is replaced by @@BarkSpectrogram at .")
 NORMAL (L"An object of type BarkFilter represents an acoustic time-frequency "
 	"representation of a sound: the power spectral density %P(%z, %t), expressed "
-	"in dB's. "
+	"in dB's as 10*log10(power/4e-10)). In the now preferred BarkSpectrogram the power is represented instead of its dB value."
 	"It is sampled into a number of points around equally spaced times %t__%i_ "
 	"and frequencies %z__%j_ (on a Bark scale).")
 ENTRY (L"Inside a BarkFilter")
-NORMAL (L"With @Inspect you will see that this type contains the same "
-	"attributes a @Matrix.")
+NORMAL (L"With @Inspect you will see that this type contains the same attributes a @Matrix object.")
+MAN_END
+
+
+MAN_BEGIN (L"BarkSpectrogram", L"djmw", 20141023)
+INTRO (L"One of the @@types of objects@ in P\\s{RAAT}.")
+NORMAL (L"An object of type BarkSpectrogram represents an acoustic time-frequency "
+	"representation of a sound: the power spectral density %P(%z, %t). "
+	"It is sampled into a number of points around equally spaced times %t__%i_ "
+	"and frequencies %z__%j_ (on a Bark scale).")
+NORMAL (L" The bark to hertz transformation is defined as:")
+FORMULA (L"hertz = 650.0 * sinh (bark / 7.0),")
+NORMAL (L"while its inverse is defined as:")
+FORMULA (L"bark = 7.0 * log (hertz/650 + sqrt (1 + (hertz/650)^^2^).")
+ENTRY (L"Inside a BarkSpectrogram")
+NORMAL (L"With @Inspect you will see that this type contains the same attributes a @Matrix object.")
+MAN_END
+
+MAN_BEGIN (L"BarkSpectrogram: Draw Sekey-Hanson auditory filters...", L"djmw", 20141023)
+INTRO (L"A command to draw the auditory filters defined in @@Sekey & Hanson (1984)@.")
 MAN_END
 
-MAN_BEGIN (L"Bootstrap", L"djmw", 20031103)
-INTRO (L"The bootstrap data points are a random sample of size %n "
+MAN_BEGIN (L"BarkSpectrogram: Paint image...", L"djmw", 20141023)
+INTRO (L"A command to draw the selected @BarkSpectrogram into the @@Picture window@ in shades of grey.")
+MAN_END
+
+MAN_BEGIN (L"bootstrap", L"djmw", 20141101)
+INTRO (L"The bootstrap data set is a random sample of size %n "
 	"drawn %%with% replacement from the sample (%x__1_,...%x__n_). This "
 	"means that the bootstrap data set consists of members of the original "
 	"data set, some appearing zero times, some appearing once, some appearing "
@@ -617,6 +639,15 @@ FORMULA (L"\\Si__%k=1..%numberOfCoefficients_ %c__%k_ %x^^%k^ = \\Si__%k=1.."
 NORMAL (L"We use the recurrence relation for @@Chebyshev polynomials@ to calculate these coefficients.")
 MAN_END
 
+MAN_BEGIN (L"ClassificationTable: To Confusion...", L"djmw", 20141030)
+INTRO (L"A command to create a @Confusion object from the selected @ClassificationTable object.")
+ENTRY (L"Settings")
+TAG (L"##Only class labels#")
+DEFINITION (L"defines whether the class labels from the ClassificationTable object will be used not only as response labels but also as stimulus labels. If checked the resulting Confusion will always have equal stimulus and response labels. If not checked the stimulus labels will be determined from the row labels of the ClassificationTable object. ")
+ENTRY (L"Behaviour")
+NORMAL (L"In obtaining a Confusion object from a ClassificationTable we explicitly use its row labels as stimulus labels.")
+MAN_END
+
 MAN_BEGIN (L"ClassificationTable", L"djmw", 19990525)
 INTRO (L"One of the @@types of objects@ in Praat.")
 NORMAL (L"An object of type ClassificationTable represents the result of a classification experiment. "
@@ -742,7 +773,7 @@ CODE (L"       u    i    a   ! The response labels")
 CODE (L" u     6    2    1   ! Responses on stimulus  u,")
 CODE (L" i     3    4    2   ! Responses on stimulus  i")
 CODE (L" a     1    4    4   ! Responses on stimulus  a")
-NORMAL (L"The command ##Increase: \"u\", \"i\"# results in:")
+NORMAL (L"The command  ##Increase: \"u\", \"i\"#  results in:")
 CODE (L"       u    i    a   ! The responses")
 CODE (L" u     6    3    1   ! Responses on stimulus  u,")
 CODE (L" i     3    4    2   ! Responses on stimulus  i")
@@ -867,6 +898,17 @@ NORMAL (L"The \"fraction correct\" is defined as the quotient of the number "
 NORMAL (L"Correct classifications have identical row and column labels.")
 MAN_END
 
+MAN_BEGIN (L"Confusion & ClassificationTable: Increase confusion count", L"djmw", 201411101)
+INTRO (L"Increases the contents of cell(s) in the selected @@Confusion at . The cells to increase are determined by the selected "
+	"@ClassificationTable.")
+ENTRY (L"Behaviour")
+NORMAL (L"For each row in the ClassificationTable object the contents of one cell in the Confusion we be increased by one. "
+	"This cell is determined as follows: we start by finding the label of the column wich the largest number in it. "
+	"This label is defined as the ##response label#. We use the corresponding row label as the ##stimulus label#. The content "
+	"of the cell in the Confusion object whose row and column are labeled with ##stimulus label# and ##response label#, "
+	"respectively, is increased by one.")
+MAN_END
+
 MAN_BEGIN (L"Confusion: To TableOfReal (marginals)", L"djmw", 20011031)
 INTRO (L"A new @TableOfReal object is created from the selected @Confusion "
 	"object with one extra row and column. ")
@@ -1487,7 +1529,7 @@ LIST_ITEM (L"\\bu Draw eigenvector...")
 LIST_ITEM (L"\\bu @@Discriminant: Draw sigma ellipses...|Draw sigma ellipses...@")
 MAN_END
 
-MAN_BEGIN (L"Discriminant analysis", L"djmw", 20140509)
+MAN_BEGIN (L"Discriminant analysis", L"djmw", 20141101)
 INTRO (L"This tutorial will show you how to perform discriminant analysis with P\\s{RAAT}")
 NORMAL (L"As an example, we will use the dataset from @@Pols et al. (1973)@ "
 	"with the frequencies and levels of the first three formants from the 12 "
@@ -1579,8 +1621,48 @@ NORMAL (L"In general you would separate your data into two independent sets, "
 	"\\s{TRAIN} and \\s{TEST}. You would use \\s{TRAIN} to train the "
 	"discriminant classifier and \\s{TEST} to test how well it classifies. "
 	"Several possibilities for splitting a dataset into two sets exist. "
-	"We mention the @@Jackknife|jackknife@ (\"leave-one-out\") and the "
-	"@@Bootstrap|bootstrap@ methods (\"resampling\").")
+	"We mention the @@jackknife@ (\"leave-one-out\") and the "
+	"@@bootstrap@ methods (\"resampling\").")
+ENTRY (L"5.1 Jacknife classification")
+NORMAL (L"The following script summarizes #jackknife classification of the dataset:")
+CODE (L"selectObject: table")
+CODE (L"numberOfRows = Get number of rows")
+CODE (L"for irow to numberOfRows")
+CODE (L"  selectObject: table")
+CODE (L"  rowi = Extract rows where: \"row = irow\"")
+CODE (L"  selectObject: table")
+CODE (L"  rest = Extract rows where: \"row <> .irow\"")
+CODE (L"  discriminant = To Discriminant")
+CODE (L"  plusObject: rowi")
+CODE (L"  classification = To ClassificationTable: \"yes\", \"yes\"")
+CODE (L"    if irow = 1")
+CODE (L"    confusion = To Confusion: \"yes\"")
+CODE (L"  else")
+CODE (L"    plusObject: confusion")
+CODE (L"    Increase confusion count")
+CODE (L"  endif")
+CODE (L"  removeObject: rowi, rest, discriminant, classification")
+CODE (L"endfor")
+CODE (L"selectObject: confusion")
+CODE (L"fractionCorrect = Get fraction correct")
+CODE (L"appendInfoLine: fractionCorrect, \" (= fraction correct, jackknifed \", numberOfRows, \" times).\"")
+CODE (L"removeObject: confusion")
+ENTRY (L"5.2 Bootstrap classification")
+NORMAL (L"The following script summarizes bootstrap classification.")
+CODE (L"fractionCorrect = 0")
+CODE (L"for i to numberOfBootstraps")
+CODE (L"  selectObject: table")
+CODE (L"  resampled = To TableOfReal (bootstrap)")
+CODE (L"  discriminant = To Discriminant")
+CODE (L"  plusObject: resampled")
+CODE (L"  classification = To ClassificationTable: \"yes\", \"yes\"")
+CODE (L"  confusion = To Confusion: \"yes\"")
+CODE (L"  fc = Get fraction correct")
+CODE (L"  fractionCorrect += fc")
+CODE (L"  removeObject: resampled, discriminant, classification, confusion")
+CODE (L"endfor")
+CODE (L"fractionCorrect /= numberOfBootstraps")
+CODE (L"appendInfoLine: fractionCorrect, \" (= fraction correct, bootstrapped \", numberOfBootstraps, \" times).\"")
 MAN_END
 
 MAN_BEGIN (L"Discriminant: Draw sigma ellipses...", L"djmw", 20040407)
@@ -2207,11 +2289,11 @@ MAN_END
 MAN_BEGIN (L"FilterBank: Get frequency in mel...", L"djmw", 20030901)
 MAN_END
 
-MAN_BEGIN (L"FormantFilter", L"djmw", 20010404)
-INTRO (L"One of the @@types of objects@ in P\\s{RAAT}.")
+MAN_BEGIN (L"FormantFilter", L"djmw", 20141022)
+INTRO (L"A #deprecated @@types of objects|type of object@ in P\\s{RAAT}. It is replaced by @@Spectrogram at .")
 NORMAL (L"An object of type FormantFilter represents an acoustic time-frequency "
 	"representation of a sound: the power spectral density %P(%f, %t), expressed "
-	"in dB's. "
+	"in dB as 10*log10(power/4e-10)). In the now preferred Spectrogram the power is represented instead of its dB value. "
 	"It is sampled into a number of points around equally spaced times %t__%i_ "
 	"and frequencies %f__%j_ (on a linear frequency scale).")
 ENTRY (L"Inside a FormantFilter")
@@ -2287,9 +2369,8 @@ NORMAL (L"An object of type ISpline represents a linear combination of basis "
 FORMULA (L"ISpline (%x) = \\Si__%k=1..%numberOfCoefficients_ %c__%k_ %ispline__%k_(%x)")
 MAN_END
 
-MAN_BEGIN (L"Jackknife", L"djmw", 20031103)
-INTRO (L"A technique for estimating the bias and standard deviation of an "
-	"estimate.")
+MAN_BEGIN (L"jackknife", L"djmw", 20141101)
+INTRO (L"A technique for estimating the bias and standard deviation of an estimate.")
 NORMAL (L"Suppose we have a sample #%x = (%x__1_, %x__2_,...%x__n_) and wish to estimate "
 	"the bias and standard error of an estimator \\Te. The jackknife "
 	"focuses on the samples that leave out one observation at a time: "
@@ -2375,8 +2456,8 @@ NORMAL (L"Singular value decomposition with backsubstitution. "
 NORMAL (L"See for more details: @@Golub & van Loan (1996)@ chapters 2 and 3.")
 MAN_END
 
-MAN_BEGIN (L"MelFilter", L"djmw", 20120724)
-INTRO (L"One of the @@types of objects@ in P\\s{RAAT}.")
+MAN_BEGIN (L"MelFilter", L"djmw", 20141022)
+INTRO (L"A #deprecated @@types of objects|type of object@ in P\\s{RAAT}. It is replaced by the @@MelSpectrogram at .")
 NORMAL (L"An object of type MelFilter represents an acoustic time-frequency "
 	"representation of a sound: the power spectral density %P(%f, %t), "
 	"expressed in dB's. "
@@ -2386,20 +2467,36 @@ NORMAL (L"The frequency in mels is:")
 FORMULA (L"mels = 2595 * log10 (1 + hertz / 700),")
 NORMAL (L"and its inverse is:")
 FORMULA (L"hertz = 700 * (10.0^^mel / 2595.0^ - 1).")
-ENTRY (L"Inside a MelFilter")
+MAN_END
+
+MAN_BEGIN (L"MelSpectrogram", L"djmw", 20141022)
+INTRO (L"One of the @@types of objects@ in P\\s{RAAT}.")
+NORMAL (L"An object of type MelSpectrogram represents an acoustic time-frequency "
+	"representation of a sound: the power spectral density %P(%f, %t)."
+	"It is sampled into a number of points around equally spaced times %t__%i_ "
+	"and frequencies %f__%j_ (on a Mel frequency scale).")
+NORMAL (L"The mel frequency scale is defined as:")
+FORMULA (L"mels = 2595 * log10 (1 + hertz / 700),")
+NORMAL (L"and its inverse is:")
+FORMULA (L"hertz = 700 * (10.0^^mel / 2595.0^ - 1).")
+ENTRY (L"Inside a MelSpectrogram")
 NORMAL (L"With @Inspect you will see that this type contains the same "
 	"attributes a @Matrix.")
 MAN_END
 
-MAN_BEGIN (L"MelFilter: To MFCC...", L"djmw", 20130221)
-INTRO (L"A command to create a @MFCC object from each selected @MelFilter "
+MAN_BEGIN (L"MelSpectrogram: Paint image...", L"djmw", 20141023)
+INTRO (L"A command to draw the selected @MelSpectrogram into the @@Picture window@ in shades of grey.")
+MAN_END
+
+MAN_BEGIN (L"MelSpectrogram: To MFCC...", L"djmw", 20141023)
+INTRO (L"A command to create a @MFCC object from each selected @MelSpectrogram "
 	"object.")
-NORMAL (L"Mel frequency cepstral coefficients result from the Discrete Cosine "
-	"Transform of the filterbank spectrum (in dB). The following formula "
-	"shows the relation:")
+NORMAL (L"Mel frequency cepstral coefficients %c__%k_ in each frame of the MFCC object result from the output of a Discrete Cosine "
+	"Transform on spectral values %P__%j_ in the corresponding frame of the MelSpectrogram. The following formula "
+	"shows the relation between the values in each frame:")
 FORMULA (L"%c__%k-1_ = \\Si__%j=1_^^%N^ %P__%j_ cos (\\pi(%k-1)(%j-0.5)/%N)),")
-NORMAL (L"where %N represents the number of filters and %P__%j_ the power in dB "
-	"in the %j^^%th^ filter (%k runs from 1 to %N).")
+NORMAL (L"where %N represents the number of spectral values and %P__%j_ the power in dB "
+	"of the %j^^%th^ spectral value (%k runs from 1 to %N).")
 NORMAL (L"This transformation was first used by @@Davis & Mermelstein (1980)@.")
 MAN_END
 
@@ -3429,27 +3526,36 @@ NORMAL (L"Rodents produce sounds with frequencies far outside the human audible
 	"rodents frequencies in the interval from 54000 Hz to 76050 Hz will theredore be mapped to the frequency interval between 0 and 22050 Hz. ")
 MAN_END
 
-MAN_BEGIN (L"Sound: To BarkFilter...", L"djmw", 20010404)
-INTRO (L"A command that creates a @BarkFilter object from every selected "
+MAN_BEGIN (L"Sound: To BarkSpectrogram...", L"djmw", 20141023)
+INTRO (L"A command that creates a @BarkSpectrogram object from every selected "
 	"@Sound object by @@band filtering in the frequency domain@ with a "
 	"bank of filters.")
-NORMAL (L"The filter functions used are:")
+NORMAL (L"The auditory filter functions used are defined as:")
 FORMULA (L"10 log %#H(%z) = 7 - 7.5 * (%z__%c_ - %z - 0.215) - 17.5 * \\Vr "
 	"(0.196 + (%z__%c_ - %z - 0.215)^2)")
 NORMAL (L"where %z__%c_ is the central (resonance) frequency of the filter in Bark. "
-	"The bandwidths of these filters are constant and equal 1 Bark.")
+	"The bandwidths of these filters are constant and equal 1 Bark. ")
+NORMAL (L"The auditory filters are defined in @@Sekey & Hanson (1984)@. You can draw these filters from "
+	"a BarkSpectrogram object by selecting @@BarkSpectrogram: Draw Sekey-Hanson auditory filters... at .")
+MAN_END
+
+MAN_BEGIN (L"Sound: To FormantFilter...", L"djmw", 20141024)
+INTRO (L"A #deprecated command that creates a @FormantFilter object from every selected @Sound object by "
+	"@@band filtering in the frequency domain@ with a bank of filters whose bandwidths depend on the pitch of the signal.")
+NORMAL (L"The analysis proceeds in two steps:")
+LIST_ITEM (L"1. We perform a pitch analysis (see @@Sound: To Pitch...@ for details).")
+LIST_ITEM (L"2. We perform a filter bank analysis on a linear frequency scale. The bandwidth of the filters depends on "
+	"the measured pitch (see @@Sound & Pitch: To Spectrogram...@ for details).")
 MAN_END
 
-MAN_BEGIN (L"Sound: To FormantFilter...", L"djmw", 20010404)
-INTRO (L"A command that creates a @FormantFilter object from every selected "
+MAN_BEGIN (L"Sound: To Spectrogram (pitch-dependent)...", L"djmw", 20141024)
+INTRO (L"A command that creates a @Spectrogram object from every selected "
 	"@Sound object by @@band filtering in the frequency domain@ with a "
-	"bank of filters whose bandwidths depend on the pitch of the signal.")
+	"bank of formant filters whose bandwidths vary with the local pitch of the signal.")
 NORMAL (L"The analysis proceeds in two steps:")
-LIST_ITEM (L"1. We perform a pitch analysis (see @@Sound: To Pitch...@ for "
-	"details).")
+LIST_ITEM (L"1. We perform a pitch analysis (see @@Sound: To Pitch...@ for details).")
 LIST_ITEM (L"2. We perform a filter bank analysis on a linear frequency scale. "
-	"The bandwidth of the filters depends on the measured pitch (see @@Sound & "
-	"Pitch: To FormantFilter...@ for details).")
+	"The bandwidth of the filters depends on the measured pitch (see @@Sound & Pitch: To Spectrogram...@ for details).")
 MAN_END
 
 MAN_BEGIN (L"Sound: Paint where...", L"djmw", 20140509)
@@ -3646,10 +3752,10 @@ TAG (L"%%Trim label%,")
 DEFINITION (L"determines the label that the trimmed intervals in the TextGrid will get.")
 MAN_END
 
-MAN_BEGIN (L"Sound & Pitch: To FormantFilter...", L"djmw", 20010404)
-INTRO (L"A command that creates a @FormantFilter object from the selected "
+MAN_BEGIN (L"Sound & Pitch: To Spectrogram...", L"djmw", 20141024)
+INTRO (L"A command that creates a @Spectrogram object from the selected "
 	"@Sound and @Pitch objects by @@band filtering in the frequency domain@ with a "
-	"bank of filters whose bandwidths depend on the Pitch.")
+	"bank of filters whose bandwidths depend on the local pitch.")
 NORMAL (L"The filter functions used are:")
 FORMULA (L"%#H(%f, %F__0_) = 1 / (((%f__%c_^^2^ - %f^2) /%f\\.c%B(%F__0_)))^2 + 1),")
 NORMAL (L"where %f__%c_ is the central (resonance) frequency of the filter. "
@@ -3659,20 +3765,24 @@ NORMAL (L"where %F__0_ is the fundamental frequency as determined from the Pitch
 	"object. Whenever the value of %F__0_ is undefined, a value of 100 Hz is taken.")
 MAN_END
 
-MAN_BEGIN (L"Sound: To MelFilter...", L"djmw", 20010404)
-INTRO (L"A command that creates a @MelFilter object from every selected "
+MAN_BEGIN (L"Sound: To MelFilter...", L"djmw", 20141022)
+INTRO (L"A deprecated command. Use @@Sound: To MelSpectrogram...@ instead.")
+MAN_END
+
+MAN_BEGIN (L"Sound: To MelSpectrogram...", L"djmw", 20141022)
+INTRO (L"A command that creates a @MelSpectrogram object from every selected "
 	"@Sound object by @@band filtering in the frequency domain@ with a "
-	"bank of filters.")
-NORMAL (L"The filter functions used are triangular in shape on a %linear "
-	"frequency scale. The filter function depends on three parameters, the "
+	"set of triangular filters.")
+NORMAL (L"The filter functions used are all triangular in shape on a %mel "
+	"frequency scale. Each filter function depends on three parameters, the "
 	"lower frequency %f__%l_, the central frequency %f__%c_ and the higher "
 	"frequency %f__%h_. "
 	"On a %mel scale, the distances %f__%c_-%f__%l_ and %f__%h_-%f__%c_ "
-	"are the same for each filter and are equal to the distance between the "
-	"%f__%c_'s of successive filters. The filter function is:" )
+	"are equal for each filter. The filter function is as follows:" )
 FORMULA (L"%#H(%f) = 0 for %f \\<_ %f__%l_ and %f \\>_ %f__%h_")
 FORMULA (L"%#H(%f) = (%f - %f__%l_) / (%f__%c_ - %f__%l_) for %f__%l_ \\<_ %f \\<_ %f__%c_")
 FORMULA (L"%#H(%f) = (%f__%h_ - %f) / (%f__%h_ - %f__%c_) for %f__%c_ \\<_ %f \\<_ %f__%h_")
+NORMAL (L"In general the number of filter values stored in each frame of the MelSpectrogram is an order of magnitude smaller than the number of sound samples in the corresponding analysis frame.")
 MAN_END
 
 MAN_BEGIN (L"Sound: To Pitch (shs)...", L"djmw", 19970402)
@@ -4941,6 +5051,11 @@ NORMAL (L"H. Sakoe & S. Chiba (1978): \"Dynamic programming algorithm optimizati
 	"%%Transactions on ASSP% #26: 43\\--49.")
 MAN_END
 
+MAN_BEGIN (L"Sekey & Hanson (1984)", L"djmw", 20050302)
+NORMAL (L"A. Sekey & B.A. Hanson (1984): \"Improved 1-Bark bandwidth auditory filter.\" "
+	"%%Journal of the Acoustical Society of America% #75: 1902\\--1904.")
+MAN_END
+
 MAN_BEGIN (L"Schott (2001)", L"djmw", 20090629)
 NORMAL (L"J. R. Schott (2001): \"Some tests for the equality of covariance matrices.\" "
 	"%%Journal of Statistical Planning and Inference% #94: 25\\-–36.")
diff --git a/dwtools/praat_David_init.cpp b/dwtools/praat_David_init.cpp
index 516be3b..ca5c19b 100644
--- a/dwtools/praat_David_init.cpp
+++ b/dwtools/praat_David_init.cpp
@@ -86,6 +86,7 @@
 #include "Excitations.h"
 #include "espeakdata_FileInMemory.h"
 #include "FileInMemory.h"
+#include "FilterBank.h"
 #include "Formula.h"
 #include "FormantGridEditor.h"
 #include "DataModeler.h"
@@ -123,10 +124,9 @@
 #include "CCs_to_DTW.h"
 #include "Discriminant_Pattern_Categories.h"
 #include "DTW_and_TextGrid.h"
-#include "MelFilter_and_MFCC.h"
 #include "Permutation_and_Index.h"
 #include "Pitch_extensions.h"
-#include "Sound_and_FilterBank.h"
+#include "Sound_and_Spectrogram_extensions.h"
 #include "Sound_to_Pitch2.h"
 #include "Sound_to_SPINET.h"
 #include "TableOfReal_and_SVD.h"
@@ -149,7 +149,7 @@ void praat_SSCP_as_TableOfReal_init (ClassInfo klas);
 void praat_CC_init (ClassInfo klas);
 void DTW_constraints_addCommonFields (void *dia);
 void DTW_constraints_getCommonFields (void *dia, int *begin, int *end, int *slope);
-void praat_Matrixft_query_init (ClassInfo klas);
+void praat_BandFilterSpectrogram_query_init (ClassInfo klas);
 int praat_Fon_formula (UiForm dia, Interpreter interpreter);
 void praat_EditDistanceTable_as_TableOfReal_init (ClassInfo klas);
 
@@ -172,12 +172,36 @@ DIRECT (Activation_to_Matrix)
 	}
 END
 
+/********************** BandFilterSpectrogram *******************************************/
+
+FORM (BandFilterSpectrogram_drawFrequencyScale, L"", L"")
+	REAL (L"left Horizontal frequency range (Hz)", L"0.0")
+	REAL (L"right Horizontal frequency range (Hz)", L"0.0")
+	REAL (L"left Vertical frequency range (mel)", L"0.0")
+	REAL (L"right Vertical frequency range (mel)", L"0.0")
+	BOOLEAN (L"Garnish", 1)
+	OK
+DO
+	autoPraatPicture picture;
+	LOOP {
+		iam (BandFilterSpectrogram);
+		BandFilterSpectrogram_drawFrequencyScale (me, GRAPHICS, GET_REAL (L"left Horizontal frequency range"), 
+			GET_REAL (L"right Horizontal frequency range"),
+			GET_REAL (L"left Vertical frequency range"), GET_REAL (L"right Vertical frequency range"),
+			GET_INTEGER (L"Garnish"));
+	}
+END
+
 /********************** BarkFilter *******************************************/
 
 DIRECT (BarkFilter_help)
 	Melder_help (L"BarkFilter");
 END
 
+DIRECT (BarkSpectrogram_help)
+	Melder_help (L"BarkSpectrogram");
+END
+
 FORM (BarkFilter_drawSpectrum, L"BarkFilter: Draw spectrum (slice)", L"FilterBank: Draw spectrum (slice)...")
 	POSITIVE (L"Time (s)", L"0.1")
 	REAL (L"left Frequency range (Bark)", L"0.0")
@@ -221,6 +245,73 @@ DO
 			GET_REAL (L"right Amplitude range"), GET_INTEGER (L"Garnish"));
 	}
 END
+
+FORM (BarkSpectrogram_drawSekeyHansonAuditoryFilters, L"BarkSpectrogram: Draw Sekey-Hanson auditory filters", L"BarkSpectrogram: Draw Sekey-Hanson auditory filters...")
+	INTEGER (L"left Filter range", L"0")
+	INTEGER (L"right Filter range", L"0")
+	RADIO (L"Frequency scale", 2)
+	RADIOBUTTON (L"Hertz")
+	RADIOBUTTON (L"Bark")
+	REAL (L"left Frequency range", L"0.0")
+	REAL (L"right Frequency range", L"0.0")
+	BOOLEAN (L"Amplitude scale in dB", 1)
+	REAL (L"left Amplitude range", L"0.0")
+	REAL (L"right Amplitude range", L"0.0")
+	BOOLEAN (L"Garnish", 1)
+	OK
+DO
+	autoPraatPicture picture;
+	LOOP {
+		iam (BarkSpectrogram);
+		bool xIsHertz = GET_INTEGER (L"Frequency scale") == 1;
+		BarkSpectrogram_drawSekeyHansonFilterFunctions (me, GRAPHICS, xIsHertz,
+			GET_INTEGER (L"left Filter range"), GET_INTEGER (L"right Filter range"),
+			GET_REAL (L"left Frequency range"), GET_REAL (L"right Frequency range"),
+			GET_INTEGER (L"Amplitude scale in dB"), GET_REAL (L"left Amplitude range"),
+			GET_REAL (L"right Amplitude range"), GET_INTEGER (L"Garnish"));
+	}
+END
+
+FORM (BarkFilter_paint, L"FilterBank: Paint", 0)
+	REAL (L"left Time range (s)", L"0.0")
+	REAL (L"right Time range (s)", L"0.0")
+	REAL (L"left Frequency range (bark)", L"0.0")
+	REAL (L"right Frequency range (bark)", L"0.0")
+	REAL (L"left Amplitude range", L"0.0")
+	REAL (L"right Amplitude range", L"0.0")
+	BOOLEAN (L"Garnish", 0)
+	OK
+DO
+	autoPraatPicture picture;
+	LOOP {
+		iam (Matrix);
+		FilterBank_paint ((FilterBank) me, GRAPHICS, GET_REAL (L"left Time range"), GET_REAL (L"right Time range"),
+		GET_REAL (L"left Frequency range"), GET_REAL (L"right Frequency range"),
+		GET_REAL (L"left Amplitude range"), GET_REAL (L"right Amplitude range"), GET_INTEGER (L"Garnish"));
+	}
+END
+
+DIRECT (BarkFilter_to_BarkSpectrogram)
+	LOOP {
+		iam (BarkFilter);
+		praat_new (BarkFilter_to_BarkSpectrogram (me), my name);
+	}
+END
+
+DIRECT (MelFilter_to_MelSpectrogram)
+	LOOP {
+		iam (MelFilter);
+		praat_new (MelFilter_to_MelSpectrogram (me), my name);
+	}
+END
+
+DIRECT (FormantFilter_to_Spectrogram)
+	LOOP {
+		iam (FormantFilter);
+		praat_new (FormantFilter_to_Spectrogram (me), my name);
+	}
+END
+
 /********************** Categories  ****************************************/
 
 FORM (Categories_append, L"Categories: Append 1 category", L"Categories: Append 1 category...")
@@ -626,10 +717,43 @@ DIRECT (ClassificationTable_help)
 	Melder_help (L"ClassificationTable");
 END
 
-DIRECT (ClassificationTable_to_Confusion)
+FORM (ClassificationTable_getClassIndexAtMaximumInRow, L"ClassificationTable: Get class index at maximum in row", 0)
+	NATURAL (L"Row number", L"1")
+	OK
+DO
 	LOOP {
 		iam (ClassificationTable);
-		praat_new (ClassificationTable_to_Confusion (me), 0);
+		long classIndex = TableOfReal_getColumnIndexAtMaximumInRow (me, GET_INTEGER (L"Row number"));
+		Melder_information (Melder_integer (classIndex));
+	}
+END
+
+FORM (ClassificationTable_getClassLabelAtMaximumInRow, L"ClassificationTable: Get class label at maximum in row", 0)
+	NATURAL (L"Row number", L"1")
+	OK
+DO
+	LOOP {
+		iam (ClassificationTable);
+		const wchar_t *classLabel = TableOfReal_getColumnLabelAtMaximumInRow (me, GET_INTEGER (L"Row number"));
+		Melder_information (classLabel);
+	}
+END
+
+// deprecated 2014
+DIRECT (ClassificationTable_to_Confusion_old)
+	LOOP {
+		iam (ClassificationTable);
+		praat_new (ClassificationTable_to_Confusion (me, 0), my name);
+	}
+END
+
+FORM (ClassificationTable_to_Confusion, L"ClassificationTable: To Confusion", L"ClassificationTable: To Confusion...")
+	BOOLEAN (L"Only class labels", 1)
+	OK
+DO
+	LOOP {
+		iam (ClassificationTable);
+		praat_new (ClassificationTable_to_Confusion (me, GET_INTEGER (L"Only class labels")), my name);
 	}
 END
 
@@ -809,6 +933,12 @@ DIRECT (Confusion_getFractionCorrect)
 	}
 END
 
+DIRECT (Confusion_and_ClassificationTable_increase)
+	Confusion me = FIRST (Confusion);
+	ClassificationTable thee = FIRST (ClassificationTable);
+	Confusion_and_ClassificationTable_increase (me, thee);
+END
+
 /******************* Confusion & Matrix *************************************/
 
 FORM (Confusion_Matrix_draw, L"Confusion & Matrix: Draw confusions with arrows", 0)
@@ -2314,8 +2444,8 @@ FORM (Eigen_and_Matrix_project, L"Eigen & Matrix: Project", L"Eigen & Matrix: Pr
 	OK
 DO
 	Eigen me = FIRST_GENERIC (Eigen);
-	Matrix mat = FIRST_GENERIC (Matrix);
-	praat_new (Eigen_and_Matrix_project (me, mat, GET_INTEGER (L"Number of dimensions")), my name, L"_", mat->name);
+	Matrix thee = FIRST_GENERIC (Matrix);
+	praat_new (Eigen_and_Matrix_project (me, thee, GET_INTEGER (L"Number of dimensions")), my name, L"_", thy name);
 END
 
 DIRECT (Eigen_and_SSCP_project)
@@ -2731,6 +2861,44 @@ DO
 	}
 END
 
+FORM (MelSpectrogram_paintImage, L"MelSpectrogram: Paint image", L"MelSpectrogram: Paint image...")
+	REAL (L"left Time range (s)", L"0.0")
+	REAL (L"right Time range (s)", L"0.0")
+	REAL (L"left Frequency range (mel)", L"0.0")
+	REAL (L"right Frequency range (mel)", L"0.0")
+	REAL (L"left Amplitude range (dB)", L"0.0")
+	REAL (L"right Amplitude range (dB)", L"0.0")
+	BOOLEAN (L"Garnish", 1)
+	OK
+DO
+	autoPraatPicture picture;
+	LOOP {
+		iam (MelSpectrogram);
+		BandFilterSpectrogram_paintImage (me, GRAPHICS, GET_REAL (L"left Time range"), GET_REAL (L"right Time range"),
+		GET_REAL (L"left Frequency range"), GET_REAL (L"right Frequency range"),
+		GET_REAL (L"left Amplitude range"), GET_REAL (L"right Amplitude range"), GET_INTEGER (L"Garnish"));
+	}
+END
+
+FORM (BarkSpectrogram_paintImage, L"BarkSpectrogram: Paint image", L"BarkSpectrogram: Paint image...")
+	REAL (L"left Time range (s)", L"0.0")
+	REAL (L"right Time range (s)", L"0.0")
+	REAL (L"left Frequency range (bark)", L"0.0")
+	REAL (L"right Frequency range (bark)", L"0.0")
+	REAL (L"left Amplitude range (dB)", L"0.0")
+	REAL (L"right Amplitude range (dB)", L"0.0")
+	BOOLEAN (L"Garnish", 1)
+	OK
+DO
+	autoPraatPicture picture;
+	LOOP {
+		iam (BarkSpectrogram);
+		BandFilterSpectrogram_paintImage (me, GRAPHICS, GET_REAL (L"left Time range"), GET_REAL (L"right Time range"),
+		GET_REAL (L"left Frequency range"), GET_REAL (L"right Frequency range"),
+		GET_REAL (L"left Amplitude range"), GET_REAL (L"right Amplitude range"), GET_INTEGER (L"Garnish"));
+	}
+END
+
 FORM (FilterBank_paintImage, L"FilterBank: Paint image", 0)
 	REAL (L"left Time range (s)", L"0.0")
 	REAL (L"right Time range (s)", L"0.0")
@@ -2860,6 +3028,17 @@ DO
 	}
 END
 
+FORM (BandFilterSpectrogram_equalizeIntensities, L"BandFilterSpectrogram: Equalize intensities", L"")
+	REAL (L"Intensity (dB)", L"80.0")
+	OK
+DO
+	LOOP {
+		iam (BandFilterSpectrogram);
+		BandFilterSpectrogram_equalizeIntensities (me, GET_REAL (L"Intensity"));
+		praat_dataChanged (me);
+	}
+END
+
 DIRECT (FilterBank_to_Matrix)
 	LOOP {
 		iam (FilterBank);
@@ -2867,6 +3046,16 @@ DIRECT (FilterBank_to_Matrix)
 	}
 END
 
+FORM (BandFilterSpectrogram_to_Matrix, L"(BandFilterSpectrogram: To Matrix", 0)
+	BOOLEAN (L"Convert to dB values", 1)
+	OK
+DO
+	LOOP {
+		iam (BandFilterSpectrogram);
+		praat_new (BandFilterSpectrogram_to_Matrix (me, GET_INTEGER (L"Convert to dB values")), my name);
+	}
+END
+
 FORM (FilterBanks_crossCorrelate, L"FilterBanks: Cross-correlate", 0)
 	RADIO_ENUM (L"Amplitude scaling", kSounds_convolve_scaling, DEFAULT)
 	RADIO_ENUM (L"Signal outside time domain is...", kSounds_convolve_signalOutsideTimeDomain, DEFAULT)
@@ -2880,6 +3069,22 @@ DO
 		f1 -> name, L"_", f2 -> name);
 END
 
+FORM (BandFilterSpectrograms_crossCorrelate, L"BandFilterSpectrograms: Cross-correlate", 0)
+	RADIO_ENUM (L"Amplitude scaling", kSounds_convolve_scaling, DEFAULT)
+	RADIO_ENUM (L"Signal outside time domain is...", kSounds_convolve_signalOutsideTimeDomain, DEFAULT)
+	OK
+DO
+	BandFilterSpectrogram f1 = 0, f2 = 0;
+	LOOP {
+		iam (BandFilterSpectrogram); 
+		(f1 ? f2 : f1) = me;
+	}
+	Melder_assert (f1 != 0 && f2 != 0);
+	praat_new (BandFilterSpectrograms_crossCorrelate (f1, f2, GET_ENUM (kSounds_convolve_scaling, L"Amplitude scaling"),
+		GET_ENUM (kSounds_convolve_signalOutsideTimeDomain, L"Signal outside time domain is...")),
+		f1 -> name, L"_", f2 -> name);
+END
+
 FORM (FilterBanks_convolve, L"FilterBanks: Convolve", 0)
 	RADIO_ENUM (L"Amplitude scaling", kSounds_convolve_scaling, DEFAULT)
 	RADIO_ENUM (L"Signal outside time domain is...", kSounds_convolve_signalOutsideTimeDomain, DEFAULT)
@@ -2893,6 +3098,22 @@ DO
 		f1 -> name, L"_", f2 -> name);
 END
 
+FORM (BandFilterSpectrograms_convolve, L"BandFilterSpectrograms: Convolve", 0)
+	RADIO_ENUM (L"Amplitude scaling", kSounds_convolve_scaling, DEFAULT)
+	RADIO_ENUM (L"Signal outside time domain is...", kSounds_convolve_signalOutsideTimeDomain, DEFAULT)
+	OK
+DO
+	BandFilterSpectrogram f1 = 0, f2 = 0;
+	LOOP {
+		iam (BandFilterSpectrogram);
+		(f1 ? f2 : f1) = me;
+	}
+	Melder_assert (f1 != 0 && f2 != 0);
+	praat_new (BandFilterSpectrograms_convolve (f1, f2, GET_ENUM (kSounds_convolve_scaling, L"Amplitude scaling"),
+		GET_ENUM (kSounds_convolve_signalOutsideTimeDomain, L"Signal outside time domain is...")),
+		f1 -> name, L"_", f2 -> name);
+END
+
 DIRECT (FilterBank_to_Intensity)
 	LOOP {
 		iam (FilterBank);
@@ -2900,6 +3121,13 @@ DIRECT (FilterBank_to_Intensity)
 	}
 END
 
+DIRECT (BandFilterSpectrogram_to_Intensity)
+	LOOP {
+		iam (BandFilterSpectrogram);
+		praat_new (BandFilterSpectrogram_to_Intensity (me), my name);
+	}
+END
+
 /*********** FormantFilter *******************************************/
 
 DIRECT (FormantFilter_help)
@@ -3717,55 +3945,55 @@ END
 
 /***** MATRIXFT *************/
 
-DIRECT (Matrixft_getHighestFrequency)
+DIRECT (BandFilterSpectrogram_getHighestFrequency)
 	LOOP {
-		iam (Matrix);
-		Melder_information (Melder_double (my ymax));
+		iam (BandFilterSpectrogram);
+		Melder_information (Melder_double (my ymax), L" ", my v_getFrequencyUnit ());
 	}
 END
 
-DIRECT (Matrixft_getLowestFrequency)
+DIRECT (BandFilterSpectrogram_getLowestFrequency)
 	LOOP {
-		iam (Matrix);
-		Melder_information (Melder_double (my ymin));
+		iam (BandFilterSpectrogram);
+		Melder_information (Melder_double (my ymin), L" ", my v_getFrequencyUnit ());
 	}
 END
 
-DIRECT (Matrixft_getNumberOfFrequencies)
+DIRECT (BandFilterSpectrogram_getNumberOfFrequencies)
 	LOOP {
-		iam (Matrix);
+		iam (BandFilterSpectrogram);
 		Melder_information (Melder_double (my ny));
 	}
 END
 
-DIRECT (Matrixft_getFrequencyDistance)
+DIRECT (BandFilterSpectrogram_getFrequencyDistance)
 	LOOP {
-		iam (Matrix);
-		Melder_information (Melder_double (my dy));
+		iam (BandFilterSpectrogram);
+		Melder_information (Melder_double (my dy), L" ", my v_getFrequencyUnit ());
 	}
 END
 
-FORM (Matrixft_getFrequencyOfRow, L"Get frequency of row", 0)
+FORM (BandFilterSpectrogram_getFrequencyOfRow, L"Get frequency of row", 0)
 	NATURAL (L"Row number", L"1")
 	OK
 DO
 	LOOP {
-		iam (Matrix);
-		Melder_information (Melder_double (Matrix_rowToY (me, GET_INTEGER (L"Row number"))));
+		iam (BandFilterSpectrogram);
+		Melder_information (Melder_double (Matrix_rowToY (me, GET_INTEGER (L"Row number"))), L" ", my v_getFrequencyUnit ());
 	}
 END
 
-FORM (Matrixft_getXofColumn, L"Get time of column", 0)
+FORM (BandFilterSpectrogram_getXofColumn, L"Get time of column", 0)
 	NATURAL (L"Column number", L"1")
 	OK
 DO
 	LOOP {
-		iam (Matrix);
+		iam (BandFilterSpectrogram);
 		Melder_information (Melder_double (Matrix_columnToX (me, GET_INTEGER (L"Column number"))));
 	}
 END
 
-FORM (Matrixft_getValueInCell, L"Get value in cell", 0)
+FORM (BandFilterSpectrogram_getValueInCell, L"Get value in cell", 0)
 	POSITIVE (L"Time (s)", L"0.5")
 	POSITIVE (L"Frequency", L"1")
 	OK
@@ -3773,7 +4001,7 @@ DO
 	double t = GET_REAL (L"Time");
 	double f = GET_REAL (L"Frequency");
 	LOOP {
-		iam (Matrix);
+		iam (BandFilterSpectrogram);
 		if (f < my ymin || f > my ymax) {
 			Melder_throw ("Frequency out of range.");
 		}
@@ -3807,6 +4035,10 @@ DIRECT (MelFilter_help)
 	Melder_help (L"MelFilter");
 END
 
+DIRECT (MelSpectrogram_help)
+	Melder_help (L"MelSpectrogram");
+END
+
 FORM (MelFilter_drawFilterFunctions, L"MelFilter: Draw filter functions", L"FilterBank: Draw filter functions...")
 	INTEGER (L"left Filter range", L"0")
 	INTEGER (L"right Filter range", L"0")
@@ -3834,6 +4066,33 @@ DO
 	}
 END
 
+FORM (MelSpectrogram_drawTriangularFilterFunctions, L"MelSpectrogram: Draw triangulat filter functions", L"MelSpectrogram: Draw filter functions...")
+	INTEGER (L"left Filter range", L"0")
+	INTEGER (L"right Filter range", L"0")
+	RADIO (L"Frequency scale", 1)
+	RADIOBUTTON (L"Mel")
+	RADIOBUTTON (L"Hertz")
+	REAL (L"left Frequency range", L"0.0")
+	REAL (L"right Frequency range", L"0.0")
+	BOOLEAN (L"Amplitude scale in dB", 0)
+	REAL (L"left Amplitude range", L"0.0")
+	REAL (L"right Amplitude range", L"0.0")
+	BOOLEAN (L"Garnish", 1)
+	OK
+DO
+	autoPraatPicture picture;
+	LOOP {
+		iam (MelSpectrogram);
+		MelSpectrogram_drawTriangularFilterFunctions (me, GRAPHICS, GET_INTEGER (L"Frequency scale") - 1,
+			GET_INTEGER (L"left Filter range"), GET_INTEGER (L"right Filter range"),
+			GET_REAL (L"left Frequency range"), GET_REAL (L"right Frequency range"),
+			GET_INTEGER (L"Amplitude scale in dB"),
+			GET_REAL (L"left Amplitude range"), GET_REAL (L"right Amplitude range"),
+			GET_INTEGER (L"Garnish"));
+	}
+END
+
+
 FORM (MelFilter_drawSpectrum, L"MelFilter: Draw spectrum (slice)", L"FilterBank: Draw spectrum (slice)...")
 	POSITIVE (L"Time (s)", L"0.1")
 	REAL (L"left Frequency range (mel)", L"0.0")
@@ -3852,7 +4111,62 @@ DO
 	}
 END
 
-FORM (MelFilter_to_MFCC, L"MelFilter: To MFCC", L"MelFilter: To MFCC...")
+FORM (MelSpectrogram_drawSpectrumAtNearestTimeSlice, L"MelSpectrogram: Draw spectrum at nearest time slice", L"BandFilterSpectrogram: Draw spectrum at nearest time slice...")
+	REAL (L"Time (s)", L"0.1")
+	REAL (L"left Frequency range (mel)", L"0.0")
+	REAL (L"right Frequency range (mel)", L"0.0")
+	REAL (L"left Amplitude range (dB)", L"0.0")
+	REAL (L"right Amplitude range (dB)", L"0.0")
+	BOOLEAN (L"Garnish", 1)
+	OK
+DO
+	autoPraatPicture picture;
+	LOOP {
+		iam (MelSpectrogram);
+		BandFilterSpectrogram_drawSpectrumAtNearestTimeSlice (me, GRAPHICS, GET_REAL (L"Time"), GET_REAL (L"left Frequency range"),
+			GET_REAL (L"right Frequency range"), GET_REAL (L"left Amplitude range"),
+			GET_REAL (L"right Amplitude range"), GET_INTEGER (L"Garnish"));
+	}
+END
+
+FORM (BarkSpectrogram_drawSpectrumAtNearestTimeSlice, L"BarkSpectrogram: Draw spectrum at nearest time slice", L"BandFilterSpectrogram: Draw spectrum at nearest time slice...")
+	REAL (L"Time (s)", L"0.1")
+	REAL (L"left Frequency range (bark)", L"0.0")
+	REAL (L"right Frequency range (bark)", L"0.0")
+	REAL (L"left Amplitude range (dB)", L"0.0")
+	REAL (L"right Amplitude range (dB)", L"0.0")
+	BOOLEAN (L"Garnish", 1)
+	OK
+DO
+	autoPraatPicture picture;
+	LOOP {
+		iam (MelSpectrogram);
+		BandFilterSpectrogram_drawSpectrumAtNearestTimeSlice (me, GRAPHICS, GET_REAL (L"Time"), GET_REAL (L"left Frequency range"),
+			GET_REAL (L"right Frequency range"), GET_REAL (L"left Amplitude range"),
+			GET_REAL (L"right Amplitude range"), GET_INTEGER (L"Garnish"));
+	}
+END
+
+FORM (MelFilter_paint, L"FilterBank: Paint", 0)
+	REAL (L"left Time range (s)", L"0.0")
+	REAL (L"right Time range (s)", L"0.0")
+	REAL (L"left Frequency range (mel)", L"0.0")
+	REAL (L"right Frequency range (mel)", L"0.0")
+	REAL (L"left Amplitude range", L"0.0")
+	REAL (L"right Amplitude range", L"0.0")
+	BOOLEAN (L"Garnish", 0)
+	OK
+DO
+	autoPraatPicture picture;
+	LOOP {
+		iam (Matrix);
+		FilterBank_paint ((FilterBank) me, GRAPHICS, GET_REAL (L"left Time range"), GET_REAL (L"right Time range"),
+		GET_REAL (L"left Frequency range"), GET_REAL (L"right Frequency range"),
+		GET_REAL (L"left Amplitude range"), GET_REAL (L"right Amplitude range"), GET_INTEGER (L"Garnish"));
+	}
+END
+
+FORM (MelFilter_to_MFCC, L"MelFilter: To MFCC", L"MelSpectrogram: To MFCC...")
 	NATURAL (L"Number of coefficients", L"12")
 	OK
 DO
@@ -3862,6 +4176,16 @@ DO
 	}
 END
 
+FORM (MelSpectrogram_to_MFCC, L"MelSpectrogram: To MFCC", L"MelSpectrogram: To MFCC...")
+	NATURAL (L"Number of coefficients", L"12")
+	OK
+DO
+	LOOP {
+		iam (MelSpectrogram);
+		praat_new (MelSpectrogram_to_MFCC (me, GET_INTEGER (L"Number of coefficients")), my name);
+	}
+END
+
 /**************** Ltas *******************************************/
 
 #include "UnicodeData.h"
@@ -3897,7 +4221,7 @@ DIRECT (MFCC_help)
 	Melder_help (L"MFCC");
 END
 
-FORM (MFCC_to_MelFilter, L"MFCC: To MelFilter", L"MFCC: To MelFilter...")
+FORM (MFCC_to_MelFilter, L"MFCC: To MelFilter", 0)
 	INTEGER (L"From coefficient", L"0")
 	INTEGER (L"To coefficient", L"0")
 	OK
@@ -3908,6 +4232,19 @@ DO
 	}
 END
 
+FORM (MFCC_to_MelSpectrogram, L"MFCC: MelSpectrogram", L"MFCC: To MelSpectrogram...")
+	INTEGER (L"From coefficient", L"0")
+	INTEGER (L"To coefficient", L"0")
+	BOOLEAN (L"Include constant term", 1)
+	OK
+DO
+	LOOP {
+		iam (MFCC);
+		praat_new (MFCC_to_MelSpectrogram (me, GET_INTEGER (L"From coefficient"), GET_INTEGER (L"To coefficient"),
+			GET_INTEGER (L"Include constant term")), my name);
+	}
+END
+
 FORM (MFCC_to_TableOfReal, L"MFCC: To TableOfReal", L"MFCC: To TableOfReal...")
 	BOOLEAN (L"Include energy", 0)
 	OK
@@ -4983,7 +5320,7 @@ static void Sound_create_checkCommonFields (void *dia, double *startingTime, dou
 	}
 }
 
-FORM (Sound_and_Pitch_to_FormantFilter, L"Sound & Pitch: To FormantFilter", L"Sound & Pitch: To FormantFilter...")
+FORM (Sound_and_Pitch_to_FormantFilter, L"Sound & Pitch: To FormantFilter", L"Sound & Pitch: To Spectrogram...")
 	POSITIVE (L"Analysis window duration (s)", L"0.015")
 	POSITIVE (L"Time step (s)", L"0.005")
 	LABEL (L"", L"Filter bank parameters")
@@ -5001,6 +5338,24 @@ DO
 		GET_REAL (L"Relative bandwidth")), my name, L"_", p->name);
 END
 
+FORM (Sound_and_Pitch_to_Spectrogram, L"Sound & Pitch: To Spectrogram", L"Sound & Pitch: To Spectrogram...")
+	POSITIVE (L"Analysis window duration (s)", L"0.015")
+	POSITIVE (L"Time step (s)", L"0.005")
+	LABEL (L"", L"Filter bank parameters")
+	POSITIVE (L"Position of first filter (Hz)", L"100.0")
+	POSITIVE (L"Distance between filters (Hz)", L"50.0")
+	REAL (L"Maximum frequency", L"0");
+	POSITIVE (L"Relative bandwidth", L"1.1")
+	OK
+DO
+	Sound me = FIRST (Sound);
+	Pitch thee = FIRST (Pitch);
+	praat_new (Sound_and_Pitch_to_Spectrogram (me, thee, GET_REAL (L"Analysis window duration"),
+		GET_REAL (L"Time step"), GET_REAL (L"Position of first filter"),
+		GET_REAL (L"Maximum frequency"), GET_REAL (L"Distance between filters"),
+		GET_REAL (L"Relative bandwidth")), my name, L"_", thy name);
+END
+
 FORM (Sound_and_Pitch_changeGender, L"Sound & Pitch: Change gender", L"Sound & Pitch: Change gender...")
 	POSITIVE (L"Formant shift ratio", L"1.2")
 	REAL (L"New pitch median (Hz)", L"0.0 (=no change)")
@@ -5248,7 +5603,8 @@ DO
 	}
 END
 
-FORM (Sound_to_BarkFilter, L"Sound: To BarkFilter", L"Sound: To BarkFilter...")
+// deprecated
+FORM (Sound_to_BarkFilter, L"Sound: To BarkFilter", L"Sound: To BarkSpectrogram...")
 	POSITIVE (L"Window length (s)", L"0.015")
 	POSITIVE (L"Time step (s)", L"0.005")
 	LABEL (L"", L"Filter bank parameters")
@@ -5265,6 +5621,24 @@ DO
 	}
 END
 
+FORM (Sound_to_BarkSpectrogram, L"Sound: To BarkSpectrogram", L"Sound: To BarkSpectrogram...")
+	POSITIVE (L"Window length (s)", L"0.015")
+	POSITIVE (L"Time step (s)", L"0.005")
+	LABEL (L"", L"Filter bank parameters")
+	POSITIVE (L"Position of first filter (bark)", L"1.0")
+	POSITIVE (L"Distance between filters (bark)", L"1.0")
+	REAL (L"Maximum frequency (bark)", L"0");
+	OK
+DO
+	LOOP {
+		iam (Sound);
+		praat_new (Sound_to_BarkSpectrogram (me, GET_REAL (L"Window length"),
+			GET_REAL (L"Time step"), GET_REAL (L"Position of first filter"),
+			GET_REAL (L"Maximum frequency"), GET_REAL (L"Distance between filters")), my name);
+	}
+END
+
+// deprecated
 FORM (Sound_to_FormantFilter, L"Sound: To FormantFilter", L"Sound: To FormantFilter...")
 	POSITIVE (L"Window length (s)", L"0.015")
 	POSITIVE (L"Time step (s)", L"0.005")
@@ -5288,6 +5662,30 @@ DO
 	}
 END
 
+FORM (Sound_to_Spectrogram_pitchDependent, L"Sound: To Spectrogram (pitch-dependent)", L"Sound: To Spectrogram (pitch-dependent)...")
+	POSITIVE (L"Window length (s)", L"0.015")
+	POSITIVE (L"Time step (s)", L"0.005")
+	LABEL (L"", L"Filter bank parameters")
+	POSITIVE (L"Position of first filter (Hz)", L"100.0")
+	POSITIVE (L"Distance between filters (Hz)", L"50.0")
+	REAL (L"Maximum frequency", L"0");
+	POSITIVE (L"Relative bandwidth", L"1.1")
+	LABEL (L"", L"Pitch analysis")
+	REAL (L"Minimum pitch (Hz)", L"75.0")
+	REAL (L"Maximum pitch (Hz)", L"600.0")
+	OK
+DO
+	LOOP {
+		iam (Sound);
+		praat_new (Sound_to_Spectrogram_pitchDependent (me, GET_REAL (L"Window length"),
+		GET_REAL (L"Time step"), GET_REAL (L"Position of first filter"),
+		GET_REAL (L"Maximum frequency"), GET_REAL (L"Distance between filters"),
+		GET_REAL (L"Relative bandwidth"), GET_REAL (L"Minimum pitch"),
+		GET_REAL (L"Maximum pitch")), my name);
+	}
+END
+
+// deprecated
 FORM (Sound_to_MelFilter, L"Sound: To MelFilter", L"Sound: To MelFilter...")
 	POSITIVE (L"Window length (s)", L"0.015")
 	POSITIVE (L"Time step (s)", L"0.005")
@@ -5305,6 +5703,23 @@ DO
 	}
 END
 
+FORM (Sound_to_MelSpectrogram, L"Sound: To MelSpectrogram", L"Sound: To MelSpectrogram...")
+	POSITIVE (L"Window length (s)", L"0.015")
+	POSITIVE (L"Time step (s)", L"0.005")
+	LABEL (L"", L"Filter bank parameters")
+	POSITIVE (L"Position of first filter (mel)", L"100.0")
+	POSITIVE (L"Distance between filters (mel)", L"100.0")
+	REAL (L"Maximum frequency (mel)", L"0.0");
+	OK
+DO
+	LOOP {
+		iam (Sound);
+		praat_new (Sound_to_MelSpectrogram (me, GET_REAL (L"Window length"),
+		GET_REAL (L"Time step"), GET_REAL (L"Position of first filter"),
+		GET_REAL (L"Maximum frequency"), GET_REAL (L"Distance between filters")), my name);
+	}
+END
+
 FORM (Sound_to_Pitch_shs, L"Sound: To Pitch (shs)", L"Sound: To Pitch (shs)...")
 	POSITIVE (L"Time step (s)", L"0.01")
 	POSITIVE (L"Minimum pitch (Hz)", L"50.0")
@@ -7433,8 +7848,13 @@ void praat_CC_init (ClassInfo klas) {
 	praat_addAction1 (klas, 2, L"To DTW...", 0, 0, DO_CCs_to_DTW);
 }
 
-static void praat_Eigen_Matrix_project (ClassInfo klase, ClassInfo klasm);
+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, L"Project...", 0, praat_HIDDEN, DO_Eigen_and_Matrix_project);
+}
+
+static void praat_Eigen_Spectrogram_project (ClassInfo klase, ClassInfo klasm);
+static void praat_Eigen_Spectrogram_project (ClassInfo klase, ClassInfo klasm) {
 	praat_addAction2 (klase, 1, klasm, 1, L"Project...", 0, 0, DO_Eigen_and_Matrix_project);
 }
 
@@ -7458,35 +7878,62 @@ static void praat_Index_init (ClassInfo klas) {
 	praat_addAction1 (klas, 1, L"Extract part...", 0, 0, DO_Index_extractPart);
 }
 
+static void praat_BandFilterSpectrogram_draw_init (ClassInfo klas);
+static void praat_BandFilterSpectrogram_draw_init (ClassInfo klas) {
+	praat_addAction1 (klas, 0, DRAW_BUTTON, 0, 0, 0);
+//	praat_addAction1 (klas, 0, L"Paint image...", 0, praat_DEPTH_1, DO_BandFilterSpectrogram_paintImage);
+//	praat_addAction1 (klas, 0, L"Draw filters...", 0, 1, DO_FilterBank_drawFilters);
+//	praat_addAction1 (klas, 0, L"Draw one contour...", 0, 1, DO_FilterBank_drawOneContour);
+//	praat_addAction1 (klas, 0, L"Draw contours...", 0, 1, DO_FilterBank_drawContours);
+//	praat_addAction1 (klas, 0, L"Paint contours...", 0, 1, DO_FilterBank_paintContours);
+//	praat_addAction1 (klas, 0, L"Paint cells...", 0, 1, DO_FilterBank_paintCells);
+//	praat_addAction1 (klas, 0, L"Paint surface...", 0, 1, DO_FilterBank_paintSurface);
+	praat_addAction1 (klas, 0, L"-- frequency scales --", 0, 1, 0);
+	praat_addAction1 (klas, 0, L"Draw frequency scale...", 0, 1, DO_BandFilterSpectrogram_drawFrequencyScale);
+}
+
+void praat_Matrixft_query_init (ClassInfo klas);
+void praat_Matrixft_query_init (ClassInfo klas) {
+	praat_TimeFrameSampled_query_init (klas);
+	praat_addAction1 (klas, 1, L"Get time from column...", 0, praat_DEPTH_1 + praat_HIDDEN, DO_BandFilterSpectrogram_getXofColumn);
+	praat_addAction1 (klas, 1, L"-- frequencies --", 0, praat_DEPTH_1 + praat_HIDDEN, 0);
+	praat_addAction1 (klas, 1, L"Get lowest frequency", 0, praat_DEPTH_1 + praat_HIDDEN, DO_BandFilterSpectrogram_getLowestFrequency);
+	praat_addAction1 (klas, 1, L"Get highest frequency", 0, praat_DEPTH_1 + praat_HIDDEN, DO_BandFilterSpectrogram_getHighestFrequency);
+	praat_addAction1 (klas, 1, L"Get number of frequencies", 0, praat_DEPTH_1 + praat_HIDDEN, DO_BandFilterSpectrogram_getNumberOfFrequencies);
+	praat_addAction1 (klas, 1, L"Get frequency distance", 0, praat_DEPTH_1 + praat_HIDDEN, DO_BandFilterSpectrogram_getFrequencyDistance);
+	praat_addAction1 (klas, 1, L"Get frequency from row...", 0, praat_DEPTH_1 + praat_HIDDEN, DO_BandFilterSpectrogram_getFrequencyOfRow);
+	praat_addAction1 (klas, 1, L"-- get value --", 0, praat_DEPTH_1 + praat_HIDDEN, 0);
+	praat_addAction1 (klas, 1, L"Get value in cell...", 0, praat_DEPTH_1 + praat_HIDDEN, DO_BandFilterSpectrogram_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_Matrixft_query_init (klas);
-	praat_addAction1 (klas, 0, L"-- frequency scales --", 0, 1, 0);
-	praat_addAction1 (klas, 1, L"Get frequency in Hertz...", 0, 1, DO_FilterBank_getFrequencyInHertz);
-	praat_addAction1 (klas, 1, L"Get frequency in Bark...", 0, 1, DO_FilterBank_getFrequencyInBark);
-	praat_addAction1 (klas, 1, L"Get frequency in mel...", 0, 1, DO_FilterBank_getFrequencyInMel);
+	praat_addAction1 (klas, 0, L"-- frequency scales --", 0, praat_DEPTH_1 + praat_HIDDEN, 0);
+	praat_addAction1 (klas, 1, L"Get frequency in Hertz...", 0, praat_DEPTH_1 + praat_HIDDEN, DO_FilterBank_getFrequencyInHertz);
+	praat_addAction1 (klas, 1, L"Get frequency in Bark...", 0, praat_DEPTH_1 + praat_HIDDEN, DO_FilterBank_getFrequencyInBark);
+	praat_addAction1 (klas, 1, L"Get frequency in mel...", 0, praat_DEPTH_1 + praat_HIDDEN, 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, L"Equalize intensities...", 0, 1, DO_FilterBank_equalizeIntensities);
+	// praat_addAction1 (klas, 0, MODIFY_BUTTON, 0, 0, 0); 
+	praat_addAction1 (klas, 0, L"Equalize intensities...", 0, praat_DEPTH_1 + praat_HIDDEN, DO_FilterBank_equalizeIntensities);
 }
 
 static void praat_FilterBank_draw_init (ClassInfo klas);
 static void praat_FilterBank_draw_init (ClassInfo klas) {
-	praat_addAction1 (klas, 0, DRAW_BUTTON, 0, 0, 0);
-	praat_addAction1 (klas, 0, L"Draw filters...", 0, 1, DO_FilterBank_drawFilters);
-	praat_addAction1 (klas, 0, L"Draw one contour...", 0, 1, DO_FilterBank_drawOneContour);
-	praat_addAction1 (klas, 0, L"Draw contours...", 0, 1, DO_FilterBank_drawContours);
-	praat_addAction1 (klas, 0, L"Paint image...", 0, 1, DO_FilterBank_paintImage);
-	praat_addAction1 (klas, 0, L"Paint contours...", 0, 1, DO_FilterBank_paintContours);
-	praat_addAction1 (klas, 0, L"Paint cells...", 0, 1, DO_FilterBank_paintCells);
-	praat_addAction1 (klas, 0, L"Paint surface...", 0, 1, DO_FilterBank_paintSurface);
-	praat_addAction1 (klas, 0, L"-- frequency scales --", 0, 1, 0);
-	praat_addAction1 (klas, 0, L"Draw frequency scales...", 0, 1, DO_FilterBank_drawFrequencyScales);
-
+	// praat_addAction1 (klas, 0, DRAW_BUTTON, 0, 0, 0);
+	praat_addAction1 (klas, 0, L"Draw filters...", 0, praat_DEPTH_1 + praat_HIDDEN, DO_FilterBank_drawFilters);
+	praat_addAction1 (klas, 0, L"Draw one contour...", 0, praat_DEPTH_1 + praat_HIDDEN, DO_FilterBank_drawOneContour);
+	praat_addAction1 (klas, 0, L"Draw contours...", 0, praat_DEPTH_1 + praat_HIDDEN, DO_FilterBank_drawContours);
+	praat_addAction1 (klas, 0, L"Paint image...", 0, praat_DEPTH_1 + praat_HIDDEN, DO_FilterBank_paintImage);
+	praat_addAction1 (klas, 0, L"Paint contours...", 0, praat_DEPTH_1 + praat_HIDDEN, DO_FilterBank_paintContours);
+	praat_addAction1 (klas, 0, L"Paint cells...", 0, praat_DEPTH_1 + praat_HIDDEN, DO_FilterBank_paintCells);
+	praat_addAction1 (klas, 0, L"Paint surface...", 0, praat_DEPTH_1 + praat_HIDDEN, DO_FilterBank_paintSurface);
+	praat_addAction1 (klas, 0, L"-- frequency scales --", 0, praat_DEPTH_1 + praat_HIDDEN, 0);
+	praat_addAction1 (klas, 0, L"Draw frequency scales...", 0, praat_DEPTH_1 + praat_HIDDEN, DO_FilterBank_drawFrequencyScales);
 }
 
 static void praat_FilterBank_all_init (ClassInfo klas);
@@ -7494,10 +7941,10 @@ static void praat_FilterBank_all_init (ClassInfo klas) {
 	praat_FilterBank_draw_init (klas);
 	praat_FilterBank_query_init (klas);
 	praat_FilterBank_modify_init (klas);
-	praat_addAction1 (klas, 0, L"To Intensity", 0, 0, DO_FilterBank_to_Intensity);
-	praat_addAction1 (klas, 0, L"To Matrix", 0, 0, DO_FilterBank_to_Matrix);
-	praat_addAction1 (klas, 2, L"Cross-correlate...", 0, 0, DO_FilterBanks_crossCorrelate);
-	praat_addAction1 (klas, 2, L"Convolve...", 0, 0, DO_FilterBanks_convolve);
+	praat_addAction1 (klas, 0, L"To Intensity", 0, praat_HIDDEN, DO_FilterBank_to_Intensity);
+	praat_addAction1 (klas, 0, L"To Matrix", 0, praat_HIDDEN, DO_FilterBank_to_Matrix);
+	praat_addAction1 (klas, 2, L"Cross-correlate...", 0, praat_HIDDEN, DO_FilterBanks_crossCorrelate);
+	praat_addAction1 (klas, 2, L"Convolve...", 0, praat_HIDDEN, DO_FilterBanks_convolve);
 }
 
 static void praat_FunctionTerms_init (ClassInfo klas) {
@@ -7522,17 +7969,17 @@ static void praat_FunctionTerms_init (ClassInfo klas) {
 
 /* Query buttons for frame-based frequency x time subclasses of matrix. */
 
-void praat_Matrixft_query_init (ClassInfo klas) {
+void praat_BandFilterSpectrogram_query_init (ClassInfo klas) {
 	praat_TimeFrameSampled_query_init (klas);
-	praat_addAction1 (klas, 1, L"Get time from column...", 0, 1, DO_Matrixft_getXofColumn);
+	praat_addAction1 (klas, 1, L"Get time from column...", 0, 1, DO_BandFilterSpectrogram_getXofColumn);
 	praat_addAction1 (klas, 1, L"-- frequencies --", 0, 1, 0);
-	praat_addAction1 (klas, 1, L"Get lowest frequency", 0, 1, DO_Matrixft_getLowestFrequency);
-	praat_addAction1 (klas, 1, L"Get highest frequency", 0, 1, DO_Matrixft_getHighestFrequency);
-	praat_addAction1 (klas, 1, L"Get number of frequencies", 0, 1, DO_Matrixft_getNumberOfFrequencies);
-	praat_addAction1 (klas, 1, L"Get frequency distance", 0, 1, DO_Matrixft_getFrequencyDistance);
-	praat_addAction1 (klas, 1, L"Get frequency from row...", 0, 1, DO_Matrixft_getFrequencyOfRow);
+	praat_addAction1 (klas, 1, L"Get lowest frequency", 0, 1, DO_BandFilterSpectrogram_getLowestFrequency);
+	praat_addAction1 (klas, 1, L"Get highest frequency", 0, 1, DO_BandFilterSpectrogram_getHighestFrequency);
+	praat_addAction1 (klas, 1, L"Get number of frequencies", 0, 1, DO_BandFilterSpectrogram_getNumberOfFrequencies);
+	praat_addAction1 (klas, 1, L"Get frequency distance", 0, 1, DO_BandFilterSpectrogram_getFrequencyDistance);
+	praat_addAction1 (klas, 1, L"Get frequency from row...", 0, 1, DO_BandFilterSpectrogram_getFrequencyOfRow);
 	praat_addAction1 (klas, 1, L"-- get value --", 0, 1, 0);
-	praat_addAction1 (klas, 1, L"Get value in cell...", 0, 1, DO_Matrixft_getValueInCell);
+	praat_addAction1 (klas, 1, L"Get value in cell...", 0, 1, DO_BandFilterSpectrogram_getValueInCell);
 }
 
 static void praat_Spline_init (ClassInfo klas) {
@@ -7540,7 +7987,6 @@ static void praat_Spline_init (ClassInfo klas) {
 	praat_addAction1 (klas, 0, L"Draw knots...", L"Draw basis function...", 1, DO_Spline_drawKnots);
 	praat_addAction1 (klas, 1, L"Get order", L"Get degree", 1, DO_Spline_getOrder);
 	praat_addAction1 (klas, 1, L"Scale x...", L"Analyse",	0, DO_Spline_scaleX);
-
 }
 
 static void praat_SSCP_query_init (ClassInfo klas) {
@@ -7612,7 +8058,7 @@ void praat_uvafon_David_init () {
 	Data_recognizeFileType (TextGrid_TIMITLabelFileRecognizer);
 	Data_recognizeFileType (cmuAudioFileRecognizer);
 
-	Thing_recognizeClassesByName (classActivation, classBarkFilter,
+	Thing_recognizeClassesByName (classActivation, classBarkFilter, classBarkSpectrogram,
 		classCategories, classCepstrum, classCCA,
 		classChebyshevSeries, classClassificationTable, classConfusion,
 		classCorrelation, classCovariance, classDiscriminant, classDTW,
@@ -7620,7 +8066,7 @@ void praat_uvafon_David_init () {
 		classFileInMemory, classFilesInMemory, classFormantFilter,
 		classIndex, classKlattTable,
 		classPermutation, classISpline, classLegendreSeries,
-		classMelFilter, classMSpline, classPattern, classPCA, classPolynomial, classRoots,
+		classMelFilter, classMelSpectrogram, classMSpline, classPattern, classPCA, classPolynomial, classRoots,
 		classSimpleString, classStringsIndex, classSpeechSynthesizer, classSPINET, classSSCP,
 		classSVD, NULL);
 
@@ -7682,10 +8128,26 @@ void praat_uvafon_David_init () {
 	praat_addAction2 (classActivation, 1, classCategories, 1, L"To TableOfReal", 0, 0, DO_Matrix_Categories_to_TableOfReal);
 
 	praat_addAction1 (classBarkFilter, 0, L"BarkFilter help", 0, 0, DO_BarkFilter_help);
-	praat_FilterBank_all_init (classBarkFilter);
-	praat_addAction1 (classBarkFilter, 0, L"Draw spectrum (slice)...", L"Draw filters...", 1, DO_BarkFilter_drawSpectrum);
-	praat_addAction1 (classBarkFilter, 1, L"Draw filter functions...", L"Draw filters...", 1, DO_BarkFilter_drawSekeyHansonFilterFunctions);
-
+	praat_FilterBank_all_init (classBarkFilter);	// deprecated 2014
+	praat_addAction1 (classBarkFilter, 0, L"Draw spectrum (slice)...", L"Draw filters...", praat_DEPTH_1 + praat_HIDDEN, DO_BarkFilter_drawSpectrum);	// deprecated 2014
+	praat_addAction1 (classBarkFilter, 1, L"Draw filter functions...", L"Draw filters...", praat_DEPTH_1 + praat_HIDDEN, DO_BarkFilter_drawSekeyHansonFilterFunctions);	// deprecated 2014
+	praat_addAction1 (classBarkFilter, 0, L"Paint...", L"Draw filters...", praat_DEPTH_1 + praat_HIDDEN, DO_BarkFilter_paint);	// deprecated 2014
+	praat_addAction1 (classBarkFilter, 0, L"To BarkSpectrogram", 0, 0, DO_BarkFilter_to_BarkSpectrogram);
+
+	praat_addAction1 (classBarkSpectrogram, 0, L"BarkSpectrogram help", 0, 0, DO_BarkSpectrogram_help);
+	praat_BandFilterSpectrogram_draw_init (classBarkSpectrogram);
+	praat_addAction1 (classBarkSpectrogram, 0, L"Paint image...", 0, 1, DO_BarkSpectrogram_paintImage);
+	praat_addAction1 (classBarkSpectrogram, 0, L"Draw Sekey-Hanson auditory filters...", 0, 1, DO_BarkSpectrogram_drawSekeyHansonAuditoryFilters);
+	praat_addAction1 (classBarkSpectrogram, 0, L"Draw spectrum at nearest time slice...", 0, 1, DO_BarkSpectrogram_drawSpectrumAtNearestTimeSlice);
+	praat_addAction1 (classBarkSpectrogram, 0, QUERY_BUTTON, 0, 0, 0);
+	praat_BandFilterSpectrogram_query_init (classBarkSpectrogram);
+	praat_addAction1 (classBarkSpectrogram, 0, L"Equalize intensities...", 0, 0, DO_BandFilterSpectrogram_equalizeIntensities);
+	praat_addAction1 (classBarkSpectrogram, 0, L"To Intensity", 0, 0, DO_BandFilterSpectrogram_to_Intensity);
+	praat_addAction1 (classBarkSpectrogram, 0, L"To Matrix...", 0, 0, DO_BandFilterSpectrogram_to_Matrix);
+	praat_addAction1 (classBarkSpectrogram, 2, L"Cross-correlate...", 0, 0, DO_BandFilterSpectrograms_crossCorrelate);
+	praat_addAction1 (classBarkSpectrogram, 2, L"Convolve...", 0, 0, DO_BandFilterSpectrograms_convolve);
+	
+	
 	praat_addAction1 (classCategories, 0, L"Edit", 0, 0, DO_Categories_edit);
 	praat_addAction1 (classCategories, 0, QUERY_BUTTON, 0, 0, 0);
 	praat_addAction1 (classCategories, 1, L"Get number of categories", QUERY_BUTTON, 1, DO_Categories_getNumberOfCategories);
@@ -7759,6 +8221,8 @@ void praat_uvafon_David_init () {
 	praat_addAction1 (classConfusion, 0, L"Group responses...", 0, 0, DO_Confusion_groupResponses);
 	praat_addAction1 (classConfusion, 2, L"To difference matrix", 0, 0,
 	                  DO_Confusion_difference);
+	
+	praat_addAction2 (classConfusion, 1, classClassificationTable, 1, L"Increase confusion count", 0, 0, DO_Confusion_and_ClassificationTable_increase);
 
 	praat_addAction2 (classConfusion, 1, classMatrix, 1, L"Draw", 0, 0, 0);
 	praat_addAction2 (classConfusion, 1, classMatrix, 1, L"Draw confusion...",
@@ -7789,7 +8253,10 @@ void praat_uvafon_David_init () {
 
 	praat_addAction1 (classClassificationTable, 0, L"ClassificationTable help", 0, 0, DO_ClassificationTable_help);
 	praat_TableOfReal_init (classClassificationTable);
-	praat_addAction1 (classClassificationTable, 0, L"To Confusion", 0, 0, DO_ClassificationTable_to_Confusion);
+	praat_addAction1 (classClassificationTable, 0, L"Get class index at maximum in row...", L"Get column index...", 1, DO_ClassificationTable_getClassIndexAtMaximumInRow);
+	praat_addAction1 (classClassificationTable, 0, L"Get class label at maximum in row...",L"Get class index at maximum in row...", 1, DO_ClassificationTable_getClassLabelAtMaximumInRow);
+	praat_addAction1 (classClassificationTable, 0, L"To Confusion", 0, praat_HIDDEN, DO_ClassificationTable_to_Confusion_old); // deprecated 2014
+	praat_addAction1 (classClassificationTable, 0, L"To Confusion...", 0, 0, DO_ClassificationTable_to_Confusion);
 	praat_addAction1 (classClassificationTable, 0, L"To Correlation (columns)", 0, 0, DO_ClassificationTable_to_Correlation_columns);
 	praat_addAction1 (classClassificationTable, 0, L"To Strings (max. prob.)", 0, 0, DO_ClassificationTable_to_Strings_maximumProbability);
 
@@ -7850,10 +8317,13 @@ void praat_uvafon_David_init () {
 	/*		praat_addAction1 (classDiscriminant, 1, L"Extract coefficients...", 0, 1, DO_Discriminant_extractCoefficients);*/
 
 
-
-	praat_Eigen_Matrix_project (classDiscriminant, classFormantFilter);
-	praat_Eigen_Matrix_project (classDiscriminant, classBarkFilter);
-	praat_Eigen_Matrix_project (classDiscriminant, classMelFilter);
+	praat_Eigen_Spectrogram_project (classDiscriminant, classSpectrogram);
+	praat_Eigen_Spectrogram_project (classDiscriminant, classBarkSpectrogram);
+	praat_Eigen_Spectrogram_project (classDiscriminant, classMelSpectrogram);
+	
+	praat_Eigen_Matrix_project (classDiscriminant, classFormantFilter); // deprecated 2014
+	praat_Eigen_Matrix_project (classDiscriminant, classBarkFilter); // deprecated 2014
+	praat_Eigen_Matrix_project (classDiscriminant, classMelFilter); // deprecated 2014
 
 	praat_addAction2 (classDiscriminant, 1, classPattern, 1, L"To Categories...", 0, 0, DO_Discriminant_and_Pattern_to_Categories);
 	praat_addAction2 (classDiscriminant, 1, classSSCP, 1, L"Project", 0, 0, DO_Eigen_and_SSCP_project);
@@ -7981,9 +8451,12 @@ void praat_uvafon_David_init () {
 
 	praat_addAction1 (classFormantFilter, 0, L"FormantFilter help", 0, 0, DO_FormantFilter_help);
 	praat_FilterBank_all_init (classFormantFilter);
-	praat_addAction1 (classFormantFilter, 0, L"Draw spectrum (slice)...", L"Draw filters...", 1, DO_FormantFilter_drawSpectrum);
-	praat_addAction1 (classFormantFilter, 0, L"Draw filter functions...", L"Draw filters...", 1, DO_FormantFilter_drawFilterFunctions);
-	praat_addAction1 (classFormantGrid, 0, L"Draw...", L"Edit", 1, DO_FormantGrid_draw);
+	praat_addAction1 (classFormantFilter, 0, L"Draw spectrum (slice)...", L"Draw filters...", praat_DEPTH_1 + praat_HIDDEN, DO_FormantFilter_drawSpectrum);
+	praat_addAction1 (classFormantFilter, 0, L"Draw filter functions...", L"Draw filters...",  praat_DEPTH_1 + praat_HIDDEN, DO_FormantFilter_drawFilterFunctions);
+	praat_addAction1 (classFormantFilter, 0, L"To Spectrogram", 0, 0, DO_FormantFilter_to_Spectrogram);
+	
+	
+	praat_addAction1 (classFormantGrid, 0, L"Draw...", L"Edit", praat_DEPTH_1 + praat_HIDDEN, DO_FormantGrid_draw);
 
 
 	praat_addAction1 (classIntensity, 0, L"To TextGrid (silences)...", L"To IntensityTier (valleys)", 0, DO_Intensity_to_TextGrid_detectSilences);
@@ -8033,15 +8506,35 @@ void praat_uvafon_David_init () {
 
 	praat_addAction2 (classMatrix, 1, classCategories, 1, L"To TableOfReal", 0, 0, DO_Matrix_Categories_to_TableOfReal);
 
-	praat_addAction1 (classMelFilter, 0, L"MelFilter help", 0, 0, DO_MelFilter_help);
-	praat_FilterBank_all_init (classMelFilter);
-	praat_addAction1 (classMelFilter, 0, L"Draw spectrum (slice)...", L"Draw filters...", 1, DO_MelFilter_drawSpectrum);
-	praat_addAction1 (classMelFilter, 0, L"Draw filter functions...", L"Draw filters...", 1, DO_MelFilter_drawFilterFunctions);
-	praat_addAction1 (classMelFilter, 0, L"To MFCC...", 0, 0, DO_MelFilter_to_MFCC);
+	
+	praat_addAction1 (classMelSpectrogram, 0, L"MelSpectrogram help", 0, 0, DO_MelSpectrogram_help);
+	praat_BandFilterSpectrogram_draw_init (classMelSpectrogram);
+	praat_addAction1 (classMelSpectrogram, 0, L"Paint image...", 0, 1, DO_MelSpectrogram_paintImage);
+	praat_addAction1 (classMelSpectrogram, 0, L"Draw triangular filter functions...", 0, 1, DO_MelSpectrogram_drawTriangularFilterFunctions);
+	praat_addAction1 (classMelSpectrogram, 0, L"Draw spectrum at nearest time slice...", 0, 1, DO_MelSpectrogram_drawSpectrumAtNearestTimeSlice);
+	praat_addAction1 (classMelSpectrogram, 0, QUERY_BUTTON, 0, 0, 0);
+	praat_BandFilterSpectrogram_query_init (classMelSpectrogram);
+
+	praat_addAction1 (classMelSpectrogram, 0, L"Equalize intensities...", 0, 0, DO_BandFilterSpectrogram_equalizeIntensities);
+	praat_addAction1 (classMelSpectrogram, 0, L"To MFCC...", 0, 0, DO_MelSpectrogram_to_MFCC);
+	praat_addAction1 (classMelSpectrogram, 0, L"To Intensity", 0, 0, DO_BandFilterSpectrogram_to_Intensity);
+	praat_addAction1 (classMelSpectrogram, 0, L"To Matrix...", 0, 0, DO_BandFilterSpectrogram_to_Matrix);
+	praat_addAction1 (classMelSpectrogram, 2, L"Cross-correlate...", 0, 0, DO_BandFilterSpectrograms_crossCorrelate);
+	praat_addAction1 (classMelSpectrogram, 2, L"Convolve...", 0, 0, DO_BandFilterSpectrograms_convolve);
+	
+	praat_addAction1 (classMelFilter, 0, L"MelFilter help", 0, 0, DO_MelFilter_help); // deprecated 2014
+	praat_FilterBank_all_init (classMelFilter); // deprecated 2014
+	praat_addAction1 (classMelFilter, 0, L"Draw spectrum (slice)...", L"Draw filters...", praat_DEPTH_1 + praat_HIDDEN, DO_MelFilter_drawSpectrum); // deprecated 2014
+	praat_addAction1 (classMelFilter, 0, L"Draw filter functions...", L"Draw filters...", praat_DEPTH_1 + praat_HIDDEN, DO_MelFilter_drawFilterFunctions); // deprecated 2014
+	praat_addAction1 (classMelFilter, 0, L"Paint...", L"Draw filter functions...", praat_DEPTH_1 + praat_HIDDEN, DO_MelFilter_paint); // deprecated 2014
+	praat_addAction1 (classMelFilter, 0, L"To MFCC...", 0, praat_HIDDEN, DO_MelFilter_to_MFCC); // deprecated 2014
+	praat_addAction1 (classMelFilter, 0, L"To MelSpectrogram", 0, 0, DO_MelFilter_to_MelSpectrogram);
 
+	
 	praat_addAction1 (classMFCC, 0, L"MFCC help", 0, 0, DO_MFCC_help);
 	praat_CC_init (classMFCC);
-	praat_addAction1 (classMFCC, 0, L"To MelFilter...", 0, 0, DO_MFCC_to_MelFilter);
+	praat_addAction1 (classMFCC, 0, L"To MelFilter...", 0, praat_HIDDEN, DO_MFCC_to_MelFilter);
+	praat_addAction1 (classMFCC, 0, L"To MelSpectrogram...", 0, 0, DO_MFCC_to_MelSpectrogram);
 	praat_addAction1 (classMFCC, 0, L"To TableOfReal...", 0, 0, DO_MFCC_to_TableOfReal);
 	praat_addAction1 (classMFCC, 0, L"To Matrix (features)...", 0, praat_HIDDEN, DO_MFCC_to_Matrix_features);
 	praat_addAction1 (classMFCC, 0, L"To Sound", 0, praat_HIDDEN, DO_MFCC_to_Sound);
@@ -8088,9 +8581,13 @@ void praat_uvafon_David_init () {
 	praat_addAction2 (classPCA, 1, classTableOfReal, 1, L"Get fraction variance...", 0, 0, DO_PCA_and_TableOfReal_getFractionVariance);
 	praat_addAction2 (classPCA, 1, classCovariance, 1, L"Project", 0, 0, DO_Eigen_and_Covariance_project);
 
-	praat_Eigen_Matrix_project (classPCA, classFormantFilter);
-	praat_Eigen_Matrix_project (classPCA, classBarkFilter);
-	praat_Eigen_Matrix_project (classPCA, classMelFilter);
+	praat_Eigen_Spectrogram_project (classPCA, classSpectrogram);
+	praat_Eigen_Spectrogram_project (classPCA, classBarkSpectrogram);
+	praat_Eigen_Spectrogram_project (classPCA, classMelSpectrogram);
+
+	praat_Eigen_Matrix_project (classPCA, classFormantFilter); // deprecated 2014
+	praat_Eigen_Matrix_project (classPCA, classBarkFilter); // deprecated 2014
+	praat_Eigen_Matrix_project (classPCA, classMelFilter); // deprecated 2014
 
 	praat_addAction1 (classPermutation, 0, L"Permutation help", 0, 0, DO_Permutation_help);
 	praat_addAction1 (classPermutation, 0, QUERY_BUTTON, 0, 0, 0);
@@ -8177,11 +8674,14 @@ void praat_uvafon_David_init () {
 	praat_addAction1 (classSound, 0, L"Fade out...", L"Fade in...", praat_HIDDEN + praat_DEPTH_1, DO_Sound_fadeOut);
 	praat_addAction1 (classSound, 0, L"To Pitch (SPINET)...", L"To Pitch (cc)...", 1, DO_Sound_to_Pitch_SPINET);
 
-	praat_addAction1 (classSound, 0, L"To FormantFilter...", L"To Cochleagram (edb)...", 1, DO_Sound_to_FormantFilter);
+	praat_addAction1 (classSound, 0, L"To FormantFilter...", L"To Cochleagram (edb)...", praat_HIDDEN + praat_DEPTH_1, DO_Sound_to_FormantFilter);
+	praat_addAction1 (classSound, 0, L"To Spectrogram (pitch-dependent)...", L"To Cochleagram (edb)...", 1, DO_Sound_to_Spectrogram_pitchDependent);
 
-	praat_addAction1 (classSound, 0, L"To BarkFilter...", L"To FormantFilter...", 1, DO_Sound_to_BarkFilter);
+	praat_addAction1 (classSound, 0, L"To BarkFilter...", L"To FormantFilter...", praat_HIDDEN + praat_DEPTH_1, DO_Sound_to_BarkFilter); // deprecated 2014
+	praat_addAction1 (classSound, 0, L"To BarkSpectrogram...", L"To FormantFilter...", praat_DEPTH_1, DO_Sound_to_BarkSpectrogram);
 
-	praat_addAction1 (classSound, 0, L"To MelFilter...", L"To BarkFilter...", 1, DO_Sound_to_MelFilter);
+	praat_addAction1 (classSound, 0, L"To MelFilter...", L"To BarkFilter...", praat_HIDDEN + praat_DEPTH_1, DO_Sound_to_MelFilter); // deprecated 2014
+	praat_addAction1 (classSound, 0, L"To MelSpectrogram...", L"To BarkSpectrogram...", praat_DEPTH_1, DO_Sound_to_MelSpectrogram);
 
 	praat_addAction1 (classSound, 0, L"To Polygon...", L"Down to Matrix", praat_DEPTH_1 | praat_HIDDEN, DO_Sound_to_Polygon);
     praat_addAction1 (classSound, 2, L"To Polygon (enclosed)...", L"Cross-correlate...", praat_DEPTH_1 | praat_HIDDEN, DO_Sounds_to_Polygon_enclosed);
@@ -8196,7 +8696,8 @@ void praat_uvafon_David_init () {
 	praat_addAction1 (classSound, 0, L"Copy channel ranges...", L"Extract all channels", praat_DEPTH_1 | praat_HIDDEN, DO_Sound_copyChannelRanges);
 	praat_addAction1 (classSound, 0, L"Trim silences...", L"Resample...", praat_DEPTH_1 | praat_HIDDEN, DO_Sound_trimSilences);
 	praat_addAction1 (classSound, 0, L"To KlattGrid (simple)...", L"To Manipulation...", 1, DO_Sound_to_KlattGrid_simple);
-	praat_addAction2 (classSound, 1, classPitch, 1, L"To FormantFilter...", 0, 0, DO_Sound_and_Pitch_to_FormantFilter);
+	praat_addAction2 (classSound, 1, classPitch, 1, L"To FormantFilter...", 0, praat_HIDDEN, DO_Sound_and_Pitch_to_FormantFilter);
+	praat_addAction2 (classSound, 1, classPitch, 1, L"To Spectrogram (pitch-dependent)...", 0, 0, DO_Sound_and_Pitch_to_Spectrogram);
 
 	praat_addAction2 (classSound, 1, classPitch, 1, L"Change gender...", 0, 0, DO_Sound_and_Pitch_changeGender);
 	praat_addAction2 (classSound, 1, classPitch, 1, L"Change speaker...", 0, praat_HIDDEN, DO_Sound_and_Pitch_changeSpeaker);
diff --git a/external/espeak/speech.h b/external/espeak/speech.h
index 7f1dc96..24c8bfd 100644
--- a/external/espeak/speech.h
+++ b/external/espeak/speech.h
@@ -26,7 +26,7 @@
 
 #ifdef _WIN32
 	wchar_t * Melder_peekUtf8ToWcs (const char *string);
-	const uint16_t * Melder_peekWcsToUtf16 (const wchar_t *string);
+	const utf16_t * Melder_peekWcsToUtf16 (const wchar_t *string);
 #endif
 
 // conditional compilation options
diff --git a/external/flac/READ_ME.TXT b/external/flac/READ_ME.TXT
index 9ccb292..9d933b6 100644
--- a/external/flac/READ_ME.TXT
+++ b/external/flac/READ_ME.TXT
@@ -1,5 +1,5 @@
 Praats/external/flac/READ_ME.TXT
-Paul Boersma, 26 December 2008
+Paul Boersma, 18 December 2014
 This file describes the adaptations to the FLAC 1.2.1 sources
 that are needed to make them compatible with Praat.
 
@@ -18,19 +18,6 @@ For MinGW we need to change in flac_share_alloc.h:
 #include <stdint.h> /* for SIZE_MAX in case limits.h didn't get it */
 #endif
 
-
-For win32 on Metrowerks CodeWarrior we do a
-#if defined (_WIN32) && ! defined (off_t)
-#define off_t long
-#endif
-just above the declaration of
-FLAC__metadata_simple_iterator_get_block_offset;
-we also do
-#define fseeko fseek
-#define ftello ftell
-on win32 on Metrowerks CodeWarrior.
-This measure may be expendable once we compile on mingw or so.
-
 The sources contain a confusion of FLAC__int32 and int,
 especially in calls to local_bitreader_read_rice_signed_block
 or FLAC__bitreader_read_rice_signed_block;
diff --git a/external/flac/flac_FLAC_metadata.h b/external/flac/flac_FLAC_metadata.h
index e5e8ec0..5dd3539 100644
--- a/external/flac/flac_FLAC_metadata.h
+++ b/external/flac/flac_FLAC_metadata.h
@@ -471,9 +471,6 @@ FLAC_API FLAC__bool FLAC__metadata_simple_iterator_is_last(const FLAC__Metadata_
  *    This is the byte offset relative to the beginning of the file of
  *    the current metadata block's header.
  */
-#if defined (_WIN32) && ! defined (off_t)
-#define off_t long
-#endif
 FLAC_API off_t FLAC__metadata_simple_iterator_get_block_offset(const FLAC__Metadata_SimpleIterator *iterator);
 
 /** Get the type of the metadata block at the current position.  This
diff --git a/external/flac/flac_metadata_iterators.c b/external/flac/flac_metadata_iterators.c
index 2f6c13d..5e662c3 100644
--- a/external/flac/flac_metadata_iterators.c
+++ b/external/flac/flac_metadata_iterators.c
@@ -42,8 +42,6 @@
 #include <utime.h> /* for utime() */
 #include <io.h> /* for chmod() */
 #include <sys/types.h> /* for off_t */
-#define fseeko fseek
-#define ftello ftell
 #else
 #include <sys/types.h> /* some flavors of BSD (like OS X) require this to get time_t */
 #include <utime.h> /* for utime() */
diff --git a/external/flac/flac_stream_decoder.c b/external/flac/flac_stream_decoder.c
index a4ec6bb..ef053fc 100644
--- a/external/flac/flac_stream_decoder.c
+++ b/external/flac/flac_stream_decoder.c
@@ -42,10 +42,6 @@
 #include <string.h> /* for memset/memcpy() */
 #include <sys/stat.h> /* for stat() */
 #include <sys/types.h> /* for off_t */
-#if defined _WIN32
-#define fseeko fseek
-#define ftello ftell
-#endif
 #include "flac_FLAC_assert.h"
 #include "flac_share_alloc.h"
 #include "flac_protected_stream_decoder.h"
diff --git a/external/flac/flac_stream_encoder.c b/external/flac/flac_stream_encoder.c
index affde5b..65f18a9 100644
--- a/external/flac/flac_stream_encoder.c
+++ b/external/flac/flac_stream_encoder.c
@@ -42,10 +42,6 @@
 #include <stdlib.h> /* for malloc() */
 #include <string.h> /* for memcpy() */
 #include <sys/types.h> /* for off_t */
-#if defined _WIN32
-#define fseeko fseek
-#define ftello ftell
-#endif
 #include "flac_FLAC_assert.h"
 #include "flac_FLAC_stream_decoder.h"
 #include "flac_share_alloc.h"
diff --git a/fon/FunctionEditor.cpp b/fon/FunctionEditor.cpp
index 3c80a56..908e0a6 100644
--- a/fon/FunctionEditor.cpp
+++ b/fon/FunctionEditor.cpp
@@ -191,11 +191,13 @@ static void drawNow (FunctionEditor me) {
 	Graphics_fillRectangle (my d_graphics, my functionViewerLeft + MARGIN, my selectionViewerRight - MARGIN, my height - (TOP_MARGIN + space), my height);
 	Graphics_fillRectangle (my d_graphics, my functionViewerLeft, my functionViewerLeft + MARGIN, BOTTOM_MARGIN + ( leftFromWindow ? space * 2 : 0 ), my height);
 	Graphics_fillRectangle (my d_graphics, my functionViewerRight - MARGIN, my functionViewerRight, BOTTOM_MARGIN + ( rightFromWindow ? space * 2 : 0 ), my height);
-	Graphics_setViewport (my d_graphics, my selectionViewerLeft, my selectionViewerRight, 0, my height);
-	Graphics_setWindow (my d_graphics, my selectionViewerLeft, my selectionViewerRight, 0, my height);
-	Graphics_fillRectangle (my d_graphics, my selectionViewerLeft, my selectionViewerLeft + MARGIN, BOTTOM_MARGIN, my height);
-	Graphics_fillRectangle (my d_graphics, my selectionViewerRight - MARGIN, my selectionViewerRight, BOTTOM_MARGIN, my height);
-	Graphics_fillRectangle (my d_graphics, my selectionViewerLeft + MARGIN, my selectionViewerRight - MARGIN, 0, BOTTOM_MARGIN + space * 3);
+	if (my p_showSelectionViewer) {
+		Graphics_setViewport (my d_graphics, my selectionViewerLeft, my selectionViewerRight, 0, my height);
+		Graphics_setWindow (my d_graphics, my selectionViewerLeft, my selectionViewerRight, 0, my height);
+		Graphics_fillRectangle (my d_graphics, my selectionViewerLeft, my selectionViewerLeft + MARGIN, BOTTOM_MARGIN, my height);
+		Graphics_fillRectangle (my d_graphics, my selectionViewerRight - MARGIN, my selectionViewerRight, BOTTOM_MARGIN, my height);
+		Graphics_fillRectangle (my d_graphics, my selectionViewerLeft + MARGIN, my selectionViewerRight - MARGIN, 0, BOTTOM_MARGIN + space * 3);
+	}
 	Graphics_setGrey (my d_graphics, 0.0);
 	#if defined (macintosh)
 		Graphics_line (my d_graphics, my functionViewerLeft, 2, my selectionViewerRight, 2);
diff --git a/fon/Sampled.cpp b/fon/Sampled.cpp
index a545be7..385dcdb 100644
--- a/fon/Sampled.cpp
+++ b/fon/Sampled.cpp
@@ -43,13 +43,13 @@ Thing_implement (Sampled, Function, 0);
 
 void structSampled :: v_shiftX (double xfrom, double xto) {
 	Sampled_Parent :: v_shiftX (xfrom, xto);
-	NUMshift (& x1, xfrom, xto);
+	NUMshift (& our x1, xfrom, xto);
 }
 
 void structSampled :: v_scaleX (double xminfrom, double xmaxfrom, double xminto, double xmaxto) {
 	Sampled_Parent :: v_scaleX (xminfrom, xmaxfrom, xminto, xmaxto);
-	NUMscale (& x1, xminfrom, xmaxfrom, xminto, xmaxto);
-	dx *= (xmaxto - xminto) / (xmaxfrom - xminfrom);
+	NUMscale (& our x1, xminfrom, xmaxfrom, xminto, xmaxto);
+	our dx *= (xmaxto - xminto) / (xmaxfrom - xminfrom);
 }
 
 long Sampled_getWindowSamples (Sampled me, double xmin, double xmax, long *ixmin, long *ixmax) {
diff --git a/fon/Sound.cpp b/fon/Sound.cpp
index b4a1477..055aeb7 100644
--- a/fon/Sound.cpp
+++ b/fon/Sound.cpp
@@ -134,7 +134,12 @@ Sound Sound_create (long numberOfChannels, double xmin, double xmax, long nx, do
 }
 
 Sound Sound_createSimple (long numberOfChannels, double duration, double samplingFrequency) {
-	return Sound_create (numberOfChannels, 0.0, duration, floor (duration * samplingFrequency + 0.5),
+	Melder_assert (duration > 0.0);
+	Melder_assert (samplingFrequency > 0.0);
+	double numberOfSamples_f = round (duration * samplingFrequency);
+	if (numberOfSamples_f > (double) INT32_MAX)
+		Melder_throw ("Cannot create sounds with more than ", Melder_bigInteger (INT32_MAX), " samples, because they cannot be saved to disk.");
+	return Sound_create (numberOfChannels, 0.0, duration, (long) (int32_t) numberOfSamples_f,
 		1 / samplingFrequency, 0.5 / samplingFrequency);
 }
 
@@ -891,7 +896,10 @@ Sound Sound_createAsPureTone (long numberOfChannels, double startingTime, double
 	double sampleRate, double frequency, double amplitude, double fadeInDuration, double fadeOutDuration)
 {
 	try {
-		autoSound me = Sound_create (numberOfChannels, startingTime, endTime, round ((endTime - startingTime) * sampleRate),
+		double numberOfSamples_f = round ((endTime - startingTime) * sampleRate);
+		if (numberOfSamples_f > (double) INT32_MAX)
+			Melder_throw ("Cannot create sounds with more than ", Melder_bigInteger (INT32_MAX), " samples, because they cannot be saved to disk.");
+		autoSound me = Sound_create (numberOfChannels, startingTime, endTime, (long) numberOfSamples_f,
 			1 / sampleRate, startingTime + 0.5 / sampleRate);
 		for (long isamp = 1; isamp <= my nx; isamp ++) {
 			double time = my x1 + (isamp - 1) * my dx;
diff --git a/fon/SoundRecorder.cpp b/fon/SoundRecorder.cpp
index b9e0862..c45c341 100644
--- a/fon/SoundRecorder.cpp
+++ b/fon/SoundRecorder.cpp
@@ -553,11 +553,11 @@ static void gui_button_cb_record (I, GuiButtonEvent event) {
 				if (Melder_debug == 20) Melder_casual ("Before Pa_OpenStream");
 				PaError err = Pa_OpenStream (& my portaudioStream, & streamParameters, NULL,
 					theControlPanel. sampleRate, 0, paNoFlag, portaudioStreamCallback, (void *) me);
-				if (Melder_debug == 20) Melder_casual ("Pa_OpenStream returns %d", err);
+				if (Melder_debug == 20) Melder_casual ("Pa_OpenStream returns %d", (int) err);
 				if (err)
 					Melder_throw ("open ", Melder_peekUtf8ToWcs (Pa_GetErrorText (err)));
 				Pa_StartStream (my portaudioStream);
-				if (Melder_debug == 20) Melder_casual ("Pa_StartStream returns %d", err);
+				if (Melder_debug == 20) Melder_casual ("Pa_StartStream returns %d", (int) err);
 				if (err)
 					Melder_throw ("start ", Melder_peekUtf8ToWcs (Pa_GetErrorText (err)));
 			} else {
@@ -1105,7 +1105,7 @@ SoundRecorder SoundRecorder_create (int numberOfChannels) {
 			for (PaDeviceIndex idevice = 0; idevice < deviceCount; idevice ++) {
 				const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo (idevice);
 				if (Melder_debug == 20) Melder_casual ("Device \"%s\", input %d, output %d, sample rate %lf", deviceInfo -> name,
-					deviceInfo -> maxInputChannels, deviceInfo -> maxOutputChannels, deviceInfo -> defaultSampleRate);
+					(int) deviceInfo -> maxInputChannels, (int) deviceInfo -> maxOutputChannels, deviceInfo -> defaultSampleRate);
 				if (deviceInfo -> maxInputChannels > 0 && my numberOfInputDevices < SoundRecorder_IDEVICE_MAX) {
 					my device_ [++ my numberOfInputDevices]. canDo = true;
 					wcsncpy (my device_ [my numberOfInputDevices]. name, Melder_peekUtf8ToWcs (deviceInfo -> name), 40);
diff --git a/fon/Sound_to_Formant.cpp b/fon/Sound_to_Formant.cpp
index a73cb52..9c2cfcc 100644
--- a/fon/Sound_to_Formant.cpp
+++ b/fon/Sound_to_Formant.cpp
@@ -136,7 +136,7 @@ static int findNewZeroes (int ijt, double ppORIG [], int degree,
 	newZeroes [0] = 1.0;
 	for (i = 1; i <= half_degree; i ++) {
 		if (! findOneZero (ijt, px, zeroes [i - 1], zeroes [i], & newZeroes [i])) {
-			Melder_casual ("Degree %d not completed.", degree);
+			Melder_casual ("Degree %d not completed.", (int) degree);
 			return 0;
 		}
 	}
diff --git a/fon/Sound_to_Pitch.cpp b/fon/Sound_to_Pitch.cpp
index 4309977..829c1ff 100644
--- a/fon/Sound_to_Pitch.cpp
+++ b/fon/Sound_to_Pitch.cpp
@@ -523,7 +523,7 @@ Pitch Sound_to_Pitch_any (Sound me,
 		long numberOfFramesPerThread = 20;
 		int numberOfThreads = (nFrames - 1) / numberOfFramesPerThread + 1;
 		const int numberOfProcessors = MelderThread_getNumberOfProcessors ();
-		trace ("%d processors", numberOfProcessors);
+		trace ("%d processors", (int) numberOfProcessors);
 		if (numberOfThreads > numberOfProcessors) numberOfThreads = numberOfProcessors;
 		if (numberOfThreads > 16) numberOfThreads = 16;
 		if (numberOfThreads < 1) numberOfThreads = 1;
diff --git a/fon/Spectrum.cpp b/fon/Spectrum.cpp
index 16ecdce..3b06d09 100644
--- a/fon/Spectrum.cpp
+++ b/fon/Spectrum.cpp
@@ -1,6 +1,6 @@
 /* Spectrum.cpp
  *
- * Copyright (C) 1992-2012 Paul Boersma
+ * Copyright (C) 1992-2012,2014 Paul Boersma
  *
  * 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
@@ -149,9 +149,9 @@ void Spectrum_draw (Spectrum me, Graphics g, double fmin, double fmax, double mi
 	if (garnish) {
 		Graphics_drawInnerBox (g);
 		Graphics_textBottom (g, 1, L"Frequency (Hz)");
-		Graphics_marksBottom (g, 2, TRUE, TRUE, FALSE);
+		Graphics_marksBottom (g, 2, true, true, false);
 		Graphics_textLeft (g, 1, L"Sound pressure level (dB/Hz)");
-		Graphics_marksLeftEvery (g, 1.0, 20.0, TRUE, TRUE, FALSE);
+		Graphics_marksLeftEvery (g, 1.0, 20.0, true, true, false);
 	}
 }
 
@@ -391,9 +391,8 @@ double Spectrum_getBandEnergyDifference (Spectrum me, double lowBandMin, double
 }
 
 double Spectrum_getCentreOfGravity (Spectrum me, double power) {
-	long i;
 	double halfpower = 0.5 * power, sumenergy = 0.0, sumfenergy = 0.0;
-	for (i = 1; i <= my nx; i ++) {
+	for (long i = 1; i <= my nx; i ++) {
 		double re = my z [1] [i], im = my z [2] [i], energy = re * re + im * im;
 		double f = my x1 + (i - 1) * my dx;
 		if (halfpower != 1.0) energy = pow (energy, halfpower);
@@ -404,11 +403,10 @@ double Spectrum_getCentreOfGravity (Spectrum me, double power) {
 }
 
 double Spectrum_getCentralMoment (Spectrum me, double moment, double power) {
-	long i;
 	double halfpower = 0.5 * power, sumenergy = 0.0, sumfenergy = 0.0;
 	double fmean = Spectrum_getCentreOfGravity (me, power);
 	if (fmean == NUMundefined) return NUMundefined;
-	for (i = 1; i <= my nx; i ++) {
+	for (long i = 1; i <= my nx; i ++) {
 		double re = my z [1] [i], im = my z [2] [i], energy = re * re + im * im;
 		double f = my x1 + (i - 1) * my dx;
 		if (halfpower != 1.0) energy = pow (energy, halfpower);
diff --git a/fon/TextGrid.cpp b/fon/TextGrid.cpp
index d1e98c4..9f2db2a 100644
--- a/fon/TextGrid.cpp
+++ b/fon/TextGrid.cpp
@@ -300,6 +300,23 @@ void structTextGrid :: v_info () {
 	MelderInfo_writeLine (L"Number of points: ", Melder_integer (numberOfPoints));
 }
 
+static void IntervalTier_addInterval_unsafe (IntervalTier me, double tmin, double tmax, const wchar_t *label) {
+	autoTextInterval interval = TextInterval_create (tmin, tmax, label);
+	Collection_addItem (my intervals, interval.transfer());
+}
+
+void structTextGrid :: v_repair () {
+	for (long itier = 1; itier <= our numberOfTiers (); itier ++) {
+		Function anyTier = our tier (itier);
+		if (anyTier -> classInfo == classIntervalTier) {
+			IntervalTier tier = (IntervalTier) anyTier;
+			if (tier -> numberOfIntervals () == 0) {
+				IntervalTier_addInterval_unsafe (tier, tier -> xmin, tier -> xmax, L"");
+			}
+		}
+	}
+}
+
 void structTextGrid :: v_shiftX (double xfrom, double xto) {
 	TextGrid_Parent :: v_shiftX (xfrom, xto);
 	for (long i = 1; i <= tiers -> size; i ++) {
@@ -865,11 +882,6 @@ TableOfReal TextTier_downto_TableOfReal_any (TextTier me) {
 	return TextTier_downto_TableOfReal (me, NULL);
 }
 
-static void IntervalTier_addInterval_unsafe (IntervalTier me, double tmin, double tmax, const wchar_t *label) {
-	autoTextInterval interval = TextInterval_create (tmin, tmax, label);
-	Collection_addItem (my intervals, interval.transfer());
-}
-
 IntervalTier IntervalTier_readFromXwaves (MelderFile file) {
 	try {
 		char *line;
diff --git a/fon/TextGridEditor.cpp b/fon/TextGridEditor.cpp
index 200dc4b..73befb1 100644
--- a/fon/TextGridEditor.cpp
+++ b/fon/TextGridEditor.cpp
@@ -92,6 +92,8 @@ static void _TextGridEditor_timeToInterval (TextGridEditor me, double t, int iti
 				iinterval = intervalTier -> intervals -> size;
 			}
 		}
+		Melder_assert (iinterval >= 1);
+		Melder_assert (iinterval <= intervalTier -> intervals -> size);
 		interval = (TextInterval) intervalTier -> intervals -> item [iinterval];
 		*tmin = interval -> xmin;
 		*tmax = interval -> xmax;
diff --git a/fon/TextGrid_def.h b/fon/TextGrid_def.h
index cb2ec91..52b5bbf 100644
--- a/fon/TextGrid_def.h
+++ b/fon/TextGrid_def.h
@@ -73,7 +73,8 @@ oo_DEFINE_CLASS (IntervalTier, Function)
 	#if oo_DECLARING
 		long numberOfIntervals () { return our intervals -> size; }
 		TextInterval interval (long i) { return static_cast <TextInterval> (our intervals -> item [i]); }
-		// TextInterval operator[] (long i) { return static_cast <TextInterval> (our intervals -> item [i]); }   // oops: operator[] not for pointer objects
+		//template <class T> T& operator[] (long i) { return (T) (our intervals -> item [i]); }
+		//TextInterval* intervalss () { return (TextInterval *) (our intervals -> item); }
 		virtual int v_domainQuantity () { return MelderQuantity_TIME_SECONDS; }
 		virtual void v_shiftX (double xfrom, double xto);
 		virtual void v_scaleX (double xminfrom, double xmaxfrom, double xminto, double xmaxto);
@@ -93,6 +94,7 @@ oo_DEFINE_CLASS (TextGrid, Function)
 		Function tier (long i) { return static_cast <Function> (our tiers -> item [i]); }
 		void removePoints (long tierNumber, int which_Melder_STRING, const wchar_t *criterion);
 		virtual void v_info ();
+		virtual void v_repair ();
 		virtual int v_domainQuantity () { return MelderQuantity_TIME_SECONDS; }
 		virtual void v_shiftX (double xfrom, double xto);
 		virtual void v_scaleX (double xminfrom, double xmaxfrom, double xminto, double xmaxto);
diff --git a/fon/manual_Script.cpp b/fon/manual_Script.cpp
index 835fd56..898f71d 100644
--- a/fon/manual_Script.cpp
+++ b/fon/manual_Script.cpp
@@ -1365,7 +1365,7 @@ NORMAL (L"On Windows it is called ##Preferences5.ini#, "
 	"for instance ##C:\\bsDocuments and Settings\\bsMiep\\bsPraat\\bsPreferences5.ini#.")
 MAN_END
 
-MAN_BEGIN (L"Scripting", L"ppgb", 20140212)
+MAN_BEGIN (L"Scripting", L"ppgb", 20141012)
 INTRO (L"This is one of the tutorials of the Praat program. It assumes you are familiar with the @Intro.")
 NORMAL (L"A %script is a text that consists of menu commands and action commands. "
 	"If you %run the script (perhaps from a @ScriptEditor), "
@@ -1399,7 +1399,7 @@ LIST_ITEM1 (L"@@Scripting 6.1. Arguments to the script@ (form/endform, runScript
 LIST_ITEM1 (L"@@Scripting 6.2. Writing to the Info window@ (writeInfoLine, appendInfoLine, appendInfo, tab\\$ )")
 LIST_ITEM1 (L"@@Scripting 6.3. Query commands@ (Get, Count)")
 LIST_ITEM1 (L"@@Scripting 6.4. Files@ (fileReadable, readFile, writeFile, deleteFile, createDirectory)")
-LIST_ITEM1 (L"@@Scripting 6.5. Calling system commands@ (system, environment\\$ , stopwatch)")
+LIST_ITEM1 (L"@@Scripting 6.5. Calling system commands@ (runSystem, environment\\$ , stopwatch)")
 LIST_ITEM1 (L"@@Scripting 6.6. Controlling the user@ (pause, beginPause/endPause, chooseReadFile\\$ )")
 LIST_ITEM1 (L"@@Scripting 6.7. Sending a message to another program@ (sendsocket)")
 LIST_ITEM1 (L"@@Scripting 6.8. Messages to the user@ (exitScript, assert, nowarn, nocheck)")
@@ -2710,12 +2710,12 @@ DEFINITION (L"stops the execution of the script while sending an error message t
 NORMAL (L"For an example, see @@Scripting 6.8. Messages to the user at .")
 MAN_END
 
-MAN_BEGIN (L"Scripting 6. Communication outside the script", L"ppgb", 20140223)
+MAN_BEGIN (L"Scripting 6. Communication outside the script", L"ppgb", 20141012)
 LIST_ITEM (L"@@Scripting 6.1. Arguments to the script@ (form/endform, runScript)")
 LIST_ITEM (L"@@Scripting 6.2. Writing to the Info window@ (writeInfoLine, appendInfoLine, appendInfo, tab\\$ )")
 LIST_ITEM (L"@@Scripting 6.3. Query commands@ (Get, Count)")
 LIST_ITEM (L"@@Scripting 6.4. Files@ (fileReadable, readFile, writeFile, deleteFile, createDirectory)")
-LIST_ITEM (L"@@Scripting 6.5. Calling system commands@ (system, environment\\$ , stopwatch)")
+LIST_ITEM (L"@@Scripting 6.5. Calling system commands@ (runSystem, environment\\$ , stopwatch)")
 LIST_ITEM (L"@@Scripting 6.6. Controlling the user@ (pause, beginPause/endPause, chooseReadFile\\$ )")
 LIST_ITEM (L"@@Scripting 6.7. Sending a message to another program@ (sendsocket)")
 LIST_ITEM (L"@@Scripting 6.8. Messages to the user@ (exitScript, assert, nowarn, nocheck)")
@@ -2965,31 +2965,28 @@ NORMAL (L"you can also write")
 CODE (L"#deleteFile (%%fileName\\$ %)")
 MAN_END
 
-MAN_BEGIN (L"Scripting 6.5. Calling system commands", L"ppgb", 20140111)
+MAN_BEGIN (L"Scripting 6.5. Calling system commands", L"ppgb", 20141012)
 INTRO (L"From a Praat script you can call system commands. "
-	"These are the same commands that you would normally type into a terminal window or into the Window command line prompt.")
-TAG (L"#system %command")
-DEFINITION (L"executes a system command.")
-NORMAL (L"Some system commands are identical on all platforms (Macintosh, Windows, Unix):")
-CODE (L"#system mkdir sounds")
-NORMAL (L"which creates a new directory #sounds in the directory of the script. Some other system commands "
-	"are different on different platforms. For instance, to throw away all WAV files in the script's directory, "
-	"you would write")
-CODE (L"#system del *.wav")
+	"These are the same commands that you would normally type into a terminal window or into the Window command line prompt. "
+	"The syntax is the same as that of the #writeInfo command.")
+NORMAL (L"Most system commands are different on different platforms. "
+	"For instance, to throw away all WAV files in the directory whose name (relative to the script's directory) is "
+	"in the variable directory\\$ , you would write")
+CODE (L"#runSystem: \"del \", directory\\$ , \"\\bs*.wav\"")
 NORMAL (L"on Windows, but")
-CODE (L"#system rm *.wav")
+CODE (L"#runSystem: \"rm \", directory\\$ , \"/*.wav\"")
 NORMAL (L"on Macintosh and Unix.")
 NORMAL (L"The script will stop running if a system command returns an error. For instance,")
-CODE (L"#system mkdir sounds")
-NORMAL (L"will stop the script if the directory #sounds already exists. "
-	"In order to prevent this, you can tell Praat to ignore the return value of the system command:")
-TAG (L"#system_nocheck %command")
-DEFINITION (L"executes a system command, ignoring any errors.")
-NORMAL (L"Thus, to make sure that the directory #sounds exists, you would write")
-CODE (L"#system_nocheck mkdir sounds")
+CODE (L"#runSystem: \"rm \", directory\\$ , \"/*.wav\"")
+NORMAL (L"will stop the script if there are no WAV files in the directory. "
+	"In order to prevent this, you can tell Praat to ignore the return value of the runSystem command.")
+NORMAL (L"Thus, to make sure that the directory contains no WAV files, you would write")
+CODE (L"#runSystem_nocheck: \"rm \", directory\\$ , \"/*.wav\"")
+ENTRY (L"Getting the values of system variables")
 TAG (L"##environment\\$  (#%%symbol-string%#)")
 DEFINITION (L"returns the value of an environment variable, e.g.")
 CODE1 (L"homeDirectory\\$  = ##environment\\$ # (\"HOME\")")
+ENTRY (L"Getting system duration")
 TAG (L"##stopwatch")
 DEFINITION (L"returns the time that has elapsed since the previous #stopwatch.")
 NORMAL (L"Here is a Praat script that measures how long it takes to do a million assignments:")
diff --git a/fon/manual_tutorials.cpp b/fon/manual_tutorials.cpp
index e36460a..7007e82 100644
--- a/fon/manual_tutorials.cpp
+++ b/fon/manual_tutorials.cpp
@@ -23,9 +23,20 @@
 void manual_tutorials_init (ManPages me);
 void manual_tutorials_init (ManPages me) {
 
-MAN_BEGIN (L"What's new?", L"ppgb", 20141004)
+MAN_BEGIN (L"What's new?", L"ppgb", 20141228)
 INTRO (L"Latest changes in Praat.")
 /*LIST_ITEM (L"• Manual page about @@drawing a vowel triangle at .")*/
+NORMAL (L"##5.4.04# (28 December 2014)")
+LIST_ITEM (L"• Windows audio playback: if the sound has more channels than the audio hardware, distribute them evenly.")
+NORMAL (L"##5.4.03# (18 December 2014)")
+LIST_ITEM (L"• TextGrid reading: Praat now corrects some incomplete TextGrid files created by others.")
+LIST_ITEM (L"• Better support for text files larger than 2 GB.")
+NORMAL (L"##5.4.02# (26 November 2014)")
+LIST_ITEM (L"• Mac: repaired a bug by which quote characters typed into the script window could become curly instead of straight.")
+NORMAL (L"##5.4.01# (9 November 2014)")
+LIST_ITEM (L"• @MelSpectrogram, @BarkSpectrogram.")
+LIST_ITEM (L"• Linux: removed a bug that could cause too many flashes when scrolling a Sound window.")
+LIST_ITEM (L"• Mac: repaired a bug that could cause a crash in the VowelEditor window.")
 NORMAL (L"##5.4# (4 October 2014)")
 NORMAL (L"##5.3.87# (3 October 2014)")
 LIST_ITEM (L"• Windows scripting: prevented incorrect handling of relative paths after the use of chooseReadFile\\$ .")
@@ -44,7 +55,7 @@ LIST_ITEM (L"• Linux audio: created a second workaround that reduces even more
 LIST_ITEM (L"• Smoother communication with Phon.")
 LIST_ITEM (L"• Windows: repaired a memory leak when saving PNG files.")
 NORMAL (L"##5.3.82# (26 July 2014)")
-LIST_ITEM (L"• Audio playback: if the sound has more channels than the audio hardware, distribute them evenly.")
+LIST_ITEM (L"• Linux and Mac audio playback: if the sound has more channels than the audio hardware, distribute them evenly.")
 LIST_ITEM (L"• Pause forms: more consistent appearance of the Revert button.")
 LIST_ITEM (L"• Scripting: pauseScript ( ) function.")
 NORMAL (L"##5.3.81# (2 July 2014)")
@@ -3525,7 +3536,7 @@ MAN_BEGIN (L"Technical", L"ppgb", 20120915)
 INTRO (L"The title of a submenu of the @@Praat menu at .")
 MAN_END
 
-MAN_BEGIN (L"Types of objects", L"ppgb", 20101230)
+MAN_BEGIN (L"Types of objects", L"ppgb", 20141109)
 INTRO (L"Praat contains the following types of objects and @Editors. "
 	"For an introduction and tutorials, see @Intro.")
 NORMAL (L"General purpose:")
@@ -3553,7 +3564,7 @@ LIST_ITEM1 (L"• @@Intro 3. Spectral analysis")
 LIST_ITEM1 (L"• @@Intro 5. Formant analysis")
 LIST_ITEM (L"• @Spectrum: complex-valued equally spaced frequency spectrum (@SpectrumEditor)")
 LIST_ITEM (L"• @Ltas: long-term average spectrum")
-LIST_ITEM (L"• Spectro-temporal: @Spectrogram, @BarkFilter, @MelFilter, @FormantFilter")
+LIST_ITEM (L"• Spectro-temporal: @Spectrogram, @BarkSpectrogram, @MelSpectrogram")
 LIST_ITEM (L"• @Formant: acoustic formant contours")
 LIST_ITEM (L"• @LPC: coefficients of Linear Predictive Coding, as a function of time")
 LIST_ITEM (L"• @Cepstrum, @CC, @LFCC, @MFCC (cepstral coefficients)")
diff --git a/makefiles/makefile.defs.linux.alsa b/makefiles/makefile.defs.linux.alsa
index 60447f5..78a7966 100644
--- a/makefiles/makefile.defs.linux.alsa
+++ b/makefiles/makefile.defs.linux.alsa
@@ -1,13 +1,13 @@
 # File: makefile.defs.linux.alsa
 
 # System: Linux
-# Paul Boersma, 12 June 2014
+# Paul Boersma, 17 December 2014
 
 CC = gcc -std=gnu99
 
 CXX = g++ -std=c++0x
 
-CFLAGS = -DUNIX -Dlinux -DALSA `pkg-config --cflags gtk+-2.0` -Werror=missing-prototypes -Werror=implicit -Wreturn-type -Wunused -Wunused-parameter -Wuninitialized -O1 -g1 -pthread
+CFLAGS = -DUNIX -Dlinux -DALSA -D_FILE_OFFSET_BITS=64 `pkg-config --cflags gtk+-2.0` -Werror=missing-prototypes -Werror=implicit -Wreturn-type -Wunused -Wunused-parameter -Wuninitialized -O1 -g1 -pthread
 
 CXXFLAGS = $(CFLAGS) -Wshadow
 
diff --git a/makefiles/makefile.defs.linux.silent b/makefiles/makefile.defs.linux.silent
index 74d7648..83e2254 100644
--- a/makefiles/makefile.defs.linux.silent
+++ b/makefiles/makefile.defs.linux.silent
@@ -1,13 +1,13 @@
 # File: makefile.defs.linux.silent
 
 # System: Linux without sound
-# Paul Boersma, 28 May 2014
+# Paul Boersma, 17 December 2014
 
 CC = gcc -std=gnu99
 
 CXX = g++ -std=c++0x
 
-CFLAGS = -DUNIX -Dlinux `pkg-config --cflags gtk+-2.0` -Werror=missing-prototypes -Werror=implicit -Wreturn-type -Wunused -Wunused-parameter -Wuninitialized -O1 -g1 -pthread
+CFLAGS = -D_FILE_OFFSET_BITS=64 -DUNIX -Dlinux `pkg-config --cflags gtk+-2.0` -Werror=missing-prototypes -Werror=implicit -Wreturn-type -Wunused -Wunused-parameter -Wuninitialized -O1 -g1 -pthread
 
 CXXFLAGS = $(CFLAGS) -Wshadow
 
diff --git a/makefiles/makefile.defs.linuxc.alsa b/makefiles/makefile.defs.linuxc.alsa
index 655507f..b7aaef4 100644
--- a/makefiles/makefile.defs.linuxc.alsa
+++ b/makefiles/makefile.defs.linuxc.alsa
@@ -1,13 +1,13 @@
 # File: makefile.defs.linuxc.alsa
 
 # System: Linux
-# Paul Boersma, 28 May 2014
+# Paul Boersma, 17 December 2014
 
 CC = gcc -std=gnu99
 
 CXX = g++ -std=c++0x
 
-CFLAGS = -DCONSOLE_APPLICATION -DUNIX -Dlinux -DALSA -Werror=missing-prototypes -Werror=implicit -Wreturn-type -Wunused -Wunused-parameter -Wuninitialized -O1 -g1 -pthread
+CFLAGS = -DCONSOLE_APPLICATION -DUNIX -Dlinux -DALSA -D_FILE_OFFSET_BITS=64 -Werror=missing-prototypes -Werror=implicit -Wreturn-type -Wunused -Wunused-parameter -Wuninitialized -O1 -g1 -pthread
 
 CXXFLAGS = $(CFLAGS) -Wshadow
 
diff --git a/makefiles/makefile.defs.linuxs.alsa b/makefiles/makefile.defs.linuxs.alsa
index 21af920..3d23304 100644
--- a/makefiles/makefile.defs.linuxs.alsa
+++ b/makefiles/makefile.defs.linuxs.alsa
@@ -1,13 +1,13 @@
 # File: makefile.defs.linuxc.alsa
 
 # System: Linux
-# Paul Boersma, 28 May 2014
+# Paul Boersma, 17 December 2014
 
 CC = gcc -std=gnu99
 
 CXX = g++ -std=c++0x
 
-CFLAGS = -DNO_GRAPHICS -DUNIX -Dlinux -DALSA -Werror=missing-prototypes -Werror=implicit -Wreturn-type -Wunused -Wunused-parameter -Wuninitialized -O1 -g1 -pthread
+CFLAGS = -DNO_GRAPHICS -DUNIX -Dlinux -DALSA -D_FILE_OFFSET_BITS=64 -Werror=missing-prototypes -Werror=implicit -Wreturn-type -Wunused -Wunused-parameter -Wuninitialized -O1 -g1 -pthread
 
 CXXFLAGS = $(CFLAGS) -Wshadow
 
diff --git a/makefiles/makefile.defs.mingw32 b/makefiles/makefile.defs.mingw32
index 863d6d4..09b51ff 100644
--- a/makefiles/makefile.defs.mingw32
+++ b/makefiles/makefile.defs.mingw32
@@ -1,13 +1,13 @@
 # File: makefile.defs.mingw32
 
 # System: MinGW
-# Paul Boersma, 12 June 2014
+# Paul Boersma, 17 December 2014
 
 CC = /mingw32/bin/gcc -std=gnu99 -isystem /mingw32/include
 
 CXX = /mingw32/bin/g++ -std=c++0x -isystem /mingw32/include/c++/4.7.0 -isystem /mingw32/include -Wshadow
 
-CFLAGS = -DWINVER=0x0500 -D_WIN32_WINNT=0x0500 -D_WIN32_IE=0x0500 -DUNICODE -Dmain=wingwmain -O1 -pthread
+CFLAGS = -DWINVER=0x0500 -D_WIN32_WINNT=0x0500 -D_WIN32_IE=0x0500 -DUNICODE -D_FILE_OFFSET_BITS=64 -Dmain=wingwmain -O1 -pthread
 
 CXXFLAGS = $(CFLAGS)
 
diff --git a/makefiles/makefile.defs.mingw32-490 b/makefiles/makefile.defs.mingw32-490
index e4aba07..4c26614 100644
--- a/makefiles/makefile.defs.mingw32-490
+++ b/makefiles/makefile.defs.mingw32-490
@@ -1,13 +1,13 @@
 # File: makefile.defs.mingw32-490
 
 # System: MinGW
-# Paul Boersma, 9 June 2014
+# Paul Boersma, 17 December 2014
 
 CC = /mingw32-490/bin/x86_64-w64-mingw32-gcc -std=gnu99 -isystem /mingw32-490/mingw/include
 
 CXX = /mingw32-490/bin/x86_64-w64-mingw32-g++ -std=c++0x -isystem /mingw32-490/mingw/include/c++/4.9.0 -isystem /mingw32-490/mingw/include -Wshadow -m64 -flto
 
-CFLAGS = -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 -D_WIN32_IE=0x0700 -DUNICODE -Dmain=wingwmain -O1
+CFLAGS = -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 -D_WIN32_IE=0x0700 -DUNICODE -D_FILE_OFFSET_BITS=64 -Dmain=wingwmain -O1
 
 CXXFLAGS = $(CFLAGS) -pthread
 
diff --git a/makefiles/makefile.defs.mingw32c b/makefiles/makefile.defs.mingw32c
index ade9b61..2386a26 100644
--- a/makefiles/makefile.defs.mingw32c
+++ b/makefiles/makefile.defs.mingw32c
@@ -1,13 +1,13 @@
 # File: makefile.defs.mingw32c
 
 # System: MinGW
-# Paul Boersma, 12 June 2014
+# Paul Boersma, 17 December 2014
 
 CC = /mingw32/bin/gcc -std=gnu99 -isystem /mingw32/include
 
 CXX = /mingw32/bin/g++ -std=c++0x -isystem /mingw32/include/c++/4.7.0 -isystem /mingw32/include -Wshadow
 
-CFLAGS = -DWINVER=0x0500 -D_WIN32_WINNT=0x0500 -D_WIN32_IE=0x0500 -DUNICODE -DCONSOLE_APPLICATION -O1 -pthread
+CFLAGS = -DWINVER=0x0500 -D_WIN32_WINNT=0x0500 -D_WIN32_IE=0x0500 -DUNICODE -D_FILE_OFFSET_BITS=64 -DCONSOLE_APPLICATION -O1 -pthread
 
 CXXFLAGS = $(CFLAGS)
 
diff --git a/makefiles/makefile.defs.mingw64 b/makefiles/makefile.defs.mingw64
index 8a78ce8..381dd11 100644
--- a/makefiles/makefile.defs.mingw64
+++ b/makefiles/makefile.defs.mingw64
@@ -1,13 +1,13 @@
 # File: makefile.defs.mingw64
 
 # System: MinGW
-# Paul Boersma, 12 June 2014
+# Paul Boersma, 17 December 2014
 
 CC = /mingw64/bin/gcc -std=gnu99 -isystem /mingw64/include
 
 CXX = /mingw64/bin/g++ -std=c++0x -isystem /mingw64/include/c++/4.7.0 -isystem /mingw64/include -Wshadow -m64
 
-CFLAGS = -DWINVER=0x0500 -D_WIN32_WINNT=0x0500 -D_WIN32_IE=0x0500 -DUNICODE -Dmain=wingwmain -O1 -pthread
+CFLAGS = -DWINVER=0x0500 -D_WIN32_WINNT=0x0500 -D_WIN32_IE=0x0500 -DUNICODE -D_FILE_OFFSET_BITS=64 -Dmain=wingwmain -O1 -pthread
 
 CXXFLAGS = $(CFLAGS)
 
diff --git a/makefiles/makefile.defs.mingw64-490 b/makefiles/makefile.defs.mingw64-490
index ba6ff5e..d1381f7 100644
--- a/makefiles/makefile.defs.mingw64-490
+++ b/makefiles/makefile.defs.mingw64-490
@@ -1,13 +1,13 @@
 # File: makefile.defs.mingw64-490
 
 # System: MinGW
-# Paul Boersma, 9 June 2014
+# Paul Boersma, 17 December 2014
 
 CC = /mingw64-490/bin/x86_64-w64-mingw32-gcc -std=gnu99 -isystem /mingw64-490/mingw/include
 
 CXX = /mingw64-490/bin/x86_64-w64-mingw32-g++ -std=c++0x -isystem /mingw64-490/mingw/include/c++/4.9.0 -isystem /mingw64-490/mingw/include -Wshadow -m64
 
-CFLAGS = -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 -D_WIN32_IE=0x0700 -DUNICODE -Dmain=wingwmain -O1 -flto
+CFLAGS = -DWINVER=0x0600 -D_WIN32_WINNT=0x0600 -D_WIN32_IE=0x0700 -DUNICODE -D_FILE_OFFSET_BITS=64 -Dmain=wingwmain -O1 -flto
 
 CXXFLAGS = $(CFLAGS) -pthread
 
diff --git a/makefiles/makefile.defs.mingw64c b/makefiles/makefile.defs.mingw64c
index b2ad9b1..16fa729 100644
--- a/makefiles/makefile.defs.mingw64c
+++ b/makefiles/makefile.defs.mingw64c
@@ -1,13 +1,13 @@
 # File: makefile.defs.mingw64c
 
 # System: MinGW
-# Paul Boersma, 12 June 2014
+# Paul Boersma, 17 December 2014
 
 CC = /mingw64/bin/gcc -std=gnu99 -isystem /mingw64/include
 
 CXX = /mingw64/bin/g++ -std=c++0x -isystem /mingw64/include/c++/4.7.0 -isystem /mingw64/include -Wshadow -m64
 
-CFLAGS = -DWINVER=0x0500 -D_WIN32_WINNT=0x0500 -D_WIN32_IE=0x0500 -DUNICODE -DCONSOLE_APPLICATION -O1 -pthread
+CFLAGS = -DWINVER=0x0500 -D_WIN32_WINNT=0x0500 -D_WIN32_IE=0x0500 -DUNICODE -D_FILE_OFFSET_BITS=64 -DCONSOLE_APPLICATION -O1 -pthread
 
 CXXFLAGS = $(CFLAGS)
 
diff --git a/num/NUMarrays.cpp b/num/NUMarrays.cpp
index 226b690..158e360 100644
--- a/num/NUMarrays.cpp
+++ b/num/NUMarrays.cpp
@@ -131,10 +131,13 @@ void NUMvector_insert (long elementSize, void **v, long lo, long *hi, long posit
 
 void * NUMmatrix (long elementSize, long row1, long row2, long col1, long col2) {
 	try {
+		int64_t numberOfRows = row2 - row1 + 1;
+		int64_t numberOfColumns = col2 - col1 + 1;
+		int64_t numberOfCells = numberOfRows * numberOfColumns;
+
 		/*
 		 * Allocate room for the row pointers.
 		 */
-		long numberOfRows = row2 - row1 + 1;
 		char **result;
 		Melder_assert (sizeof (char) == 1);   // true by definition
 		for (;;) {
@@ -147,10 +150,9 @@ void * NUMmatrix (long elementSize, long row1, long row2, long col1, long col2)
 		 * Allocate room for the cells.
 		 * The first row pointer points to below the first cell.
 		 */
-		long numberOfColumns = col2 - col1 + 1;
 		for (;;) {
 			try {
-				result [row1] = reinterpret_cast <char *> (_Melder_calloc (numberOfRows * numberOfColumns, elementSize));
+				result [row1] = reinterpret_cast <char *> (_Melder_calloc (numberOfCells, elementSize));
 			} catch (MelderError) {
 				result += row1;
 				Melder_free (result);   // free the row pointers
@@ -159,7 +161,7 @@ void * NUMmatrix (long elementSize, long row1, long row2, long col1, long col2)
 			if ((result [row1] -= col1 * elementSize) != NULL) break;   // this will normally succeed at the first try
 			(void) Melder_realloc_f (result [row1] + col1 * elementSize, 1);   // make "sure" that the second try will succeed
 		}
-		long columnSize = numberOfColumns * elementSize;
+		int64_t columnSize = numberOfColumns * elementSize;
 		for (long irow = row1 + 1; irow <= row2; irow ++) result [irow] = result [irow - 1] + columnSize;
 		theTotalNumberOfArrays += 1;
 		return result;
diff --git a/num/NUMrandom.cpp b/num/NUMrandom.cpp
index 12932ba..806e243 100644
--- a/num/NUMrandom.cpp
+++ b/num/NUMrandom.cpp
@@ -78,7 +78,6 @@
    email: m-mat @ math.sci.hiroshima-u.ac.jp (remove spaces)
 */
 
-#include <inttypes.h>
 #if defined (__MINGW32__) || defined (linux)
 	#define UINT64_C(n)  n ## ULL
 #endif
@@ -159,7 +158,7 @@ void NUMrandom_State :: init_by_array64 (uint64_t init_key [], unsigned int key_
 static bool theInited = false;
 void NUMrandom_init () {
 	for (int threadNumber = 0; threadNumber <= 16; threadNumber ++) {
-		const int numberOfKeys = 5;
+		const int numberOfKeys = 6;
 		uint64_t keys [numberOfKeys];
 		keys [0] = round (1e6 * Melder_clock ());   // unique between boots of the same computer
 		keys [1] = UINT64_C (7320321686725470078) + (uint64_t) threadNumber;   // unique between threads in the same process
@@ -184,9 +183,9 @@ void NUMrandom_init () {
 			default: Melder_fatal ("Thread number too high.");
 		}
 		keys [4] = getpid ();   // unique between processes that run simultaneously on the same computer
-		/*
-			TODO: need to add a seed that is unique between computers. gethostid?
-		*/
+		#ifndef _WIN32
+		keys [5] = gethostid ();   // unique between computers
+		#endif
 		states [threadNumber]. init_by_array64 (keys, numberOfKeys);
 	}
 	theInited = true;
diff --git a/sys/Collection.h b/sys/Collection.h
index 42f04b5..5c518ad 100644
--- a/sys/Collection.h
+++ b/sys/Collection.h
@@ -282,13 +282,6 @@ Thing_define (SortedSetOfDouble, SortedSet) {
 void SortedSetOfDouble_init (SortedSetOfDouble me);
 SortedSetOfDouble SortedSetOfDouble_create (void);
 
-template <class T>
-class SortedSetOfDouble_vector : public structSortedSetOfDouble {
-	T& operator[] (long i) {
-		return (T) item [i];
-	}
-};
-
 /********** class SortedSetOfString **********/
 
 Thing_define (SortedSetOfString, SortedSet) {
diff --git a/sys/Data.cpp b/sys/Data.cpp
index e590d94..606ee45 100644
--- a/sys/Data.cpp
+++ b/sys/Data.cpp
@@ -184,6 +184,7 @@ bool Data_canReadText (Data me) {
 void Data_readText (Data me, MelderReadText text) {
 	try {
 		my v_readText (text);
+		my v_repair ();
 	} catch (MelderError) {
 		Melder_throw (Thing_className (me), " not read.");
 	}
@@ -227,6 +228,7 @@ void Data_readBinary (Data me, FILE *f) {
 			Melder_throw ("Early end of file.");
 		if (ferror (f))
 			Melder_throw ("I/O error.");
+		my v_repair ();
 	} catch (MelderError) {
 		Melder_throw (Thing_className (me), " not read.");
 	}
diff --git a/sys/Data.h b/sys/Data.h
index 7dc1644..11396b7 100644
--- a/sys/Data.h
+++ b/sys/Data.h
@@ -47,6 +47,7 @@ Thing_define (Data, Thing) {
 		virtual void v_readText (MelderReadText text);
 		virtual void v_writeBinary (FILE *f);
 		virtual void v_readBinary (FILE *f);
+		virtual void v_repair () { }   // after reading Praat data files created by others
 		// messages for scripting:
 		virtual bool v_hasGetNrow      () { return false; }   virtual double        v_getNrow      ()                      { return NUMundefined; }
 		virtual bool v_hasGetNcol      () { return false; }   virtual double        v_getNcol      ()                      { return NUMundefined; }
diff --git a/sys/Formula.cpp b/sys/Formula.cpp
index 94e1461..531ce98 100644
--- a/sys/Formula.cpp
+++ b/sys/Formula.cpp
@@ -124,7 +124,7 @@ enum { GEENSYMBOOL_,
 		DO_, DOSTR_,
 		WRITE_INFO_, WRITE_INFO_LINE_, APPEND_INFO_, APPEND_INFO_LINE_,
 		WRITE_FILE_, WRITE_FILE_LINE_, APPEND_FILE_, APPEND_FILE_LINE_,
-		PAUSE_SCRIPT_, EXIT_SCRIPT_, RUN_SCRIPT_,
+		PAUSE_SCRIPT_, EXIT_SCRIPT_, RUN_SCRIPT_, RUN_SYSTEM_, RUN_SYSTEM_NOCHECK_,
 		MIN_, MAX_, IMIN_, IMAX_,
 		LEFTSTR_, RIGHTSTR_, MIDSTR_,
 		SELECTED_, SELECTEDSTR_, NUMBER_OF_SELECTED_, SELECT_OBJECT_, PLUS_OBJECT_, MINUS_OBJECT_, REMOVE_OBJECT_,
@@ -221,7 +221,7 @@ static const wchar_t *Formula_instructionNames [1 + hoogsteSymbool] = { L"",
 	L"do", L"do$",
 	L"writeInfo", L"writeInfoLine", L"appendInfo", L"appendInfoLine",
 	L"writeFile", L"writeFileLine", L"appendFile", L"appendFileLine",
-	L"pauseScript", L"exitScript", L"runScript",
+	L"pauseScript", L"exitScript", L"runScript", L"runSystem", L"runSystem_nocheck",
 	L"min", L"max", L"imin", L"imax",
 	L"left$", L"right$", L"mid$",
 	L"selected", L"selected$", L"numberOfSelected", L"selectObject", L"plusObject", L"minusObject", L"removeObject",
@@ -1792,6 +1792,8 @@ static void Formula_removeLabels (void) {
 	numberOfInstructions --;   /* Het END_-symbol hoeft niet geinterpreteerd. */
 }
 
+#include <inttypes.h>
+
 /*
  * For debugging.
  */
@@ -1802,31 +1804,31 @@ static void Formula_print (FormulaInstruction f) {
 		symbol = f [++ i]. symbol;
 		instructionName = Formula_instructionNames [symbol];
 		if (symbol == NUMBER_)
-			Melder_casual ("%d %ls %.17g", i, instructionName, f [i]. content.number);
+			Melder_casual ("%d %ls %.17g", (int) i, instructionName, f [i]. content.number);
 		else if (symbol == GOTO_ || symbol == IFFALSE_ || symbol == IFTRUE_ || symbol == LABEL_ || symbol == INCREMENT_GREATER_GOTO_)
-			Melder_casual ("%d %ls %d", i, instructionName, f [i]. content.label);
+			Melder_casual ("%d %ls %d", (int) i, instructionName, (int) f [i]. content.label);
 		else if (symbol == NUMERIC_VARIABLE_)
-			Melder_casual ("%d %ls %ls %ls", i, instructionName, f [i]. content.variable -> string, Melder_double (f [i]. content.variable -> numericValue));
+			Melder_casual ("%d %ls %ls %ls", (int) i, instructionName, f [i]. content.variable -> string, Melder_double (f [i]. content.variable -> numericValue));
 		else if (symbol == STRING_VARIABLE_)
-			Melder_casual ("%d %ls %ls %ls", i, instructionName, f [i]. content.variable -> string, f [i]. content.variable -> stringValue);
+			Melder_casual ("%d %ls %ls %ls", (int) i, instructionName, f [i]. content.variable -> string, f [i]. content.variable -> stringValue);
 		else if (symbol == STRING_ || symbol == VARIABLE_NAME_ || symbol == INDEXED_NUMERIC_VARIABLE_ || symbol == INDEXED_STRING_VARIABLE_)
-			Melder_casual ("%d %ls \"%ls\"", i, instructionName, f [i]. content.string);
+			Melder_casual ("%d %ls \"%ls\"", (int) i, instructionName, f [i]. content.string);
 		else if (symbol == MATRIKS_ || symbol == MATRIKSSTR_ || symbol == MATRIKS1_ || symbol == MATRIKSSTR1_ ||
 		         symbol == MATRIKS2_ || symbol == MATRIKSSTR2_ || symbol == ROWSTR_ || symbol == COLSTR_)
 		{
 			Thing object = (Thing) f [i]. content.object;
 			if (object) {
-				Melder_casual ("%d %ls %s %s", i, instructionName,
+				Melder_casual ("%d %ls %s %s", (int) i, instructionName,
 					Melder_peekWcsToUtf8 (Thing_className (object)),
 					Melder_peekWcsToUtf8 (object -> name));
 			} else {
-				Melder_casual ("%d %ls", i, instructionName);
+				Melder_casual ("%d %ls", (int) i, instructionName);
 			}
 		}
 		else if (symbol == CALL_)
-			Melder_casual ("%d %ls %ls", i, instructionName, f [i]. content.string);
+			Melder_casual ("%d %ls %ls", (int) i, instructionName, f [i]. content.string);
 		else
-			Melder_casual ("%d %ls", i, instructionName);
+			Melder_casual ("%d %ls", (int) i, instructionName);
 	} while (symbol != END_);
 }
 
@@ -2731,6 +2733,51 @@ static void do_runScript () {
 	}
 	pushNumber (1);
 }
+static void do_runSystem () {
+	if (theCurrentPraatObjects != & theForegroundPraatObjects)
+		Melder_throw ("The function \"runSystem\" is not available inside manuals.");
+	Stackel narg = pop;
+	Melder_assert (narg->which == Stackel_NUMBER);
+	int numberOfArguments = narg->number;
+	w -= numberOfArguments;
+	autoMelderString text;
+	for (int iarg = 1; iarg <= numberOfArguments; iarg ++) {
+		Stackel arg = & theStack [w + iarg];
+		if (arg->which == Stackel_NUMBER)
+			MelderString_append (& text, Melder_double (arg->number));
+		else if (arg->which == Stackel_STRING)
+			MelderString_append (& text, arg->string);
+	}
+	try {
+		Melder_system (text.string);
+	} catch (MelderError) {
+		Melder_throw ("System command \"", text.string, "\" returned error status;\n"
+			"if you want to ignore this, use `runSystem_nocheck' instead of `runSystem'.");
+	}
+	pushNumber (1);
+}
+static void do_runSystem_nocheck () {
+	if (theCurrentPraatObjects != & theForegroundPraatObjects)
+		Melder_throw ("The function \"runSystem\" is not available inside manuals.");
+	Stackel narg = pop;
+	Melder_assert (narg->which == Stackel_NUMBER);
+	int numberOfArguments = narg->number;
+	w -= numberOfArguments;
+	autoMelderString text;
+	for (int iarg = 1; iarg <= numberOfArguments; iarg ++) {
+		Stackel arg = & theStack [w + iarg];
+		if (arg->which == Stackel_NUMBER)
+			MelderString_append (& text, Melder_double (arg->number));
+		else if (arg->which == Stackel_STRING)
+			MelderString_append (& text, arg->string);
+	}
+	try {
+		Melder_system (text.string);
+	} catch (MelderError) {
+		Melder_clearError ();
+	}
+	pushNumber (1);
+}
 static void do_min (void) {
 	Stackel n = pop, last;
 	double result;
@@ -3428,6 +3475,12 @@ static int praat_findObjectFromString (const wchar_t *name) {
 			if (wcsequ (className, Thing_className ((Thing) OBJECT)) && wcsequ (givenName, object -> name))
 				return IOBJECT;
 		}
+		ClassInfo klas = Thing_classFromClassName (className);
+		WHERE_DOWN (1) {
+			Data object = (Data) OBJECT;
+			if (wcsequ (klas -> className, Thing_className ((Thing) OBJECT)) && wcsequ (givenName, object -> name))
+				return IOBJECT;
+		}
 	}
 	Melder_throw ("No object with name \"", name, "\".");
 }
@@ -3890,7 +3943,7 @@ static void do_endPauseForm (void) {
 		co [7] == NULL ? NULL : co[7]->string, co [8] == NULL ? NULL : co[8]->string,
 		co [9] == NULL ? NULL : co[9]->string, co [10] == NULL ? NULL : co[10]->string,
 		theInterpreter);
-	//Melder_casual ("Button %d", buttonClicked);
+	//Melder_casual ("Button %d", (int) buttonClicked);
 	pushNumber (buttonClicked);
 }
 static void do_chooseReadFileStr (void) {
@@ -4762,6 +4815,8 @@ case NUMBER_: { pushNumber (f [programPointer]. content.number);
 } break; case PAUSE_SCRIPT_: { do_pauseScript ();
 } break; case EXIT_SCRIPT_: { do_exitScript ();
 } break; case RUN_SCRIPT_: { do_runScript ();
+} break; case RUN_SYSTEM_: { do_runSystem ();
+} break; case RUN_SYSTEM_NOCHECK_: { do_runSystem_nocheck ();
 } break; case MIN_: { do_min ();
 } break; case MAX_: { do_max ();
 } break; case IMIN_: { do_imin ();
@@ -4881,7 +4936,7 @@ case NUMBER_: { pushNumber (f [programPointer]. content.number);
 	//Melder_casual ("starting value %f", var -> numericValue);
 	pushVariable (var);
 } break; case INCREMENT_GREATER_GOTO_: {
-	//Melder_casual ("top of loop, stack depth %d", w);
+	//Melder_casual ("top of loop, stack depth %d", (int) w);
 	Stackel e = & theStack [w], v = & theStack [w - 1];
 	Melder_assert (e->which == Stackel_NUMBER);
 	Melder_assert (v->which == Stackel_VARIABLE);
diff --git a/sys/Graphics.cpp b/sys/Graphics.cpp
index 844b302..04044cf 100644
--- a/sys/Graphics.cpp
+++ b/sys/Graphics.cpp
@@ -75,13 +75,17 @@ static void widgetToWindowCoordinates (I) {
 
 static void computeTrafo (Graphics me) {
 	double worldScaleX, worldScaleY, workScaleX, workScaleY;
+	Melder_assert (my d_x2WC != my d_x1WC);
 	worldScaleX = (my d_x2NDC - my d_x1NDC) / (my d_x2WC - my d_x1WC);
+	Melder_assert (my d_y2WC != my d_y1WC);
 	worldScaleY = (my d_y2NDC - my d_y1NDC) / (my d_y2WC - my d_y1WC);
 	my deltaX = my d_x1NDC - my d_x1WC * worldScaleX;
 	my deltaY = my d_y1NDC - my d_y1WC * worldScaleY;
+	Melder_assert (my d_x2wNDC != my d_x1wNDC);
 	workScaleX = (my d_x2DC - my d_x1DC) / (my d_x2wNDC - my d_x1wNDC);
 	my deltaX = my d_x1DC - (my d_x1wNDC - my deltaX) * workScaleX;
 	my scaleX = worldScaleX * workScaleX;
+	Melder_assert (my d_y2wNDC != my d_y1wNDC);
 	if (my yIsZeroAtTheTop) {
 		workScaleY = ((int) my d_y1DC - (int) my d_y2DC) / (my d_y2wNDC - my d_y1wNDC);
 		my deltaY = my d_y2DC - (my d_y1wNDC - my deltaY) * workScaleY;
@@ -285,6 +289,8 @@ void Graphics_unsetInner (Graphics me) {
 }
 
 void Graphics_setWindow (Graphics me, double x1WC, double x2WC, double y1WC, double y2WC) {
+	Melder_assert (x1WC != x2WC);
+	Melder_assert (y1WC != y2WC);
 	my d_x1WC = x1WC;
 	my d_x2WC = x2WC;
 	my d_y1WC = y1WC;
diff --git a/sys/GraphicsScreen.cpp b/sys/GraphicsScreen.cpp
index db771b7..f63e6b4 100644
--- a/sys/GraphicsScreen.cpp
+++ b/sys/GraphicsScreen.cpp
@@ -484,6 +484,11 @@ static int GraphicsScreen_init (GraphicsScreen me, void *voidDisplay, void *void
 Graphics Graphics_create_screen (void *display, void *window, int resolution) {
 	GraphicsScreen me = Thing_new (GraphicsScreen);
 	my screen = true;
+	#if win
+		my d_useGdiplus = _GraphicsWindows_tryToInitializeGdiPlus ();
+	#elif mac
+		_GraphicsMacintosh_tryToInitializeQuartz ();
+	#endif
 	my yIsZeroAtTheTop = true;
 	Graphics_init (me, resolution);
 	Graphics_setWsViewport ((Graphics) me, 0, 100, 0, 100);
@@ -500,7 +505,9 @@ Graphics Graphics_create_screenPrinter (void *display, void *window) {
 	my screen = true;
 	my yIsZeroAtTheTop = true;
 	my printer = true;
-	#ifdef macintosh
+	#if win
+		my d_useGdiplus = _GraphicsWindows_tryToInitializeGdiPlus ();
+	#elif mac
 		_GraphicsMacintosh_tryToInitializeQuartz ();
 	#endif
 	Graphics_init (me, thePrinter. resolution);
diff --git a/sys/Graphics_image.cpp b/sys/Graphics_image.cpp
index 2e4cc47..329f1ca 100644
--- a/sys/Graphics_image.cpp
+++ b/sys/Graphics_image.cpp
@@ -759,6 +759,7 @@ static void _GraphicsScreen_imageFromFile (GraphicsScreen me, const wchar_t *rel
 			Gdiplus::Graphics dcplus (my d_gdiGraphicsContext);
 			Gdiplus::Rect rect (x1DC, y2DC, width, height);
 			dcplus. DrawImage (& image, rect);
+		} else {
 		}
 	#elif mac
 		structMelderFile file = { 0 };
diff --git a/sys/Graphics_linesAndAreas.cpp b/sys/Graphics_linesAndAreas.cpp
index a293a6a..b2524d2 100644
--- a/sys/Graphics_linesAndAreas.cpp
+++ b/sys/Graphics_linesAndAreas.cpp
@@ -179,36 +179,69 @@ void structGraphicsScreen :: v_polyline (long numberOfPoints, double *xyDC, bool
 			cairoRevertLine (this);
 		}
 	#elif win
-		winPrepareLine (this);
-		POINT *points = Melder_malloc (POINT, numberOfPoints + close);
-		if (points) {
-			for (long i = 0; i < numberOfPoints; i ++) {
-				points [i]. x = *xyDC, xyDC ++;
-				points [i]. y = *xyDC, xyDC ++;
-			}
-			if (close)
-				points [numberOfPoints] = points [0];
-			Polyline (our d_gdiGraphicsContext, points, numberOfPoints + close);
-			if (our d_fatNonSolid) {
-				for (long i = 0; i < numberOfPoints; i ++)
-					points [i]. x -= 1;
+		if (our d_useGdiplus && 0) {
+			Gdiplus::Graphics dcplus (our d_gdiGraphicsContext);
+			Gdiplus::Point *points = Melder_malloc (Gdiplus::Point, numberOfPoints + close);
+			if (points) {
+				for (long i = 0; i < numberOfPoints; i ++) {
+					points [i]. X = *xyDC, xyDC ++;
+					points [i]. Y = *xyDC, xyDC ++;
+				}
 				if (close)
 					points [numberOfPoints] = points [0];
-				Polyline (our d_gdiGraphicsContext, points, numberOfPoints + close);
+#define LINE_WIDTH_IN_PIXELS2(hjhkj)  ( our resolution > 192 ? our lineWidth * (our resolution / 192.0) : our lineWidth )
+				Gdiplus::Pen pen (Gdiplus::Color (255,0,0,0), LINE_WIDTH_IN_PIXELS2 (this) + 0.5);
+				float dotted_line [] = { 2, 2 };
+				float dashed_line [] = { 6, 2 };
+				float dashed_dotted_line [] = { 6, 2, 2, 2 };
+				switch (our lineType) {
+					case Graphics_DOTTED:
+						pen. SetDashPattern (dotted_line, 2);
+						break;
+					case Graphics_DASHED:
+						pen. SetDashPattern (dashed_line, 2);
+						break;
+					case Graphics_DASHED_DOTTED:
+						pen. SetDashPattern (dashed_dotted_line, 4);
+						break;
+				}
+				dcplus. DrawLines (& pen, points, numberOfPoints + close);
+				Melder_free (points);
+			}
+		} else {
+			winPrepareLine (this);
+			POINT *points = Melder_malloc (POINT, numberOfPoints + close);
+			if (points) {
 				for (long i = 0; i < numberOfPoints; i ++) {
-					points [i]. x += 1;
-					points [i]. y -= 1;
+					points [i]. x = *xyDC, xyDC ++;
+					points [i]. y = *xyDC, xyDC ++;
 				}
 				if (close)
 					points [numberOfPoints] = points [0];
 				Polyline (our d_gdiGraphicsContext, points, numberOfPoints + close);
+				if (our d_fatNonSolid) {
+					for (long i = 0; i < numberOfPoints; i ++)
+						points [i]. x -= 1;
+					if (close)
+						points [numberOfPoints] = points [0];
+					Polyline (our d_gdiGraphicsContext, points, numberOfPoints + close);
+					for (long i = 0; i < numberOfPoints; i ++) {
+						points [i]. x += 1;
+						points [i]. y -= 1;
+					}
+					if (close)
+						points [numberOfPoints] = points [0];
+					Polyline (our d_gdiGraphicsContext, points, numberOfPoints + close);
+				}
+				Melder_free (points);
 			}
+			DEFAULT
 		}
-		DEFAULT
 	#elif mac
 		GraphicsQuartz_initDraw (this);
 		quartzPrepareLine (this);
 		CGContextBeginPath (our d_macGraphicsContext);
+		trace ("starting point %.17g %.17g", xyDC [0], xyDC [1]);
 		CGContextMoveToPoint (our d_macGraphicsContext, xyDC [0], xyDC [1]);   // starts a new subpath
 		for (long i = 1; i < numberOfPoints; i ++) {
 			CGContextAddLineToPoint (our d_macGraphicsContext, xyDC [i + i], xyDC [i + i + 1]);
@@ -762,6 +795,7 @@ void Graphics_polyline_closed (Graphics me, long numberOfPoints, double *xWC, do
 
 void Graphics_line (Graphics me, double x1WC, double y1WC, double x2WC, double y2WC) {
 	double xyDC [4];
+	trace ("%.17g %.17g %.17g %.17g", x1WC, y1WC, x2WC, y2WC);
 	xyDC [0] = wdx (x1WC);
 	xyDC [1] = wdy (y1WC);
 	xyDC [2] = wdx (x2WC);
diff --git a/sys/Graphics_text.cpp b/sys/Graphics_text.cpp
index 9a1b03a..2d9a7e5 100644
--- a/sys/Graphics_text.cpp
+++ b/sys/Graphics_text.cpp
@@ -387,10 +387,10 @@ static void charSize (I, _Graphics_widechar *lc) {
 			int normalSize = my fontSize * my resolution / 72.0;
 			lc -> size = lc -> size < 100 ? (3 * normalSize + 2) / 4 : normalSize;
         
-			uint16_t codes16 [2];
+			utf16_t codes16 [2];
 			int nchars = 1;
 			if (lc -> kar > 0xFFFF) {
-				MelderUtf32 kar = lc -> kar - 0x10000;
+				utf32_t kar = lc -> kar - 0x10000;
 				codes16 [0] = 0xD800 + (kar >> 10);
 				codes16 [1] = 0xDC00 + (kar & 0x3FF);
 				nchars = 2;
@@ -480,13 +480,13 @@ static void charSize (I, _Graphics_widechar *lc) {
 				OSStatus err = ATSUCreateTextLayout (& textLayout);
 				if (err != 0) Melder_fatal ("Graphics_text/ATSUCreateTextLayout: unknown MacOS error %d.", (int) err);
 			}
-			uint16_t code16 [2];
+			utf16_t code16 [2];
 			if (lc -> kar <= 0xFFFF) {
 				code16 [0] = lc -> kar;
 				OSStatus err = ATSUSetTextPointerLocation (textLayout, & code16 [0], kATSUFromTextBeginning, kATSUToTextEnd, 1);   // BUG: not 64-bit
 				if (err != 0) Melder_fatal ("Graphics_text/ATSUSetTextPointerLocation low Unicode: unknown MacOS error %d.", (int) err);
 			} else {
-				MelderUtf32 kar = lc -> kar - 0x10000;
+				utf32_t kar = lc -> kar - 0x10000;
 				code16 [0] = 0xD800 + (kar >> 10);
 				code16 [1] = 0xDC00 + (kar & 0x3FF);
 				OSStatus err = ATSUSetTextPointerLocation (textLayout, & code16 [0], kATSUFromTextBeginning, kATSUToTextEnd, 2);   // BUG: not 64-bit
@@ -682,7 +682,7 @@ static void charSize (I, _Graphics_widechar *lc) {
 }
 
 static void charDraw (I, int xDC, int yDC, _Graphics_widechar *lc,
-	const wchar_t *codes, const char *codes8, const MelderUtf16 *codes16, int nchars, int width)
+	const wchar_t *codes, const char *codes8, const utf16_t *codes16, int nchars, int width)
 {
 	iam (Graphics);
 	//Melder_casual ("nchars %d first %d %c rightToLeft %d", nchars, lc->kar, lc -> kar, lc->rightToLeft);
@@ -1273,7 +1273,7 @@ static long bufferSize;
 static _Graphics_widechar *theWidechar;
 static wchar_t *charCodes;
 static char *charCodes8;
-static MelderUtf16 *charCodes16;
+static utf16_t *charCodes16;
 static int initBuffer (const wchar_t *txt) {
 	try {
 		long sizeNeeded = wcslen (txt) + 1;   /* It is true that some characters are split into two, but all of these are backslash sequences. */
@@ -1286,7 +1286,7 @@ static int initBuffer (const wchar_t *txt) {
 			theWidechar = Melder_calloc (_Graphics_widechar, sizeNeeded);
 			charCodes = Melder_calloc (wchar_t, sizeNeeded);
 			charCodes8 = Melder_calloc (char, sizeNeeded);
-			charCodes16 = Melder_calloc (MelderUtf16, sizeNeeded);
+			charCodes16 = Melder_calloc (utf16_t, sizeNeeded);
 			bufferSize = sizeNeeded;
 		}
 		return 1;
diff --git a/sys/GuiLabel.cpp b/sys/GuiLabel.cpp
index 56c284a..4b9ae70 100644
--- a/sys/GuiLabel.cpp
+++ b/sys/GuiLabel.cpp
@@ -90,7 +90,7 @@ GuiLabel GuiLabel_create (GuiForm parent, int left, int right, int top, int bott
 		trace ("set user data");
 		[label setUserData: me];
 		trace ("set bezel style");
-		[label setBezelStyle: NSRoundedBezelStyle];
+		[label setBezelStyle: NSTextFieldRoundedBezel];
 		trace ("set bordered");
 		[label setBordered: NO];
 		trace ("set selectable");
diff --git a/sys/GuiList.cpp b/sys/GuiList.cpp
index decffd3..14faa98 100644
--- a/sys/GuiList.cpp
+++ b/sys/GuiList.cpp
@@ -319,7 +319,7 @@ Thing_implement (GuiList, GuiControl, 0);
 				strncpy (Melder_buffer1, text_utf8, dataLength);
 				Melder_buffer1 [dataLength] = '\0';
 				wchar_t *text_wcs = Melder_peekUtf8ToWcs (Melder_buffer1);
-				const MelderUtf16 *text_utf16 = Melder_peekWcsToUtf16 (text_wcs);
+				const utf16_t *text_utf16 = Melder_peekWcsToUtf16 (text_wcs);
 				UniCharCount runLength = wcslen (text_wcs);   // BUG
 				ATSUTextLayout textLayout;
 				ATSUStyle style;
diff --git a/sys/GuiRadioButton.cpp b/sys/GuiRadioButton.cpp
index 22b07bd..ad04538 100644
--- a/sys/GuiRadioButton.cpp
+++ b/sys/GuiRadioButton.cpp
@@ -58,7 +58,9 @@ static int _GuiRadioButton_getPosition (GuiRadioButton me) {
 	}
 	static void _GuiGtkRadioButton_handleToggle (GuiObject widget, gpointer void_me) {
 		iam (GuiRadioButton);
+		trace ("enter");
 		if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) {
+			trace ("on");
 			if (my d_valueChangedCallback != NULL && ! my d_blockValueChangedCallbacks) {
 				struct structGuiRadioButtonEvent event = { me };
 				event. position = _GuiRadioButton_getPosition (me);
@@ -302,6 +304,7 @@ bool structGuiRadioButton :: f_getValue () {
 }
 
 void structGuiRadioButton :: f_set () {
+	trace ("enter");
 	GuiControlBlockValueChangedCallbacks block (this);   // the value should be set without calling the valueChanged callback (crucial on GTK)
 	#if gtk
 		gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (d_widget), TRUE);
@@ -339,6 +342,7 @@ void structGuiRadioButton :: f_set () {
 			SetControlValue (sibling -> d_widget -> nat.control.handle, false);
 		}
 	#endif
+	trace ("exit");
 }
 
 /* End of file GuiRadioButton.cpp */
diff --git a/sys/GuiScrollBar.cpp b/sys/GuiScrollBar.cpp
index 627361c..907c348 100644
--- a/sys/GuiScrollBar.cpp
+++ b/sys/GuiScrollBar.cpp
@@ -1,6 +1,6 @@
 /* GuiScrollBar.cpp
  *
- * Copyright (C) 1993-2011,2012,2013 Paul Boersma, 2013 Tom Naughton
+ * Copyright (C) 1993-2011,2012,2013,2014 Paul Boersma, 2013 Tom Naughton
  *
  * 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
@@ -40,8 +40,10 @@ Thing_implement (GuiScrollBar, GuiControl, 0);
 	}
 	static void _GuiGtkScrollBar_valueChangedCallback (GuiObject widget, gpointer void_me) {
 		iam (GuiScrollBar);
-		trace ("enter");
-		if (my d_valueChangedCallback != NULL && ! my d_blockValueChangedCallbacks) {
+		trace ("enter: blocked %d", my d_blockValueChangedCallbacks);
+		if (my d_blockValueChangedCallbacks) {
+			my d_blockValueChangedCallbacks = false;
+		} else if (my d_valueChangedCallback) {
 			struct structGuiScrollBarEvent event = { me };
 			try {
 				my d_valueChangedCallback (my d_valueChangedBoss, & event);
@@ -279,8 +281,22 @@ GuiScrollBar GuiScrollBar_createShown (GuiForm parent, int left, int right, int
 }
 
 void structGuiScrollBar :: f_set (double minimum, double maximum, double value, double sliderSize, double increment, double pageIncrement) {
-	GuiControlBlockValueChangedCallbacks block (this);
+	/*
+	 * This function calls the native scroll bar modification function.
+	 *
+	 * Note:
+	 * On almost all platforms, using the native scroll bar modification function sends a value-changed notification to the scroll bar.
+	 * This will call our own d_valueChangedCallback if we don't prevent it.
+	 * We have to prevent that, because our d_valueChangedCallback is only for user-initiated modifications.
+	 */
+	trace ("enter %.17g %.17g %.17g %.17g %.17g %.17g", minimum, maximum, value, sliderSize, increment, pageIncrement);
 	#if gtk
+		/*
+		 * We're going to modify the scroll bar with gtk_adjustment_configure ().
+		 * This function sends a *slow* value-changed notification to the scroll bar.
+		 * We have to make sure that our own d_valueChangedCallback is not called.
+		 */
+		d_blockValueChangedCallbacks = true;
 		GtkAdjustment *adj = gtk_range_get_adjustment (GTK_RANGE (d_widget));
 		gtk_adjustment_configure (GTK_ADJUSTMENT (adj),
 			NUMdefined (value)         ? value         : gtk_adjustment_get_value          (GTK_ADJUSTMENT (adj)),
@@ -289,7 +305,16 @@ void structGuiScrollBar :: f_set (double minimum, double maximum, double value,
 			NUMdefined (increment)     ? increment     : gtk_adjustment_get_step_increment (GTK_ADJUSTMENT (adj)),
 			NUMdefined (pageIncrement) ? pageIncrement : gtk_adjustment_get_page_increment (GTK_ADJUSTMENT (adj)),
 			NUMdefined (sliderSize)    ? sliderSize    : gtk_adjustment_get_page_size      (GTK_ADJUSTMENT (adj)));
+		/*
+		 * We don't set d_blockValueChangedCallbacks back to false yet, because GTK calls the valueChangedCallback with a delay.
+		 */
 	#elif cocoa
+		/*
+		 * We're going to modify the scroll bar with setMinimum:maximum:...
+		 * This function sends a *synchronous* value-changed notification to the scroll bar.
+		 * We have to make sure that our own d_valueChangedCallback is not called.
+		 */
+		GuiControlBlockValueChangedCallbacks block (this);
 		GuiCocoaScrollBar *scroller = (GuiCocoaScrollBar *) d_widget;
 		[scroller
 			setMinimum:    NUMdefined (minimum)       ? minimum       : [scroller m_minimum]
@@ -312,6 +337,7 @@ void structGuiScrollBar :: f_set (double minimum, double maximum, double value,
 			NUMdefined (pageIncrement) ? pageIncrement : oldPageIncrement,
 			False);
 	#endif
+	trace ("exit");
 }
 
 int structGuiScrollBar :: f_getValue () {
diff --git a/sys/GuiText.cpp b/sys/GuiText.cpp
index 450395f..e55ade6 100644
--- a/sys/GuiText.cpp
+++ b/sys/GuiText.cpp
@@ -996,11 +996,11 @@ GuiText GuiText_create (GuiForm parent, int left, int right, int top, int bottom
 			}
 			[my d_cocoaTextView setFont: theTextFont];
 			[my d_cocoaTextView setAllowsUndo: YES];
-			//[my d_cocoaTextView turnOffLigatures: nil];
+			[my d_cocoaTextView turnOffLigatures: nil];
 			[my d_cocoaTextView setSmartInsertDeleteEnabled: NO];
-			//[my d_cocoaTextView setAutomaticQuoteSubstitutionEnabled: NO];
+			[my d_cocoaTextView setAutomaticQuoteSubstitutionEnabled: NO];
 			[my d_cocoaTextView setAutomaticTextReplacementEnabled: NO];
-			//[my d_cocoaTextView setAutomaticDashSubstitutionEnabled: NO];
+			[my d_cocoaTextView setAutomaticDashSubstitutionEnabled: NO];
 			[my d_cocoaTextView setDelegate: my d_cocoaTextView];
 		} else {
 			my d_widget = [[GuiCocoaTextField alloc] init];
@@ -1701,7 +1701,7 @@ void structGuiText :: f_setString (const wchar_t *text) {
 		 */
 		long j = 0;
 		for (long i = 0; i < length_utf32; i ++) {
-			MelderUtf32 kar = text [i];
+			utf32_t kar = text [i];
 			if (kar == '\n') {   // LF
 				macText [j ++] = 13;   // CR
 			} else if (kar <= 0xFFFF) {
diff --git a/sys/Interpreter.cpp b/sys/Interpreter.cpp
index e1a2d48..ceb3dde 100644
--- a/sys/Interpreter.cpp
+++ b/sys/Interpreter.cpp
@@ -1728,7 +1728,7 @@ void Interpreter_run (Interpreter me, wchar_t *text) {
 					long save_assertErrorLineNumber = assertErrorLineNumber;
 					assertErrorLineNumber = 0;
 					Melder_throw ("Script assertion fails in line ", save_assertErrorLineNumber,
-							": error " L_LEFT_GUILLEMET " ", assertErrorString.string, " " L_RIGHT_GUILLEMET " not raised. Instead: no error.");
+							L": error « ", assertErrorString.string, L" » not raised. Instead: no error.");
 					
 				}
 			} catch (MelderError) {
@@ -1745,7 +1745,7 @@ void Interpreter_run (Interpreter me, wchar_t *text) {
 						Melder_clearError ();
 						autostring errorCopy = errorCopy_nothrow;   // UGLY but necessary (2)
 						Melder_throw ("Script assertion fails in line ", assertErrorLineNumber,
-							": error " L_LEFT_GUILLEMET " ", assertErrorString.string, " " L_RIGHT_GUILLEMET " not raised. Instead:\n",
+							L": error « ", assertErrorString.string, L" » not raised. Instead:\n",
 							errorCopy.peek());
 					}
 				}
@@ -1755,13 +1755,15 @@ void Interpreter_run (Interpreter me, wchar_t *text) {
 		my running = false;
 		my stopped = false;
 	} catch (MelderError) {
-		bool normalExplicitExit = wcsnequ (lines [lineNumber], L"exit ", 5) || Melder_hasError (L"Script exited.");
-		if (! normalExplicitExit && ! assertionFailed) {   // don't show the message twice!
-			while (lines [lineNumber] [0] == '\0') {   // did this use to be a continuation line?
-				lineNumber --;
-				Melder_assert (lineNumber > 0);   // originally empty lines that stayed empty should not generate errors
+		if (lineNumber > 0) {
+			bool normalExplicitExit = wcsnequ (lines [lineNumber], L"exit ", 5) || Melder_hasError (L"Script exited.");
+			if (! normalExplicitExit && ! assertionFailed) {   // don't show the message twice!
+				while (lines [lineNumber] [0] == '\0') {   // did this use to be a continuation line?
+					lineNumber --;
+					Melder_assert (lineNumber > 0);   // originally empty lines that stayed empty should not generate errors
+				}
+				Melder_error_ ("Script line ", lineNumber, L" not performed or completed:\n« ", lines [lineNumber], L" »");
 			}
-			Melder_error_ ("Script line ", lineNumber, " not performed or completed:\n" L_LEFT_GUILLEMET " ", lines [lineNumber], " " L_RIGHT_GUILLEMET);
 		}
 		my numberOfLabels = 0;
 		my running = false;
diff --git a/sys/Strings.cpp b/sys/Strings.cpp
index 59e7a26..6dced45 100644
--- a/sys/Strings.cpp
+++ b/sys/Strings.cpp
@@ -1,6 +1,6 @@
 /* Strings.cpp
  *
- * Copyright (C) 1992-2012 Paul Boersma
+ * Copyright (C) 1992-2012,2014 Paul Boersma
  *
  * 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
@@ -224,7 +224,7 @@ Strings Strings_readFromRawTextFile (MelderFile file) {
 		/*
 		 * Count number of strings.
 		 */
-		long n = MelderReadText_getNumberOfLines (text.peek());
+		int64_t n = MelderReadText_getNumberOfLines (text.peek());
 
 		/*
 		 * Create.
diff --git a/sys/TextEditor.cpp b/sys/TextEditor.cpp
index 201429a..f93c19a 100644
--- a/sys/TextEditor.cpp
+++ b/sys/TextEditor.cpp
@@ -594,8 +594,8 @@ static void menu_cb_convertToCString (EDITOR_ARGS) {
 		} else if (*p == '\\') {
 			MelderInfo_write (L"\\\\");
 		} else if (*p < 0 || *p > 127) {
-			uint32_t kar = *p;
-			if (kar <= 0xFFFF) {
+			utf32_t kar = sizeof (wchar_t) == 2 ? (utf16_t) *p : *p;
+			if (kar <= 0xFFFF) {   // BUG on Windows
 				MelderInfo_write (L"\\u", hex [kar >> 12], hex [(kar >> 8) & 0x0000000F], hex [(kar >> 4) & 0x0000000F], hex [kar & 0x0000000F]);
 			} else {
 				MelderInfo_write (L"\\U", hex [kar >> 28], hex [(kar >> 24) & 0x0000000F], hex [(kar >> 20) & 0x0000000F], hex [(kar >> 16) & 0x0000000F],
diff --git a/sys/Thing.cpp b/sys/Thing.cpp
index b27a147..bbe1cbe 100644
--- a/sys/Thing.cpp
+++ b/sys/Thing.cpp
@@ -190,9 +190,9 @@ bool Thing_member (Thing me, ClassInfo klas) {
 void * _Thing_check (Thing me, ClassInfo klas, const char *fileName, int line) {
 	if (! me) Melder_fatal ("(_Thing_check:) NULL object passed to a function\n"
 		"in file %.100s at line %d.", fileName, line);
-	ClassInfo l_classInfo = my classInfo;
-	while (l_classInfo != klas && l_classInfo != NULL) l_classInfo = l_classInfo -> parent;
-	if (! l_classInfo)
+	ClassInfo classInfo = my classInfo;
+	while (classInfo != klas && classInfo != NULL) classInfo = classInfo -> parent;
+	if (! classInfo)
 		Melder_fatal ("(_Thing_check:) Object of wrong class (%.50s) passed to a function\n"
 				"in file %.100s at line %d.", Melder_peekWcsToUtf8 (my classInfo -> className), fileName, line);
 	return me;
diff --git a/sys/UiPause.cpp b/sys/UiPause.cpp
index cd88d9f..0bf357f 100644
--- a/sys/UiPause.cpp
+++ b/sys/UiPause.cpp
@@ -203,7 +203,7 @@ int UiPause_end (int numberOfContinueButtons, int defaultContinueButton, int can
 			//Melder_flushError (NULL);
 			//Melder_clearError ();
 		} else {
-			//Melder_casual ("Clicked %d", thePauseForm_clicked);
+			//Melder_casual ("Clicked %d", (int) thePauseForm_clicked);
 		}
 	#endif
 	return thePauseForm_clicked;
diff --git a/sys/abcio.cpp b/sys/abcio.cpp
index 633b7ee..4d87f9f 100644
--- a/sys/abcio.cpp
+++ b/sys/abcio.cpp
@@ -720,7 +720,7 @@ int bingeti2 (FILE *f) {
 		} else {
 			unsigned char bytes [2];
 			if (fread (bytes, sizeof (unsigned char), 2, f) != 2) readError (f, "two bytes.");
-			uint16_t externalValue = ((uint16_t) bytes [0] << 8) | (uint16_t) bytes [1];
+			uint16_t externalValue = (uint16_t) ((uint16_t) bytes [0] << 8) | (uint16_t) bytes [1];
 			return (int) (int16_t) externalValue;   // with sign extension if an int is 4 bytes
 		}
 	} catch (MelderError) {
@@ -728,7 +728,7 @@ int bingeti2 (FILE *f) {
 	}
 }
 
-unsigned int bingetu2 (FILE *f) {
+uint16_t bingetu2 (FILE *f) {
 	try {
 		if (binario_shortBE2 && Melder_debug != 18) {
 			unsigned short s;
@@ -737,8 +737,8 @@ unsigned int bingetu2 (FILE *f) {
 		} else {
 			unsigned char bytes [2];
 			if (fread (bytes, sizeof (unsigned char), 2, f) != 2) readError (f, "two bytes.");
-			uint16_t externalValue = ((uint16_t) bytes [0] << 8) | (uint16_t) bytes [1];
-			return (unsigned int) externalValue;
+			uint16_t externalValue = (uint16_t) ((uint16_t) bytes [0] << 8) | (uint16_t) bytes [1];
+			return externalValue;
 		}
 	} catch (MelderError) {
 		Melder_throw ("Unsigned integer not read from 2 bytes in binary file.");
@@ -753,7 +753,7 @@ int bingete2 (FILE *f, int min, int max, const wchar_t *type) {
 		} else {
 			unsigned char bytes [2];
 			if (fread (bytes, sizeof (unsigned char), 2, f) != 2) readError (f, "two bytes.");
-			uint16_t externalValue = ((uint16_t) bytes [0] << 8) | (uint16_t) bytes [1];
+			uint16_t externalValue = (uint16_t) ((uint16_t) bytes [0] << 8) | (uint16_t) bytes [1];
 			result = (short) (int16_t) externalValue;
 		}
 		if (result < min || result > max)
@@ -768,10 +768,10 @@ long bingeti3 (FILE *f) {
 	try {
 		unsigned char bytes [3];
 		if (fread (bytes, sizeof (unsigned char), 3, f) != 3) readError (f, "three bytes.");
-		uint32_t externalValue = ((uint32_t) bytes [0] << 16) | ((uint32_t) bytes [1] << 8) | (uint32_t) bytes [2];
+		uint32_t externalValue = (uint32_t) ((uint32_t) bytes [0] << 16) | (uint32_t) ((uint32_t) bytes [1] << 8) | (uint32_t) bytes [2];
 		if ((bytes [0] & 128) != 0)   // is the 24-bit sign bit on?
 			externalValue |= 0xFF000000;   // extend negative sign to 32 bits
-		return (long) (int32_t) externalValue;   // first convert signedness, then perhaps extend sign to 64 bits!
+		return (long) (int32_t) externalValue;   // first add sign, then perhaps extend sign
 	} catch (MelderError) {
 		Melder_throw ("Signed long integer not read from 3 bytes in binary file.");
 	}
@@ -787,9 +787,9 @@ long bingeti4 (FILE *f) {
 			unsigned char bytes [4];
 			if (fread (bytes, sizeof (unsigned char), 4, f) != 4) readError (f, "four bytes.");
 			uint32_t externalValue = 
-				((uint32_t) bytes [0] << 24) | ((uint32_t) bytes [1] << 16) |
-				((uint32_t) bytes [2] << 8) | (uint32_t) bytes [3];
-			return (long) (int32_t) externalValue;   // first add signedness, then extend
+				(uint32_t) ((uint32_t) bytes [0] << 24) | (uint32_t) ((uint32_t) bytes [1] << 16) |
+				(uint32_t) ((uint32_t) bytes [2] << 8) | (uint32_t) bytes [3];
+			return (long) (int32_t) externalValue;   // first add sign, then perhaps extend sign
 		}
 	} catch (MelderError) {
 		Melder_throw ("Signed long integer not read from 4 bytes in binary file.");
@@ -806,8 +806,8 @@ unsigned long bingetu4 (FILE *f) {
 			unsigned char bytes [4];
 			if (fread (bytes, sizeof (unsigned char), 4, f) != 4) readError (f, "four bytes.");
 			uint32_t externalValue = 
-				((uint32_t) bytes [0] << 24) | ((uint32_t) bytes [1] << 16) |
-				((uint32_t) bytes [2] << 8) | (uint32_t) bytes [3];
+				(uint32_t) ((uint32_t) bytes [0] << 24) | (uint32_t) ((uint32_t) bytes [1] << 16) |
+				(uint32_t) ((uint32_t) bytes [2] << 8) | (uint32_t) bytes [3];
 			return (unsigned long) externalValue;
 		}
 	} catch (MelderError) {
@@ -826,7 +826,7 @@ int bingeti2LE (FILE *f) {
 		} else {
 			unsigned char bytes [2];
 			if (fread (bytes, sizeof (unsigned char), 2, f) != 2) readError (f, "two bytes.");
-			uint16_t externalValue = ((uint16_t) bytes [1] << 8) | (uint16_t) bytes [0];
+			uint16_t externalValue = (uint16_t) ((uint16_t) bytes [1] << 8) | (uint16_t) bytes [0];
 			return (int) (int16_t) externalValue;   // with sign extension if an int is 4 bytes
 		}
 	} catch (MelderError) {
@@ -834,7 +834,7 @@ int bingeti2LE (FILE *f) {
 	}
 }
 
-unsigned int bingetu2LE (FILE *f) {
+uint16_t bingetu2LE (FILE *f) {
 	try {
 		if (binario_shortLE2 && Melder_debug != 18) {
 			unsigned short s;
@@ -843,8 +843,8 @@ unsigned int bingetu2LE (FILE *f) {
 		} else {
 			unsigned char bytes [2];
 			if (fread (bytes, sizeof (unsigned char), 2, f) != 2) readError (f, "two bytes.");
-			uint16_t externalValue = ((uint16_t) bytes [1] << 8) | (uint16_t) bytes [0];
-			return (unsigned int) externalValue;
+			uint16_t externalValue = (uint16_t) ((uint16_t) bytes [1] << 8) | (uint16_t) bytes [0];
+			return externalValue;
 		}
 	} catch (MelderError) {
 		Melder_throw ("Unsigned integer not read from 2 bytes in binary file.");
@@ -873,8 +873,8 @@ long bingeti4LE (FILE *f) {
 		} else {
 			unsigned char bytes [4];
 			if (fread (bytes, sizeof (unsigned char), 4, f) != 4) readError (f, "four bytes.");
-			uint32_t externalValue = ((uint32_t) bytes [3] << 24) | ((uint32_t) bytes [2] << 16) |
-				((uint32_t) bytes [1] << 8) | (uint32_t) bytes [0];
+			uint32_t externalValue = (uint32_t) ((uint32_t) bytes [3] << 24) | (uint32_t) ((uint32_t) bytes [2] << 16) |
+				(uint32_t) ((uint32_t) bytes [1] << 8) | (uint32_t) bytes [0];
 			return (long) (int32_t) externalValue;   // first add signedness, then extend
 		}
 	} catch (MelderError) {
@@ -891,8 +891,8 @@ unsigned long bingetu4LE (FILE *f) {
 		} else {
 			unsigned char bytes [4];
 			if (fread (bytes, sizeof (unsigned char), 4, f) != 4) readError (f, "four bytes.");
-			uint32_t externalValue = ((uint32_t) bytes [3] << 24) | ((uint32_t) bytes [2] << 16) |
-				((uint32_t) bytes [1] << 8) | (uint32_t) bytes [0];
+			uint32_t externalValue = (uint32_t) ((uint32_t) bytes [3] << 24) | (uint32_t) ((uint32_t) bytes [2] << 16) |
+				(uint32_t) ((uint32_t) bytes [1] << 8) | (uint32_t) bytes [0];
 			return (unsigned long) externalValue;
 		}
 	} catch (MelderError) {
@@ -1431,8 +1431,8 @@ char * bingets1 (FILE *f) {
 
 char * bingets2 (FILE *f) {
 	try {
-		unsigned int length = bingetu2 (f);
-		autostring8 result = Melder_malloc (char, length + 1);
+		uint16_t length = bingetu2 (f);
+		autostring8 result = Melder_malloc (char, (int64_t) length + 1);
 		if (fread (result.peek(), sizeof (char), length, f) != length)
 			Melder_throw (feof (f) ? "Reached end of file" : "Error in file", " while trying to read ", length, " one-byte characters.");
 		result [length] = 0;   // trailing null byte
@@ -1467,7 +1467,7 @@ wchar_t * bingetw1 (FILE *f) {
 			result.reset (Melder_malloc (wchar_t, length + 1));
 			for (unsigned short i = 0; i < length; i ++) {
 				if (sizeof (wchar_t) == 2) {
-					result [i] = bingetu2 (f);
+					result [i] = (wchar_t) bingetu2 (f);   // add sign
 				} else {
 					uint16_t kar = bingetu2 (f);
 					if ((kar & 0xF800) == 0xD800) {
@@ -1501,7 +1501,7 @@ wchar_t * bingetw1 (FILE *f) {
 wchar_t * bingetw2 (FILE *f) {
 	try {
 		autostring result = NULL;
-		unsigned short length = bingetu2 (f);
+		uint16_t length = bingetu2 (f);
 		if (length == 0xFFFF) {   // an escape for encoding
 			/*
 			 * UTF-16
@@ -1510,13 +1510,13 @@ wchar_t * bingetw2 (FILE *f) {
 			result.reset (Melder_malloc (wchar_t, length + 1));
 			for (unsigned short i = 0; i < length; i ++) {
 				if (sizeof (wchar_t) == 2) {
-					result [i] = bingetu2 (f);
+					result [i] = (wchar_t) bingetu2 (f);
 				} else {
-					unsigned short kar = bingetu2 (f);
+					uint16_t kar = bingetu2 (f);
 					if ((kar & 0xF800) == 0xD800) {
 						if (kar > 0xDBFF)
 							Melder_throw ("Incorrect Unicode value (first surrogate member ", kar, ").");
-						unsigned short kar2 = bingetu2 (f);
+						uint16_t kar2 = bingetu2 (f);
 						if (kar2 < 0xDC00 || kar2 > 0xDFFF)
 							Melder_throw ("Incorrect Unicode value (second surrogate member ", kar2, ").");
 						result [i] = (((kar & 0x3FF) << 10) | (kar2 & 0x3FF)) + 0x10000;
@@ -1553,13 +1553,13 @@ wchar_t * bingetw4 (FILE *f) {
 			result.reset (Melder_malloc (wchar_t, length + 1));
 			for (unsigned long i = 0; i < length; i ++) {
 				if (sizeof (wchar_t) == 2) {
-					result [i] = bingetu2 (f);
+					result [i] = (wchar_t) bingetu2 (f);
 				} else {
-					unsigned short kar = bingetu2 (f);
+					uint16_t kar = bingetu2 (f);
 					if ((kar & 0xF800) == 0xD800) {
 						if (kar > 0xDBFF)
 							Melder_throw ("Incorrect Unicode value (first surrogate member ", kar, ").");
-						unsigned short kar2 = bingetu2 (f);
+						uint16_t kar2 = bingetu2 (f);
 						if (kar2 < 0xDC00 || kar2 > 0xDFFF)
 							Melder_throw ("Incorrect Unicode value (second surrogate member ", kar2, ").");
 						result [i] = (((kar & 0x3FF) << 10) | (kar2 & 0x3FF)) + 0x10000;
@@ -1641,7 +1641,7 @@ static inline void binpututf16 (wchar_t character, FILE *f) {
 	if (sizeof (wchar_t) == 2) {   // wchar_t is UTF-16?
 		binputu2 (character, f);
 	} else {   // wchar_t is UTF-32.
-		MelderUtf32 kar = character;
+		utf32_t kar = character;
 		if (kar <= 0xFFFF) {
 			binputu2 (character, f);
 		} else if (kar <= 0x10FFFF) {
diff --git a/sys/abcio.h b/sys/abcio.h
index 2192f02..c303810 100644
--- a/sys/abcio.h
+++ b/sys/abcio.h
@@ -81,7 +81,7 @@ void texputw4 (MelderFile file, const wchar_t *s, const wchar_t *s1, const wchar
 		int fgetc (FILE *f);   int fputc (int c, FILE *f);   // 0..255
 */
 unsigned int bingetu1 (FILE *f);   void binputu1 (unsigned int i, FILE *f);   /* 0..255 */
-unsigned int bingetu2 (FILE *f);   void binputu2 (unsigned int i, FILE *f);   /* 0..65535 */
+uint16_t bingetu2 (FILE *f);   void binputu2 (unsigned int i, FILE *f);   /* 0..65535 */
 unsigned long bingetu4 (FILE *f);   void binputu4 (unsigned long i, FILE *f);   /* 0..4294967295 */
 
 int bingeti1 (FILE *f);   void binputi1 (int i, FILE *f);   /* -128..127 */
@@ -97,7 +97,7 @@ long bingeti4 (FILE *f);   void binputi4 (long i, FILE *f);   /* -2147483648..21
 int bingeti2LE (FILE *f);   void binputi2LE (int i, FILE *f);   /* -32768..32767 */
 long bingeti3LE (FILE *f);   void binputi3LE (long i, FILE *f);   /* -8388608..2148388607 */
 long bingeti4LE (FILE *f);   void binputi4LE (long i, FILE *f);   /* -2147483648..2147483647 */
-unsigned int bingetu2LE (FILE *f);   void binputu2LE (unsigned int i, FILE *f);   /* 0..65535 */
+uint16_t bingetu2LE (FILE *f);   void binputu2LE (unsigned int i, FILE *f);   /* 0..65535 */
 unsigned long bingetu4LE (FILE *f);   void binputu4LE (unsigned long i, FILE *f);   /* 0..4294967295 */
 /*
 	Read or write signed or unsigned integers from or to 2 or 4 bytes in the stream 'f',
diff --git a/sys/melder.cpp b/sys/melder.cpp
index 6928153..8e9c8e1 100644
--- a/sys/melder.cpp
+++ b/sys/melder.cpp
@@ -817,7 +817,7 @@ static void mac_message (NSAlertStyle macAlertType, const wchar_t *messageW) {
 	int messageLength = wcslen (messageW);
 	int j = 0;
 	for (int i = 0; i < messageLength && j <= 4000 - 3; i ++) {
-		uint32_t kar = messageW [i];
+		utf32_t kar = messageW [i];
 		if (kar <= 0xFFFF) {
 			messageU [j ++] = kar;
 		} else if (kar <= 0x10FFFF) {
diff --git a/sys/melder.h b/sys/melder.h
index a0d061c..af3262f 100644
--- a/sys/melder.h
+++ b/sys/melder.h
@@ -39,9 +39,17 @@
 	//#define swprintf  __mingw_snwprintf
 #endif
 #include <stdbool.h>
+/*
+ * The following two lines are for obsolete (i.e. C99) versions of stdint.h
+ */
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
 #include <stdint.h>
 
 typedef wchar_t wchar;
+typedef uint8_t  utf8_t;
+typedef uint16_t utf16_t;
+typedef uint32_t utf32_t;
 
 bool Melder_wcsequ_firstCharacterCaseInsensitive (const wchar_t *string1, const wchar_t *string2);
 
@@ -58,6 +66,22 @@ bool Melder_wcsequ_firstCharacterCaseInsensitive (const wchar_t *string1, const
 #ifndef NULL
 	#define NULL  ((void *) 0)
 #endif
+#ifndef INT32_MAX
+	#define INT8_MAX         127
+	#define INT16_MAX        32767
+	#define INT32_MAX        2147483647
+	#define INT64_MAX        9223372036854775807LL
+
+	#define INT8_MIN          -128
+	#define INT16_MIN         -32768
+	#define INT32_MIN        (-INT32_MAX-1)
+	#define INT64_MIN        (-INT64_MAX-1)
+
+	#define UINT8_MAX         255
+	#define UINT16_MAX        65535
+	#define UINT32_MAX        4294967295U
+	#define UINT64_MAX        18446744073709551615ULL
+#endif
 
 /*
  * Operating system version control.
@@ -73,8 +97,8 @@ typedef struct { double red, green, blue, transparency; } double_rgbt;
 	The following routines return a static string, chosen from a circularly used set of 11 buffers.
 	You can call at most 11 of them in one Melder_casual call, for instance.
 */
-const wchar_t * Melder_integer (long value);
-const wchar_t * Melder_bigInteger (long long value);
+const wchar_t * Melder_integer (int64_t value);
+const wchar_t * Melder_bigInteger (int64_t value);
 const wchar_t * Melder_boolean (bool value);   // "yes" or "no"
 const wchar_t * Melder_double (double value);   // "--undefined--" or something in the "%.15g", "%.16g", or "%.17g" formats
 const wchar_t * Melder_single (double value);   // "--undefined--" or something in the "%.8g" format
@@ -111,15 +135,15 @@ void Melder_writeToConsole (const wchar_t *message, bool useStderr);
 
 void Melder_alloc_init (void);   // to be called around program start-up
 void Melder_message_init (void);   // to be called around program start-up
-void * _Melder_malloc (unsigned long size);
-#define Melder_malloc(type,numberOfElements)  (type *) _Melder_malloc ((numberOfElements) * sizeof (type))
-void * _Melder_malloc_f (unsigned long size);
+void * _Melder_malloc (int64_t size);
+#define Melder_malloc(type,numberOfElements)  (type *) _Melder_malloc ((numberOfElements) * (int64_t) sizeof (type))
+void * _Melder_malloc_f (int64_t size);
 #define Melder_malloc_f(type,numberOfElements)  (type *) _Melder_malloc_f ((numberOfElements) * sizeof (type))
-void * Melder_realloc (void *pointer, long size);
-void * Melder_realloc_f (void *pointer, long size);
-void * _Melder_calloc (long numberOfElements, long elementSize);
+void * Melder_realloc (void *pointer, int64_t size);
+void * Melder_realloc_f (void *pointer, int64_t size);
+void * _Melder_calloc (int64_t numberOfElements, int64_t elementSize);
 #define Melder_calloc(type,numberOfElements)  (type *) _Melder_calloc (numberOfElements, sizeof (type))
-void * _Melder_calloc_f (long numberOfElements, long elementSize);
+void * _Melder_calloc_f (int64_t numberOfElements, int64_t elementSize);
 #define Melder_calloc_f(type,numberOfElements)  (type *) _Melder_calloc_f (numberOfElements, sizeof (type))
 char * Melder_strdup (const char *string);
 char * Melder_strdup_f (const char *string);
@@ -127,8 +151,8 @@ wchar_t * Melder_wcsdup (const wchar_t *string);
 wchar_t * Melder_wcsdup_f (const wchar_t *string);
 int Melder_strcmp (const char *string1, const char *string2);   // regards null string as empty string
 int Melder_wcscmp (const wchar_t *string1, const wchar_t *string2);   // regards null string as empty string
-int Melder_strncmp (const char *string1, const char *string2, unsigned long n);
-int Melder_wcsncmp (const wchar_t *string1, const wchar_t *string2, unsigned long n);
+int Melder_strncmp (const char *string1, const char *string2, int64_t n);
+int Melder_wcsncmp (const wchar_t *string1, const wchar_t *string2, int64_t n);
 wchar_t * Melder_wcstok (wchar_t *string, const wchar_t *delimiter, wchar_t **last);   // circumvents platforms where wcstok has only two arguments
 wchar_t * Melder_wcsdecompose (const wchar_t *string);
 wchar_t * Melder_wcsprecompose (const wchar_t *string);
@@ -155,9 +179,6 @@ const uint32_t kMelder_textOutputEncoding_ASCII = 0x41534349;
 const uint32_t kMelder_textOutputEncoding_ISO_LATIN1 = 0x4C415401;
 const uint32_t kMelder_textOutputEncoding_FLAC = 0x464C4143;
 
-typedef uint16_t MelderUtf16;
-typedef uint32_t MelderUtf32;
-
 bool Melder_isValidAscii (const wchar_t *string);
 bool Melder_strIsValidUtf8 (const char *string);
 bool Melder_isEncodable (const wchar_t *string, int outputEncoding);
@@ -184,7 +205,7 @@ void Melder_wcsTo8bitFileRepresentation_inline (const wchar_t *wcs, char *utf8);
 void Melder_8bitFileRepresentationToWcs_inline (const char *utf8, wchar_t *wcs);
 extern "C" wchar_t * Melder_peekUtf8ToWcs (const char *string);
 extern "C" char * Melder_peekWcsToUtf8 (const wchar_t *string);
-extern "C" const MelderUtf16 * Melder_peekWcsToUtf16 (const wchar_t *string);
+extern "C" const utf16_t * Melder_peekWcsToUtf16 (const wchar_t *string);
 const void * Melder_peekWcsToCfstring (const wchar_t *string);
 void Melder_fwriteWcsAsUtf8 (const wchar_t *ptr, size_t n, FILE *f);
 
@@ -352,7 +373,7 @@ typedef struct {
 typedef struct {
 	unsigned long length;
 	unsigned long bufferSize;
-	MelderUtf16 *string;   // a growing buffer, never shrunk (can only be freed by MelderString16_free)
+	utf16_t *string;   // a growing buffer, never shrunk (can only be freed by MelderString16_free)
 } MelderString16;
 
 void MelderString_free (MelderString *me);   // frees the "string" attribute only (and sets other attributes to zero)
@@ -394,7 +415,7 @@ MelderReadText MelderReadText_createFromFile (MelderFile file);
 MelderReadText MelderReadText_createFromString (const wchar_t *string);
 wchar_t MelderReadText_getChar (MelderReadText text);
 wchar_t * MelderReadText_readLine (MelderReadText text);
-long MelderReadText_getNumberOfLines (MelderReadText me);
+int64_t MelderReadText_getNumberOfLines (MelderReadText me);
 const wchar_t * MelderReadText_getLineNumber (MelderReadText text);
 void MelderReadText_delete (MelderReadText text);
 
@@ -503,9 +524,6 @@ class autoMelderDivertInfo {
 		~autoMelderDivertInfo () { Melder_divertInfo (NULL); }
 };
 
-void Melder_print (const wchar_t *s);
-	/* Write formatted text to the Info window without clearing it, and without adding a new-line symbol at the end. */
-
 void Melder_clearInfo (void);   /* Clear the Info window. */
 const wchar_t * Melder_getInfo (void);
 void Melder_help (const wchar_t *query);
@@ -534,6 +552,7 @@ struct MelderArg {
 	MelderArg (const wchar_t *      arg) : type (1), argW (arg) { }
 	MelderArg (const  char   *      arg) : type (2), arg8 (arg) { }
 	MelderArg (const double         arg) : type (1), argW (Melder_double          (arg)) { }
+	MelderArg (const     long long  arg) : type (1), argW (Melder_integer         (arg)) { }
 	MelderArg (const          long  arg) : type (1), argW (Melder_integer         (arg)) { }
 	MelderArg (const unsigned long  arg) : type (1), argW (Melder_integer         (arg)) { }
 	MelderArg (const          int   arg) : type (1), argW (Melder_integer         (arg)) { }
@@ -1178,7 +1197,7 @@ public:
 		//if (Melder_debug == 39) Melder_casual ("autostring: leaving assignment from C-string; new = %ld", ptr);
 	}
 	#endif
-	T& operator[] (long i) {
+	template <class U> T& operator[] (U i) {
 		return ptr [i];
 	}
 	T * peek () const {
@@ -1196,7 +1215,7 @@ public:
 		if (ptr) Melder_free (ptr);
 		ptr = string;
 	}
-	void resize (long new_size) {
+	template <class U> void resize (U new_size) {
 		T *tmp = (T *) Melder_realloc (ptr, new_size * sizeof (T));
 		ptr = tmp;
 	}
diff --git a/sys/melder_alloc.cpp b/sys/melder_alloc.cpp
index cd359be..76b9599 100644
--- a/sys/melder_alloc.cpp
+++ b/sys/melder_alloc.cpp
@@ -1,6 +1,6 @@
 /* melder_alloc.cpp
  *
- * Copyright (C) 1992-2011 Paul Boersma
+ * Copyright (C) 1992-2011,2014 Paul Boersma
  *
  * 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
@@ -27,6 +27,7 @@
  * pb 2009/07/31 tracing by Melder_debug 34
  * pb 2010/12/28 split into _e and _f versions, and created a rainy-day fund
  * pb 2011/04/05 C++
+ * pb 2014/12/17 made everything int64_t
  */
 
 #include "melder.h"
@@ -39,7 +40,7 @@ static double totalNumberOfAllocations = 0, totalNumberOfDeallocations = 0, tota
 /*
  * The rainy-day fund.
  *
- * Typically, memory allocation for data is entirely checked by using the _e versions of the allocation routines,
+ * Typically, memory allocation for data is entirely checked by using the normal versions of the allocation routines,
  * which will call Melder_error if they are out of memory.
  * When data allocation is indeed out of memory,
  * the application will present an error message to the user saying that the data could not be created.
@@ -53,7 +54,7 @@ static double totalNumberOfAllocations = 0, totalNumberOfDeallocations = 0, tota
  * If the user doesn't do that, the application will crash upon the next failing allocation of a _f routine.
  */
 
-#define theRainyDayFund_SIZE  300000
+#define theRainyDayFund_SIZE  3000000
 static char *theRainyDayFund = NULL;
 
 void Melder_alloc_init (void) {
@@ -61,29 +62,31 @@ void Melder_alloc_init (void) {
 	assert (theRainyDayFund != NULL);
 }
 
-void * _Melder_malloc (unsigned long size) {
+void * _Melder_malloc (int64_t size) {
 	if (size <= 0)
-		Melder_throw ("Can never allocate ", size, " bytes.");
+		Melder_throw ("Can never allocate ", Melder_bigInteger (size), " bytes.");
+	if (sizeof (size_t) < 8 && size > /*2147483647*/ INT32_MAX)
+		Melder_throw ("Can never allocate ", Melder_bigInteger (size), " bytes. Use a 64-bit edition of Praat instead?");
 	void *result = malloc (size);
 	if (result == NULL)
-		Melder_throw ("Out of memory: there is not enough room for another ", size, " bytes.");
-	if (Melder_debug == 34) { Melder_casual ("Melder_malloc\t%ld\t%ld\t1", result, size); }
+		Melder_throw ("Out of memory: there is not enough room for another ", Melder_bigInteger (size), " bytes.");
+	if (Melder_debug == 34) { Melder_casual ("Melder_malloc\t%p\t%ls\t1", result, Melder_bigInteger (size)); }
 	totalNumberOfAllocations += 1;
 	totalAllocationSize += size;
 	return result;
 }
 
-void * _Melder_malloc_f (unsigned long size) {
+void * _Melder_malloc_f (int64_t size) {
 	if (size <= 0)
-		Melder_fatal ("(Melder_malloc_f:) Can never allocate %ld bytes.", size);
+		Melder_fatal ("(Melder_malloc_f:) Can never allocate %ls bytes.", Melder_bigInteger (size));
 	void *result = malloc (size);
 	if (result == NULL) {
-		if (theRainyDayFund != NULL) free (theRainyDayFund);
+		if (theRainyDayFund != NULL) { free (theRainyDayFund); theRainyDayFund = NULL; }
 		result = malloc (size);
 		if (result != NULL) {
 			Melder_flushError ("Praat is very low on memory.\nSave your work and quit Praat.\nIf you don't do that, Praat may crash.");
 		} else {
-			Melder_fatal ("Out of memory: there is not enough room for another %ld bytes.", size);
+			Melder_fatal ("Out of memory: there is not enough room for another %ls bytes.", Melder_bigInteger (size));
 		}
 	}
 	totalNumberOfAllocations += 1;
@@ -94,19 +97,19 @@ void * _Melder_malloc_f (unsigned long size) {
 void _Melder_free (void **ptr) {
 	if (*ptr == NULL) return;
 	free (*ptr);
-	if (Melder_debug == 34) { Melder_casual ("Melder_free\t%ld\t?\t?", *ptr); }
+	if (Melder_debug == 34) { Melder_casual ("Melder_free\t%p\t?\t?", *ptr); }
 	*ptr = NULL;
 	totalNumberOfDeallocations += 1;
 }
 
-void * Melder_realloc (void *ptr, long size) {
+void * Melder_realloc (void *ptr, int64_t size) {
 	if (size <= 0)
-		Melder_throw ("Can never allocate ", size, " bytes.");
+		Melder_throw ("Can never allocate ", Melder_bigInteger (size), " bytes.");
 	void *result = realloc (ptr, size);   // will not show in the statistics...
 	if (result == NULL)
-		Melder_throw ("Out of memory. Could not extend room to ", size, " bytes.");
+		Melder_throw ("Out of memory. Could not extend room to ", Melder_bigInteger (size), " bytes.");
 	if (ptr == NULL) {   // is it like malloc?
-		if (Melder_debug == 34) { Melder_casual ("Melder_realloc\t%ld\t%ld\t1", result, size); }
+		if (Melder_debug == 34) { Melder_casual ("Melder_realloc\t%p\t%ls\t1", result, Melder_bigInteger (size)); }
 		totalNumberOfAllocations += 1;
 		totalAllocationSize += size;
 	} else if (result != ptr) {   // did realloc do a malloc-and-free?
@@ -120,18 +123,18 @@ void * Melder_realloc (void *ptr, long size) {
 	return result;
 }
 
-void * Melder_realloc_f (void *ptr, long size) {
+void * Melder_realloc_f (void *ptr, int64_t size) {
 	void *result;
 	if (size <= 0)
-		Melder_fatal ("(Melder_realloc_f:) Can never allocate %ld bytes.", size);
+		Melder_fatal ("(Melder_realloc_f:) Can never allocate %ls bytes.", Melder_bigInteger (size));
 	result = realloc (ptr, size);   /* Will not show in the statistics... */
 	if (result == NULL) {
-		if (theRainyDayFund != NULL) free (theRainyDayFund);
+		if (theRainyDayFund != NULL) { free (theRainyDayFund); theRainyDayFund = NULL; }
 		result = realloc (ptr, size);
 		if (result != NULL) {
 			Melder_flushError ("Praat is very low on memory.\nSave your work and quit Praat.\nIf you don't do that, Praat may crash.");
 		} else {
-			Melder_fatal ("Out of memory. Could not extend room to %ld bytes.", size);
+			Melder_fatal ("Out of memory. Could not extend room to %ls bytes.", Melder_bigInteger (size));
 		}
 	}
 	if (ptr == NULL) {   /* Is it like malloc? */
@@ -148,35 +151,37 @@ void * Melder_realloc_f (void *ptr, long size) {
 	return result;
 }
 
-void * _Melder_calloc (long nelem, long elsize) {
+void * _Melder_calloc (int64_t nelem, int64_t elsize) {
 	void *result;
 	if (nelem <= 0)
-		Melder_throw ("Can never allocate ", nelem, " elements.");
+		Melder_throw ("Can never allocate ", Melder_bigInteger (nelem), " elements.");
 	if (elsize <= 0)
-		Melder_throw ("Can never allocate elements whose size is ", elsize, " bytes.");
+		Melder_throw ("Can never allocate elements whose size is ", Melder_bigInteger (elsize), " bytes.");
+	if (sizeof (size_t) < 8 && nelem * elsize > 2147483647)
+		Melder_throw ("Can never allocate ", Melder_bigInteger (nelem * elsize), " bytes. Use a 64-bit edition of Praat instead?");
 	result = calloc (nelem, elsize);
 	if (result == NULL)
-		Melder_throw ("Out of memory: there is not enough room for ", nelem, " more elements whose sizes are ", elsize, " bytes each.");
-	if (Melder_debug == 34) { Melder_casual ("Melder_calloc\t%ld\t%ld\t%ld", result, nelem, elsize); }
+		Melder_throw ("Out of memory: there is not enough room for ", Melder_bigInteger (nelem), " more elements whose sizes are ", elsize, " bytes each.");
+	if (Melder_debug == 34) { Melder_casual ("Melder_calloc\t%p\t%ls\t%ls", result, Melder_bigInteger (nelem), Melder_bigInteger (elsize)); }
 	totalNumberOfAllocations += 1;
 	totalAllocationSize += nelem * elsize;
 	return result;
 }
 
-void * _Melder_calloc_f (long nelem, long elsize) {
+void * _Melder_calloc_f (int64_t nelem, int64_t elsize) {
 	void *result;
 	if (nelem <= 0)
-		Melder_fatal ("(Melder_calloc_f:) Can never allocate %ld elements.", nelem);
+		Melder_fatal ("(Melder_calloc_f:) Can never allocate %ls elements.", Melder_bigInteger (nelem));
 	if (elsize <= 0)
-		Melder_fatal ("(Melder_calloc_f:) Can never allocate elements whose size is %ld bytes.", elsize);
+		Melder_fatal ("(Melder_calloc_f:) Can never allocate elements whose size is %ls bytes.", Melder_bigInteger (elsize));
 	result = calloc (nelem, elsize);
 	if (result == NULL) {
-		if (theRainyDayFund != NULL) free (theRainyDayFund);
+		if (theRainyDayFund != NULL) { free (theRainyDayFund); theRainyDayFund = NULL; }
 		result = calloc (nelem, elsize);
 		if (result != NULL) {
 			Melder_flushError ("Praat is very low on memory.\nSave your work and quit Praat.\nIf you don't do that, Praat may crash.");
 		} else {
-			Melder_fatal ("Out of memory: there is not enough room for %ld more elements whose sizes are %ld bytes each.", nelem, elsize);
+			Melder_fatal ("Out of memory: there is not enough room for %ls more elements whose sizes are %ls bytes each.", Melder_bigInteger (nelem), Melder_bigInteger (elsize));
 		}
 	}
 	totalNumberOfAllocations += 1;
@@ -186,12 +191,12 @@ void * _Melder_calloc_f (long nelem, long elsize) {
 
 char * Melder_strdup (const char *string) {
 	if (! string) return NULL;
-	long size = strlen (string) + 1;
+	int64_t size = strlen (string) + 1;
 	char *result = (char *) malloc (size * sizeof (char));
 	if (result == NULL)
-		Melder_throw ("Out of memory: there is not enough room to duplicate a text of ", size - 1, " characters.");
+		Melder_throw ("Out of memory: there is not enough room to duplicate a text of ", Melder_bigInteger (size - 1), " characters.");
 	strcpy (result, string);
-	if (Melder_debug == 34) { Melder_casual ("Melder_strdup\t%ld\t%ld\t1", result, size); }
+	if (Melder_debug == 34) { Melder_casual ("Melder_strdup\t%p\t%ls\t1", result, Melder_bigInteger (size)); }
 	totalNumberOfAllocations += 1;
 	totalAllocationSize += size;
 	return result;
@@ -199,15 +204,15 @@ char * Melder_strdup (const char *string) {
 
 char * Melder_strdup_f (const char *string) {
 	if (! string) return NULL;
-	long size = strlen (string) + 1;
+	int64_t size = strlen (string) + 1;
 	char *result = (char *) malloc (size * sizeof (char));
 	if (result == NULL) {
-		if (theRainyDayFund != NULL) free (theRainyDayFund);
+		if (theRainyDayFund != NULL) { free (theRainyDayFund); theRainyDayFund = NULL; }
 		result = (char *) malloc (size * sizeof (char));
 		if (result != NULL) {
 			Melder_flushError ("Praat is very low on memory.\nSave your work and quit Praat.\nIf you don't do that, Praat may crash.");
 		} else {
-			Melder_fatal ("Out of memory: there is not enough room to duplicate a text of %ld characters.", size - 1);
+			Melder_fatal ("Out of memory: there is not enough room to duplicate a text of %ls characters.", Melder_bigInteger (size - 1));
 		}
 	}
 	strcpy (result, string);
@@ -218,12 +223,12 @@ char * Melder_strdup_f (const char *string) {
 
 wchar_t * Melder_wcsdup (const wchar_t *string) {
 	if (! string) return NULL;
-	long size = wcslen (string) + 1;
+	int64_t size = wcslen (string) + 1;
 	wchar_t *result = (wchar_t *) malloc (size * sizeof (wchar_t));
 	if (result == NULL)
-		Melder_throw ("Out of memory: there is not enough room to duplicate a text of ", size - 1, " characters.");
+		Melder_throw ("Out of memory: there is not enough room to duplicate a text of ", Melder_bigInteger (size - 1), " characters.");
 	wcscpy (result, string);
-	if (Melder_debug == 34) { Melder_casual ("Melder_wcsdup\t%ld\t%ld\t4", result, size); }
+	if (Melder_debug == 34) { Melder_casual ("Melder_wcsdup\t%p\t%ls\t4", result, Melder_bigInteger (size)); }
 	totalNumberOfAllocations += 1;
 	totalAllocationSize += size * sizeof (wchar_t);
 	return result;
@@ -231,15 +236,15 @@ wchar_t * Melder_wcsdup (const wchar_t *string) {
 
 wchar_t * Melder_wcsdup_f (const wchar_t *string) {
 	if (! string) return NULL;
-	long size = wcslen (string) + 1;
+	int64_t size = wcslen (string) + 1;
 	wchar_t *result = (wchar_t *) malloc (size * sizeof (wchar_t));
 	if (result == NULL) {
-		if (theRainyDayFund != NULL) free (theRainyDayFund);
+		if (theRainyDayFund != NULL) { free (theRainyDayFund); theRainyDayFund = NULL; }
 		result = (wchar_t *) malloc (size * sizeof (wchar_t));
 		if (result != NULL) {
 			Melder_flushError ("Praat is very low on memory.\nSave your work and quit Praat.\nIf you don't do that, Praat may crash.");
 		} else {
-			Melder_fatal ("Out of memory: there is not enough room to duplicate a text of %ld characters.", size - 1);
+			Melder_fatal ("Out of memory: there is not enough room to duplicate a text of %ls characters.", Melder_bigInteger (size - 1));
 		}
 	}
 	wcscpy (result, string);
@@ -274,7 +279,7 @@ int Melder_strcmp (const char *string1, const char *string2) {
 	return strcmp (string1, string2);
 }
 
-int Melder_strncmp (const char *string1, const char *string2, unsigned long n) {
+int Melder_strncmp (const char *string1, const char *string2, int64_t n) {
 	if (string1 == NULL) string1 = "";
 	if (string2 == NULL) string2 = "";
 	return strncmp (string1, string2, n);
@@ -286,7 +291,7 @@ int Melder_wcscmp (const wchar_t *string1, const wchar_t *string2) {
 	return wcscmp (string1, string2);
 }
 
-int Melder_wcsncmp (const wchar_t *string1, const wchar_t *string2, unsigned long n) {
+int Melder_wcsncmp (const wchar_t *string1, const wchar_t *string2, int64_t n) {
 	if (string1 == NULL) string1 = L"";
 	if (string2 == NULL) string2 = L"";
 	return wcsncmp (string1, string2, n);
diff --git a/sys/melder_audio.cpp b/sys/melder_audio.cpp
index d4f770a..ac1d1ad 100644
--- a/sys/melder_audio.cpp
+++ b/sys/melder_audio.cpp
@@ -543,6 +543,7 @@ void MelderAudio_play16 (int16_t *buffer, long sampleRate, long numberOfSamples,
 		PaStreamParameters outputParameters = { 0 };
 		outputParameters. device = Pa_GetDefaultOutputDevice ();
 		const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo (outputParameters. device);
+		trace ("the device can handle %d channels", (int) deviceInfo -> maxOutputChannels);
 		if (my numberOfChannels > deviceInfo -> maxOutputChannels) {
 			my numberOfChannels = deviceInfo -> maxOutputChannels;
 		}
@@ -783,9 +784,52 @@ void MelderAudio_play16 (int16_t *buffer, long sampleRate, long numberOfSamples,
 						Melder_throw ("This computer probably has no sound card.");
 					if (err == MMSYSERR_NOMEM)
 						Melder_throw ("Not enough free memory to play any sound at all.");
-					if (err == WAVERR_BADFORMAT)
-						Melder_throw ("Bad sound format? Should not occur! Report bug to the author!");
-					Melder_throw ("Unknown error ", err, " while trying to play a sound? Report bug to the author!");
+					if (err == WAVERR_BADFORMAT) {
+						if (my numberOfChannels > 2) {
+							/*
+							 * Retry with 2 channels.
+							 */
+							my numberOfChannels = 2;
+							waveFormat. nChannels = my numberOfChannels;
+							waveFormat. nBlockAlign = my numberOfChannels * waveFormat. wBitsPerSample / 8;
+							waveFormat. nAvgBytesPerSec = waveFormat. nBlockAlign * waveFormat. nSamplesPerSec;
+							err = waveOutOpen (& my hWaveOut, WAVE_MAPPER, & waveFormat, 0, 0, CALLBACK_NULL | WAVE_ALLOWSYNC);
+							if (err != MMSYSERR_NOERROR)
+								Melder_throw ("Bad sound format even after reduction to 2 channels? Should not occur! Report bug to the author!");
+							MelderAudio_isPlaying = true;
+						} else {
+							Melder_throw ("Bad sound format? Should not occur! Report bug to the author!");
+						}
+					} else {
+						Melder_throw ("Unknown error ", err, " while trying to play a sound? Report bug to the author!");
+					}
+				}
+				if (numberOfChannels > my numberOfChannels) {
+					/*
+					 * Redistribute the in channels over the out channels.
+					 */
+					if (numberOfChannels == 4 && my numberOfChannels == 2) {   // a common case
+						int16_t *in = & my buffer [0], *out = & my buffer [0];
+						for (long isamp = 1; isamp <= numberOfSamples; isamp ++) {
+							long in1 = *in ++, in2 = *in ++, in3 = *in ++, in4 = *in ++;
+							*out ++ = (in1 + in2) / 2;
+							*out ++ = (in3 + in4) / 2;
+						}
+					} else {
+						int16_t *in = & my buffer [0], *out = & my buffer [0];
+						for (long isamp = 1; isamp <= numberOfSamples; isamp ++) {
+							for (long iout = 1; iout <= my numberOfChannels; iout ++) {
+								long outValue = 0;
+								long numberOfIn = numberOfChannels / my numberOfChannels;
+								if (iout == my numberOfChannels)
+									numberOfIn += numberOfChannels % my numberOfChannels;
+								for (long iin = 1; iin <= numberOfIn; iin ++)
+									outValue += *in ++;
+								outValue /= numberOfIn;
+								*out ++ = outValue;
+							}
+						}
+					}
 				}
 
 				my waveHeader. dwFlags = 0;
diff --git a/sys/melder_audiofiles.cpp b/sys/melder_audiofiles.cpp
index 6d6672e..1ed117d 100644
--- a/sys/melder_audiofiles.cpp
+++ b/sys/melder_audiofiles.cpp
@@ -457,7 +457,7 @@ static void Melder_checkWavFile (FILE *f, int *numberOfChannels, int *encoding,
 	char data [14], chunkID [4];
 	bool formatChunkPresent = false, dataChunkPresent = false;
 	int numberOfBitsPerSamplePoint = -1;
-	long dataChunkSize = -1;
+	uint32_t dataChunkSize = 0xffffffff;
 
 	if (fread (data, 1, 4, f) < 4)   Melder_throw ("File too small: no RIFF statement.");
 	if (! strnequ (data, "RIFF", 4)) Melder_throw ("Not a WAV file (RIFF statement expected).");
@@ -469,7 +469,7 @@ static void Melder_checkWavFile (FILE *f, int *numberOfChannels, int *encoding,
 	/* Search for Format Chunk and Data Chunk. */
 
 	while (fread (chunkID, 1, 4, f) == 4) {
-		long chunkSize = bingeti4LE (f);
+		uint32_t chunkSize = bingetu4LE (f);
 		if (Melder_debug == 23) {
 			Melder_warning (Melder_integer (chunkID [0]), L" ", Melder_integer (chunkID [1]), L" ",
 				Melder_integer (chunkID [2]), L" ", Melder_integer (chunkID [3]), L" ", Melder_integer (chunkSize));
@@ -565,10 +565,10 @@ static void Melder_checkWavFile (FILE *f, int *numberOfChannels, int *encoding,
 			*startOfData = ftell (f);
 			if (chunkSize & 1) chunkSize ++;
 			if (chunkSize < 0) {   // incorrect data chunk (sometimes -44); assume that the data run till the end of the file
-				fseek (f, 0, SEEK_END);
-				long endOfData = ftell (f);
+				fseeko (f, 0LL, SEEK_END);
+				off_t endOfData = ftello (f);
 				dataChunkSize = chunkSize = endOfData - *startOfData;
-				fseek (f, *startOfData, SEEK_SET);
+				fseeko (f, *startOfData, SEEK_SET);
 			}
 			if (Melder_debug == 23) {
 				for (long i = 1; i <= chunkSize; i ++)
@@ -873,6 +873,9 @@ static void Melder_readMp3File (FILE *f, int numberOfChannels, double **buffer,
 		Melder_throw ("Error decoding MP3 file.");
 }
 
+int64_t Melder_fread (uint8_t *bytes, double numberOfBytes, FILE *f) {
+}
+
 void Melder_readAudioToFloat (FILE *f, int numberOfChannels, int encoding, double **buffer, long numberOfSamples) {
 	try {
 		switch (encoding) {
@@ -914,7 +917,8 @@ void Melder_readAudioToFloat (FILE *f, int numberOfChannels, int encoding, doubl
 					} else { // optimize
 						long numberOfBytes = numberOfChannels * numberOfSamples * numberOfBytesPerSamplePerChannel;
 						unsigned char *bytes = (unsigned char *) & buffer [numberOfChannels] [numberOfSamples] + sizeof (double) - numberOfBytes;
-						if (fread (bytes, 1, numberOfBytes, f) < numberOfBytes) throw MelderError ();   // read 16-bit data into last quarter of buffer
+						Melder_assert (numberOfBytes > 0);
+						if (fread (bytes, 1, numberOfBytes, f) < (size_t) numberOfBytes) throw MelderError ();   // read 16-bit data into last quarter of buffer
 						if (numberOfChannels == 1) {
 							for (long isamp = 1; isamp <= numberOfSamples; isamp ++) {
 								unsigned char byte1 = * bytes ++, byte2 = * bytes ++;
@@ -946,9 +950,17 @@ void Melder_readAudioToFloat (FILE *f, int numberOfChannels, int encoding, doubl
 							}
 						}
 					} else { // optimize
-						long numberOfBytes = numberOfChannels * numberOfSamples * numberOfBytesPerSamplePerChannel;
+						double numberOfBytes_f = (double) numberOfChannels * (double) numberOfSamples * (double) numberOfBytesPerSamplePerChannel;
+						if (isinf (numberOfBytes_f) || numberOfBytes_f > (double) (1LL << 53)) {
+							Melder_throw ("Cannot read ", numberOfBytes_f, " bytes, because that crosses the 9-petabyte limit.");
+						}
+						if (numberOfBytes_f > (double) SIZE_MAX) {
+							Melder_throw ("Cannot read ", numberOfBytes_f, " bytes. Perhaps try a 64-bit edition of Praat?");
+						}
+						Melder_assert (numberOfBytes_f >= 0.0);
+						size_t numberOfBytes = (size_t) numberOfBytes_f;   // cast is safe because overflow and signedness have been checked
 						unsigned char *bytes = (unsigned char *) & buffer [numberOfChannels] [numberOfSamples] + sizeof (double) - numberOfBytes;
-						if (fread (bytes, 1, numberOfBytes, f) < numberOfBytes) throw MelderError ();   // read 16-bit data into last quarter of buffer
+						if (fread (bytes, 1, numberOfBytes, f) < (size_t) numberOfBytes) throw MelderError ();   // read 16-bit data into last quarter of buffer
 						if (numberOfChannels == 1) {
 							for (long isamp = 1; isamp <= numberOfSamples; isamp ++) {
 								unsigned char byte1 = * bytes ++, byte2 = * bytes ++;
diff --git a/sys/melder_files.cpp b/sys/melder_files.cpp
index 4f6702a..28ded40 100644
--- a/sys/melder_files.cpp
+++ b/sys/melder_files.cpp
@@ -103,7 +103,7 @@ void Melder_wcsTo8bitFileRepresentation_inline (const wchar_t *wcs, char *utf8)
 		UniChar unipath [kMelder_MAXPATH+1];
 		long n = wcslen (wcs), n_utf16 = 0;
 		for (long i = 0; i < n; i ++) {
-			uint32_t kar = wcs [i];
+			utf32_t kar = wcs [i];
 			if (kar <= 0xFFFF) {
 				unipath [n_utf16 ++] = kar;   // including null byte
 			} else if (kar <= 0x10FFFF) {
@@ -137,9 +137,9 @@ void Melder_8bitFileRepresentationToWcs_inline (const char *path, wchar_t *wpath
 		long n_utf16 = CFStringGetLength (cfpath2);
 		long n_wcs = 0;
 		for (long i = 0; i < n_utf16; i ++) {
-			uint32_t kar = CFStringGetCharacterAtIndex (cfpath2, i);
+			utf32_t kar = CFStringGetCharacterAtIndex (cfpath2, i);
 			if (kar >= 0xD800 && kar <= 0xDBFF) {
-				uint32_t kar2 = CFStringGetCharacterAtIndex (cfpath2, ++ i);
+				utf32_t kar2 = CFStringGetCharacterAtIndex (cfpath2, ++ i);
 				if (kar2 >= 0xDC00 && kar2 <= 0xDFFF) {
 					kar = (((kar & 0x3FF) << 10) | (kar2 & 0x3FF)) + 0x10000;
 				} else {
diff --git a/sys/melder_ftoa.cpp b/sys/melder_ftoa.cpp
index c0cc95f..411de42 100644
--- a/sys/melder_ftoa.cpp
+++ b/sys/melder_ftoa.cpp
@@ -47,13 +47,19 @@
 static wchar_t buffers [NUMBER_OF_BUFFERS] [MAXIMUM_NUMERIC_STRING_LENGTH + 1];
 static int ibuffer = 0;
 
-const wchar_t * Melder_integer (long value) {
+const wchar_t * Melder_integer (int64_t value) {
 	if (++ ibuffer == NUMBER_OF_BUFFERS) ibuffer = 0;
-	swprintf (buffers [ibuffer], MAXIMUM_NUMERIC_STRING_LENGTH, L"%ld", value);
+	if (sizeof (long) == 8) {
+		swprintf (buffers [ibuffer], MAXIMUM_NUMERIC_STRING_LENGTH, L"%ld", value);
+	} else if (sizeof (long long) == 8) {
+		swprintf (buffers [ibuffer], MAXIMUM_NUMERIC_STRING_LENGTH, L"%lld", value);
+	} else {
+		Melder_fatal ("Neither long nor long long is 8 bytes on this machine.");
+	}
 	return buffers [ibuffer];
 }
 
-const wchar_t * Melder_bigInteger (long long value) {
+const wchar_t * Melder_bigInteger (int64_t value) {
 	wchar_t *text;
 	int quintillions, quadrillions, trillions, billions, millions, thousands, units;
 	bool firstDigitPrinted = false;
diff --git a/sys/melder_info.cpp b/sys/melder_info.cpp
index c6e5941..bcaf263 100644
--- a/sys/melder_info.cpp
+++ b/sys/melder_info.cpp
@@ -1,6 +1,6 @@
 /* melder_info.cpp
  *
- * Copyright (C) 1992-2012 Paul Boersma
+ * Copyright (C) 1992-2012,2014 Paul Boersma
  *
  * 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
@@ -240,7 +240,7 @@ void MelderInfo_close (void) {
 	if (theInfos == & theForegroundBuffer) {
 		/*
 			When writing to the Info window or the console, we must add a newline symbol,
-			because a subsequent Melder_print call has to start on the next line.
+			because a subsequent MelderInfo_write call has to start on the next line.
 			When writing to a diverted string, we must *not* add a newline symbol,
 			because scripts expect returned strings without appended newlines!
 		*/
@@ -292,26 +292,6 @@ const wchar_t * Melder_getInfo (void) {
 	return theInfos -> string ? theInfos -> string : L"";
 }
 
-void Melder_print (const wchar_t *s) {
-	//Melder_assert (theInfos == & theForegroundBuffer);   // Never diverted.
-	/*
-	 * This procedure is always called from a script; therefore, this is unlikely to be called when the info is diverted.
-	 * Unlikely, but not impossible!
-	 * The small possibility occurs when the script, having diverted the info through an assignment command,
-	 * causes the progress bar to move. If the user chooses Run while the progress bar moves,
-	 * the script will start to run and may call print:
-	 *    pitch = To Pitch... 0 75 600
-	 * Therefore, we write into the Info window explicitly. The results will still be strange,
-	 * and a better solution would be to disallow the script from running (BUG: accept fewer events in waitWhileProgress).
-	 */
-	if (theInformation == defaultInformation) {
-		theInformation ((wchar_t *) s);   // Do not print the previous lines again.
-	} else {
-		MelderString_append (& theForegroundBuffer, s);
-		theInformation (theForegroundBuffer. string);
-	}
-}
-
 void Melder_information (const wchar_t *s1) {
 	MelderString_empty (theInfos);
 	MelderString_append (theInfos, s1);
diff --git a/sys/melder_readtext.cpp b/sys/melder_readtext.cpp
index d11d49e..aa88101 100644
--- a/sys/melder_readtext.cpp
+++ b/sys/melder_readtext.cpp
@@ -1,6 +1,6 @@
 /* melder_readtext.cpp
  *
- * Copyright (C) 2008-2011 Paul Boersma
+ * Copyright (C) 2008-2011,2014 Paul Boersma
  *
  * 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
@@ -22,6 +22,7 @@
  * pb 2010/03/09 support Unicode values above 0xFFFF
  * pb 2011/04/05 C++
  * pb 2011/07/03 C++
+ * pb 2014/12/17 int64_t
  */
 
 #include "melder.h"
@@ -36,20 +37,20 @@ wchar_t MelderReadText_getChar (MelderReadText me) {
 	} else {
 		if (* my readPointer8 == '\0') return 0;
 		if (my input8Encoding == kMelder_textInputEncoding_UTF8) {
-			unsigned long kar = * (unsigned char *) my readPointer8 ++;
+			utf32_t kar = * (unsigned char *) my readPointer8 ++;
 			if (kar <= 0x7F) {
 				return kar;
 			} else if (kar <= 0xDF) {
-				unsigned long kar2 = * (unsigned char *) my readPointer8 ++;
+				utf32_t kar2 = * (unsigned char *) my readPointer8 ++;
 				return ((kar & 0x1F) << 6) | (kar2 & 0x3F);
 			} else if (kar <= 0xEF) {
-				unsigned long kar2 = * (unsigned char *) my readPointer8 ++;
-				unsigned long kar3 = * (unsigned char *) my readPointer8 ++;
+				utf32_t kar2 = * (unsigned char *) my readPointer8 ++;
+				utf32_t kar3 = * (unsigned char *) my readPointer8 ++;
 				return ((kar & 0x0F) << 12) | ((kar2 & 0x3F) << 6) | (kar3 & 0x3F);
 			} else if (kar <= 0xF4) {
-				unsigned long kar2 = * (unsigned char *) my readPointer8 ++;
-				unsigned long kar3 = * (unsigned char *) my readPointer8 ++;
-				unsigned long kar4 = * (unsigned char *) my readPointer8 ++;
+				utf32_t kar2 = * (unsigned char *) my readPointer8 ++;
+				utf32_t kar3 = * (unsigned char *) my readPointer8 ++;
+				utf32_t kar4 = * (unsigned char *) my readPointer8 ++;
 				return ((kar & 0x07) << 18) | ((kar2 & 0x3F) << 12) | ((kar3 & 0x3F) << 6) | (kar4 & 0x3F);   // BUG: should be UTF-16 on Windows
 			}
 		} else if (my input8Encoding == kMelder_textInputEncoding_MACROMAN) {
@@ -105,8 +106,8 @@ wchar_t * MelderReadText_readLine (MelderReadText me) {
 	}
 }
 
-long MelderReadText_getNumberOfLines (MelderReadText me) {
-	long n = 0;
+int64_t MelderReadText_getNumberOfLines (MelderReadText me) {
+	int64_t n = 0;
 	if (my stringW != NULL) {
 		wchar_t *p = & my stringW [0];
 		for (; *p != '\0'; p ++) if (*p == '\n') n ++;
@@ -120,7 +121,7 @@ long MelderReadText_getNumberOfLines (MelderReadText me) {
 }
 
 const wchar_t * MelderReadText_getLineNumber (MelderReadText me) {
-	long result = 1;
+	int64_t result = 1;
 	if (my stringW != NULL) {
 		wchar_t *p = my stringW;
 		while (my readPointerW - p > 0) {
@@ -138,13 +139,34 @@ const wchar_t * MelderReadText_getLineNumber (MelderReadText me) {
 	return Melder_integer (result);
 }
 
+static size_t fread_multi (char *buffer, size_t numberOfBytes, FILE *f) {
+	off_t offset = 0;
+	size_t numberOfBytesRead = 0;
+	const size_t chunkSize = 1000000000;
+	while (numberOfBytes > chunkSize) {
+		size_t numberOfBytesReadInChunk = fread (buffer + offset, sizeof (char), chunkSize, f);
+		numberOfBytesRead += numberOfBytesReadInChunk;
+		if (numberOfBytesReadInChunk < chunkSize) {
+			return numberOfBytesRead;
+		}
+		numberOfBytes -= chunkSize;
+		offset += chunkSize;
+	}
+	size_t numberOfBytesReadInLastChunk = fread (buffer + offset, sizeof (char), numberOfBytes, f);
+	numberOfBytesRead += numberOfBytesReadInLastChunk;
+	return numberOfBytesRead;
+}
+
 static wchar_t * _MelderFile_readText (MelderFile file, char **string8) {
 	try {
 		int type = 0;   // 8-bit
 		autostring text;
 		autofile f = Melder_fopen (file, "rb");
-		fseek (f, 0, SEEK_END);
-		unsigned long length = ftell (f);
+		if (fseeko (f, 0, SEEK_END) < 0) {
+			Melder_throw ("Cannot count the bytes in the file.");
+		}
+		Melder_assert (sizeof (off_t) >= 8);
+		int64_t length = ftello (f);
 		rewind (f);
 		if (length >= 2) {
 			int firstByte = fgetc (f), secondByte = fgetc (f);
@@ -157,20 +179,24 @@ static wchar_t * _MelderFile_readText (MelderFile file, char **string8) {
 		if (type == 0) {
 			rewind (f);   // length and type already set correctly.
 			autostring8 text8bit = Melder_malloc (char, length + 1);
-			fread (text8bit.peek(), sizeof (char), length, f);
+			Melder_assert (text8bit.peek() != NULL);
+			int64_t numberOfBytesRead = fread_multi (text8bit.peek(), length, f);
+			if (numberOfBytesRead < length)
+				Melder_throw ("The file contains ", (double) length, " bytes, but we could read only ",
+					(double) numberOfBytesRead, " of them.");
 			text8bit [length] = '\0';
 			/*
 			 * Count and repair null bytes.
 			 */
 			if (length > 0) {
-				long numberOfNullBytes = 0;
-				for (char *p = & text8bit [length - 1]; (long) (p - text8bit.peek()) >= 0; p --) {
+				int64_t numberOfNullBytes = 0;
+				for (char *p = & text8bit [length - 1]; (int64_t) (p - text8bit.peek()) >= 0; p --) {
 					if (*p == '\0') {
 						numberOfNullBytes += 1;
 						/*
 						 * Shift.
 						 */
-						for (char *q = p; (unsigned long) (q - text8bit.peek()) < length; q ++) {
+						for (char *q = p; (int64_t) (q - text8bit.peek()) < length; q ++) {
 							*q = q [1];
 						}
 					}
@@ -190,52 +216,50 @@ static wchar_t * _MelderFile_readText (MelderFile file, char **string8) {
 			length = length / 2 - 1;   // Byte Order Mark subtracted. Length = number of UTF-16 codes
 			text.reset (Melder_malloc (wchar_t, length + 1));
 			if (type == 1) {
-				for (unsigned long i = 0; i < length; i ++) {
-					unsigned short kar = bingetu2 (f);
-					if (sizeof (wchar_t) == 2) {   // wchar_t is UTF-16?
-						text [i] = kar;
-					} else {   // wchar_t is UTF-32.
-						unsigned long kar1 = kar;
-						if (kar1 < 0xD800) {
-							text [i] = kar1;
-						} else if (kar1 < 0xDC00) {
+				for (int64_t i = 0; i < length; i ++) {
+					utf16_t kar = bingetu2 (f);
+					if (sizeof (wchar_t) == 2) {   // wchar_t is UTF-16 (or its signed counterpart)?
+						text [i] = (wchar_t) kar;
+					} else {   // wchar_t is UTF-32 (or its signed counterpart)
+						if (kar < 0xD800) {
+							text [i] = (wchar_t) kar;   // convert up without sign extension
+						} else if (kar < 0xDC00) {
 							length --;
-							unsigned long kar2 = bingetu2 (f);
+							utf16_t kar2 = bingetu2 (f);
 							if (kar2 >= 0xDC00 && kar2 <= 0xDFFF) {
-								text [i] = 0x10000 + ((kar1 & 0x3FF) << 10) + (kar2 & 0x3FF);
+								text [i] = (wchar_t) (0x00010000 + (uint32_t) (((uint32_t) kar & 0x000003FF) << 10) + (uint32_t) ((uint32_t) kar2 & 0x000003FF));
 							} else {
 								text [i] = UNICODE_REPLACEMENT_CHARACTER;
 							}
-						} else if (kar1 < 0xE000) {
+						} else if (kar < 0xE000) {
 							text [i] = UNICODE_REPLACEMENT_CHARACTER;
-						} else if (kar1 <= 0xFFFF) {
-							text [i] = kar1;
+						} else if (kar <= 0xFFFF) {
+							text [i] = (wchar_t) kar;   // convert up without sign extension
 						} else {
 							Melder_fatal ("MelderFile_readText: unsigned short greater than 0xFFFF: should not occur.");
 						}
 					}
 				}
 			} else {
-				for (unsigned long i = 0; i < length; i ++) {
-					unsigned short kar = bingetu2LE (f);
-					if (sizeof (wchar_t) == 2) {   // wchar_t is UTF-16?
-						text [i] = kar;
-					} else {   // wchar_t is UTF-32
-						unsigned long kar1 = kar;
-						if (kar1 < 0xD800) {
-							text [i] = kar1;
-						} else if (kar1 < 0xDC00) {
+				for (int64_t i = 0; i < length; i ++) {
+					utf16_t kar = bingetu2LE (f);
+					if (sizeof (wchar_t) == 2) {   // wchar_t is UTF-16 (or its signed counterpart)?
+						text [i] = (wchar_t) kar;
+					} else {   // wchar_t is UTF-32 (or its signed counterpart)
+						if (kar < 0xD800) {
+							text [i] = (wchar_t) kar;   // convert up without sign extension
+						} else if (kar < 0xDC00) {
 							length --;
-							unsigned long kar2 = bingetu2LE (f);
-							if (kar2 >= 0xDC00 && kar2 <= 0xDFFF) {
-								text [i] = 0x10000 + ((kar1 & 0x3FF) << 10) + (kar2 & 0x3FF);
+							utf16_t kar1 = bingetu2LE (f);
+							if (kar1 >= 0xDC00 && kar1 <= 0xDFFF) {
+								text [i] = (wchar_t) (0x00010000 + (uint32_t) (((uint32_t) kar & 0x000003FF) << 10) + (uint32_t) ((uint32_t) kar1 & 0x000003FF));
 							} else {
 								text [i] = UNICODE_REPLACEMENT_CHARACTER;
 							}
-						} else if (kar1 < 0xE000) {
+						} else if (kar < 0xE000) {
 							text [i] = UNICODE_REPLACEMENT_CHARACTER;
-						} else if (kar1 <= 0xFFFF) {
-							text [i] = kar1;
+						} else if (kar <= 0xFFFF) {
+							text [i] = (wchar_t) kar;   // convert up without sign extension
 						} else {
 							Melder_fatal ("MelderFile_readText: unsigned short greater than 0xFFFF: should not occur.");
 						}
diff --git a/sys/melder_strings.cpp b/sys/melder_strings.cpp
index 01b8f9f..2e56d22 100644
--- a/sys/melder_strings.cpp
+++ b/sys/melder_strings.cpp
@@ -39,7 +39,7 @@ void MelderString16_free (MelderString16 *me) {
 	Melder_free (my string);
 	if (Melder_debug == 34) fprintf (stderr, "from MelderString16_free\t%p\t%ld\t%ld\n", my string, my bufferSize, 2L);
 	totalNumberOfDeallocations += 1;
-	totalDeallocationSize += my bufferSize * sizeof (MelderUtf16);
+	totalDeallocationSize += my bufferSize * sizeof (utf16_t);
 	my bufferSize = 0;
 	my length = 0;
 }
@@ -84,7 +84,7 @@ void MelderString16_empty (MelderString16 *me) {
 		MelderString16_free (me);
 	}
 	unsigned long sizeNeeded = 1;
-	expandIfNecessary (MelderUtf16)
+	expandIfNecessary (utf16_t)
 	my string [0] = '\0';
 	my length = 0;
 }
@@ -358,12 +358,12 @@ void MelderString_appendCharacter (MelderString *me, wchar_t character) {
 
 void MelderString16_appendCharacter (MelderString16 *me, wchar_t character) {
 	unsigned long sizeNeeded = my length + 3;   // make room for character, potential surrogate character, and null byte
-	expandIfNecessary (MelderUtf16)
+	expandIfNecessary (utf16_t)
 	if (sizeof (wchar_t) == 2) {   // wchar_t is UTF-16?
 		my string [my length] = character;
 		my length ++;
 	} else {   // wchar_t is UTF-32.
-		MelderUtf32 kar = character;
+		utf32_t kar = character;
 		if (kar <= 0xFFFF) {
 			my string [my length] = character;
 			my length ++;
diff --git a/sys/melder_textencoding.cpp b/sys/melder_textencoding.cpp
index 653eb79..fd9b014 100644
--- a/sys/melder_textencoding.cpp
+++ b/sys/melder_textencoding.cpp
@@ -1,6 +1,6 @@
 /* melder_textencoding.cpp
  *
- * Copyright (C) 2007-2011 Paul Boersma
+ * Copyright (C) 2007-2011,2014 Paul Boersma
  *
  * 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
@@ -304,7 +304,7 @@ void Melder_8bitToWcs_inline (const char *string, wchar_t *wcs, int inputEncodin
 	const unsigned char *p = (const unsigned char *) & string [0];
 	if (inputEncoding == kMelder_textInputEncoding_UTF8) {
 		while (*p != '\0') {
-			uint32_t kar = * p ++;
+			utf32_t kar = * p ++;
 			if (kar <= 0x7F) {
 				* q ++ = kar;
 			} else if (kar <= 0xDF) {
@@ -413,7 +413,7 @@ unsigned long wcslen_utf8 (const wchar_t *wcs, bool expandNewlines) {
 	long length = 0;
 	for (const wchar_t *p = & wcs [0]; *p != '\0'; p ++) {
 		if (sizeof (wchar_t) == 2) {
-			unsigned short kar = *p;
+			utf16_t kar = *p;
 			if (kar <= 0x007F) {
 				#ifdef _WIN32
 					if (expandNewlines && kar == '\n') length ++;
@@ -432,7 +432,7 @@ unsigned long wcslen_utf8 (const wchar_t *wcs, bool expandNewlines) {
 				length += 3;
 			}
 		} else {
-			unsigned long kar = *p;
+			utf32_t kar = *p;
 			if (kar <= 0x00007F) {
 				#ifdef _WIN32
 					if (expandNewlines && kar == '\n') length ++;
@@ -555,7 +555,7 @@ char * Melder_peekWcsToUtf8 (const wchar_t *text) {
 	return buffer [ibuffer];
 }
 
-const MelderUtf16 * Melder_peekWcsToUtf16 (const wchar_t *text) {
+const utf16_t * Melder_peekWcsToUtf16 (const wchar_t *text) {
 	if (text == NULL) return NULL;
 	static MelderString16 buffers [11] = { { 0 } };
 	static int ibuffer = 0;
diff --git a/sys/praat.cpp b/sys/praat.cpp
index 530acf2..dc19f49 100644
--- a/sys/praat.cpp
+++ b/sys/praat.cpp
@@ -1516,10 +1516,25 @@ void praat_run (void) {
 	}
 
 	Melder_assert (wcsequ (Melder_double (1.5), L"1.5"));   // check locale settings; because of the required file portability Praat cannot stand "1,5"
-	{ int dummy = 200; Melder_assert ((int) (signed char) dummy == -56); }   // bingeti1 relies on this
-	{ int dummy = 200; Melder_assert ((int) (unsigned char) dummy == 200); }
-	{ uint16_t dummy = 40000; Melder_assert ((int) (int16_t) dummy == -25536); }   // bingeti2 relies on this
-	{ uint16_t dummy = 40000; Melder_assert ((short) (int16_t) dummy == -25536); }   // bingete2 relies on this
+	{ int dummy = 200;
+		Melder_assert ((int) (signed char) dummy == -56);   // bingeti1 relies on this
+		Melder_assert ((int) (unsigned char) dummy == 200);
+		Melder_assert ((double) dummy == 200.0);
+		Melder_assert ((double) (signed char) dummy == -56.0);
+		Melder_assert ((double) (unsigned char) dummy == 200.0);
+	}
+	{ uint16_t dummy = 40000;
+		Melder_assert ((int) (int16_t) dummy == -25536);   // bingeti2 relies on this
+		Melder_assert ((short) (int16_t) dummy == -25536);   // bingete2 relies on this
+		Melder_assert ((double) dummy == 40000.0);
+		Melder_assert ((double) (int16_t) dummy == -25536.0);
+	}
+	{ uint32_t dummy = 0xffffffff;
+		Melder_assert (wcsequ (Melder_integer (dummy), L"4294967295"));
+	}
+
+	if (sizeof (off_t) < 8)
+		Melder_fatal ("sizeof(off_t) is less than 8. Compile Praat with -D_FILE_OFFSET_BITS=64.");
 
 	if (Melder_batch) {
 		if (thePraatStandAloneScriptText != NULL) {
diff --git a/sys/praat_actions.cpp b/sys/praat_actions.cpp
index 66e5191..e0ec535 100644
--- a/sys/praat_actions.cpp
+++ b/sys/praat_actions.cpp
@@ -182,7 +182,7 @@ static void deleteDynamicMenu (void) {
 	trace ("deletion #%ld", ++ numberOfDeletions);
 	for (int i = 1; i <= theNumberOfActions; i ++) {
 		if (theActions [i]. button) {
-			trace ("trying to destroy action %d of %d: %ls", i, (int) theNumberOfActions, theActions [i]. title);
+			trace ("trying to destroy action %d of %d: %ls", (int) i, (int) theNumberOfActions, theActions [i]. title);
 			#if gtk || cocoa
 				if (theActions [i]. button -> d_parent == praat_form) {
 					trace ("destroy a label or a push button or a cascade button");
diff --git a/sys/praat_picture.cpp b/sys/praat_picture.cpp
index 9201927..11efc64 100644
--- a/sys/praat_picture.cpp
+++ b/sys/praat_picture.cpp
@@ -80,7 +80,7 @@ static void updateSizeMenu (void) {
 	}
 }
 static void setFontSize (int fontSize) {
-	//Melder_casual("Praat picture: set font size %d", fontSize);
+	//Melder_casual("Praat picture: set font size %d", (int) fontSize);
 	{// scope
 		autoPraatPicture picture;
 		Graphics_setFontSize (GRAPHICS, fontSize);
diff --git a/sys/praat_script.cpp b/sys/praat_script.cpp
index 61611db..466eb35 100644
--- a/sys/praat_script.cpp
+++ b/sys/praat_script.cpp
@@ -45,6 +45,15 @@ static int praat_findObjectFromString (Interpreter interpreter, const wchar_t *s
 				if (wcsequ (className, Thing_className ((Thing) OBJECT)) && wcsequ (givenName, object -> name))
 					return IOBJECT;
 			}
+			/*
+			 * No object with that name. Perhaps the class name was wrong?
+			 */
+			ClassInfo klas = Thing_classFromClassName (className);
+			WHERE_DOWN (1) {
+				Data object = (Data) OBJECT;
+				if (wcsequ (klas -> className, Thing_className ((Thing) OBJECT)) && wcsequ (givenName, object -> name))
+					return IOBJECT;
+			}
 			Melder_throw ("No object with that name.");
 		} else {
 			/*
@@ -189,12 +198,15 @@ int praat_executeCommand (Interpreter interpreter, wchar_t *command) {
 		} else if (wcsnequ (command, L"clearinfo", 9)) {
 			Melder_clearInfo ();
 		} else if (wcsnequ (command, L"print ", 6)) {
-			Melder_print (command + 6);
+			MelderInfo_write (command + 6);
+			MelderInfo_drain ();
 		} else if (wcsnequ (command, L"printtab", 8)) {
-			Melder_print (L"\t");
+			MelderInfo_write (L"\t");
+			MelderInfo_drain ();
 		} else if (wcsnequ (command, L"printline", 9)) {
-			if (command [9] == ' ') Melder_print (command + 10);
-			Melder_print (L"\n");
+			if (command [9] == ' ') MelderInfo_write (command + 10);
+			MelderInfo_write (L"\n");
+			MelderInfo_drain ();
 		} else if (wcsnequ (command, L"fappendinfo ", 12)) {
 			if (theCurrentPraatObjects != & theForegroundPraatObjects)
 				Melder_throw ("The script command \"fappendinfo\" is not available inside pictures.");
diff --git a/sys/praat_statistics.cpp b/sys/praat_statistics.cpp
index d132f85..8a994de 100644
--- a/sys/praat_statistics.cpp
+++ b/sys/praat_statistics.cpp
@@ -1,6 +1,6 @@
 /* praat_statistics.cpp
  *
- * Copyright (C) 1992-2012 Paul Boersma
+ * Copyright (C) 1992-2012,2014 Paul Boersma
  *
  * 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
@@ -60,6 +60,8 @@ void praat_reportIntegerProperties () {
 	MelderInfo_writeLine (L"A \"long integer\" is ",       Melder_integer (sizeof (long)        * 8), L" bits.");
 	MelderInfo_writeLine (L"A \"long long integer\" is ",  Melder_integer (sizeof (long long)   * 8), L" bits.");
 	MelderInfo_writeLine (L"A pointer is ",                Melder_integer (sizeof (void *)      * 8), L" bits.");
+	MelderInfo_writeLine (L"A memory object size is ",     Melder_integer (sizeof (size_t)      * 8), L" bits.");
+	MelderInfo_writeLine (L"A file offset is ",            Melder_integer (sizeof (off_t)       * 8), L" bits.");
 	MelderInfo_close ();
 }
 
diff --git a/sys/praat_version.h b/sys/praat_version.h
index 3dd6076..438a9dc 100644
--- a/sys/praat_version.h
+++ b/sys/praat_version.h
@@ -1,5 +1,5 @@
-#define PRAAT_VERSION_STR 5.4
-#define PRAAT_VERSION_NUM 5400
+#define PRAAT_VERSION_STR 5.4.04
+#define PRAAT_VERSION_NUM 5404
 #define PRAAT_YEAR 2014
-#define PRAAT_MONTH October
-#define PRAAT_DAY 4
+#define PRAAT_MONTH December
+#define PRAAT_DAY 28
diff --git a/test/fon/soundFiles.praat b/test/fon/soundFiles.praat
index 25f786d..a3cb433 100644
--- a/test/fon/soundFiles.praat
+++ b/test/fon/soundFiles.praat
@@ -11,7 +11,7 @@ procedure test .type$ .extension$ .duration
 		sound2 = Read from file... kanweg.'.extension$'
 		t = stopwatch
 		energy2 = Get energy in air
-		assert "'energy1:12'" = "'energy2:12'"
+		assert "'energy1:12'" = "'energy2:12'"   ; 'numberOfChannels'
 		plus sound
 		Remove
 		deleteFile ("kanweg." + .extension$)
diff --git a/test/sys/script2.praat b/test/sys/script2.praat
index aabb046..e468d1e 100644
Binary files a/test/sys/script2.praat and b/test/sys/script2.praat differ

-- 
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