[med-svn] [praat] 01/03: New upstream version 6.0.26

Rafael Laboissiere rafael at debian.org
Tue Mar 7 23:22:31 UTC 2017


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

rafael pushed a commit to branch master
in repository praat.

commit 9e679d88dbde862893255495d3a89748b4a9d52e
Author: Rafael Laboissiere <rafael at debian.org>
Date:   Fri Mar 3 12:21:15 2017 -0300

    New upstream version 6.0.26
---
 dwsys/regularExp.cpp                          | 12 ++--
 dwtools/CC.cpp                                | 17 ++++-
 dwtools/MFCC.cpp                              |  6 ++
 dwtools/MFCC.h                                |  2 +
 dwtools/manual_dwtools.cpp                    | 12 ++--
 fon/FunctionEditor.cpp                        | 20 +++++-
 fon/FunctionEditor.h                          |  3 +-
 fon/RunnerMFC.cpp                             | 51 ++++++++-------
 fon/RunnerMFC.h                               |  3 +-
 fon/SoundRecorder.cpp                         |  2 +-
 fon/TextGridEditor.cpp                        | 91 +++++++++++++++++++++++++++
 fon/TextGridEditor.h                          |  6 +-
 fon/manual_Script.cpp                         |  6 +-
 fon/manual_tutorials.cpp                      | 40 ++++++++----
 fon/praat_TextGrid_init.cpp                   |  6 +-
 kar/longchar.cpp                              | 10 +--
 kar/longchar.h                                |  4 +-
 makefiles/makefile.defs.mingw32               |  4 +-
 makefiles/makefile.defs.mingw64               |  4 +-
 sys/DemoEditor.cpp                            | 11 ----
 sys/Formula.cpp                               | 30 ++++++++-
 sys/GraphicsScreen.cpp                        | 29 +++------
 sys/GuiShell.cpp                              |  4 +-
 sys/InfoEditor.cpp                            | 21 +++++--
 sys/ManPages.cpp                              | 38 +++++------
 sys/Manual.cpp                                |  8 +--
 sys/Ui.cpp                                    | 16 ++---
 sys/melder.h                                  |  5 ++
 sys/melder_sysenv.cpp                         | 40 ++++++++++--
 sys/praat.cpp                                 | 17 ++++-
 sys/praat_logo.cpp                            |  4 +-
 sys/praat_version.h                           | 10 +--
 test/fon ExperimentMFC/simplest.ExperimentMFC |  2 +-
 test/fon/resample16_8.praat                   | 48 ++++++++++++++
 test/fon/resample22_8.praat                   | 59 +++++++++++++++++
 test/fon/resample44_8.praat                   | 60 ++++++++++++++++++
 36 files changed, 540 insertions(+), 161 deletions(-)

diff --git a/dwsys/regularExp.cpp b/dwsys/regularExp.cpp
index f38d767..73942ee 100644
--- a/dwsys/regularExp.cpp
+++ b/dwsys/regularExp.cpp
@@ -2053,8 +2053,8 @@ static void emit_class_byte (char32 c) {
 		/* For case insensitive character classes, emit both upper and lower case
 		   versions of alphabetical characters. */
 
-		*Code_Emit_Ptr++ = (char32) towlower ((int) c);
-		*Code_Emit_Ptr++ = (char32) towupper ((int) c);
+		*Code_Emit_Ptr++ = tolower32 (c);
+		*Code_Emit_Ptr++ = toupper32 (c);
 	} else {
 		*Code_Emit_Ptr++ = c;
 	}
@@ -3151,7 +3151,7 @@ static int match (char32 *prog, int *branch_index_param) {
 
 				while ( (test = *opnd++) != '\0') {
 					if (AT_END_OF_STRING (Reg_Input) ||
-					        towlower ((int) *Reg_Input++) != test) {
+					        tolower32 (*Reg_Input++) != test) {
 
 						MATCH_RETURN (0);
 					}
@@ -3852,7 +3852,7 @@ static unsigned long greedy (char32 *p, long max) {
 
 		case SIMILAR: /* Case insensitive version of EXACTLY */
 			while (count < max_cmp                  &&
-			        *operand == (char32) towlower ((int) *input_str) &&
+			        *operand == tolower32 (*input_str) &&
 			        !AT_END_OF_STRING (input_str)) {
 				count++; input_str++;
 			}
@@ -4195,7 +4195,7 @@ static void adjustcase (char32 *str, int len, char32 chgcase) {
 		case 'u':
 		case 'U':
 			for (i = 0; i < len; i++) {
-				* (string + i) = (char32) towupper ( (int) * (string + i));
+				* (string + i) = toupper32 (* (string + i));
 			}
 
 			break;
@@ -4203,7 +4203,7 @@ static void adjustcase (char32 *str, int len, char32 chgcase) {
 		case 'l':
 		case 'L':
 			for (i = 0; i < len; i++) {
-				* (string + i) = (char32) towlower ( (int) * (string + i));
+				* (string + i) = tolower32 (* (string + i));
 			}
 
 			break;
diff --git a/dwtools/CC.cpp b/dwtools/CC.cpp
index 077d7f1..905dcc3 100644
--- a/dwtools/CC.cpp
+++ b/dwtools/CC.cpp
@@ -49,15 +49,26 @@
 
 Thing_implement (CC, Sampled, 1);
 
+static long CC_getMaximumNumberOfCoefficientsUsed (CC me) {
+	long numberOfCoefficients = 0;
+	for (long iframe = 1; iframe <= my nx; iframe ++) {
+		CC_Frame cf = (CC_Frame) & my frame [iframe];
+		long numberOfCoefficients_iframe = cf -> numberOfCoefficients;
+		if (numberOfCoefficients_iframe > numberOfCoefficients) {
+			numberOfCoefficients = numberOfCoefficients_iframe;
+		}
+	}
+	return numberOfCoefficients;
+}
+
 void structCC :: v_info () {
 	structDaata :: v_info ();
 	MelderInfo_writeLine (U"Time domain:", xmin, U" to ", xmax, U" seconds");
 	MelderInfo_writeLine (U"Number of frames: ", nx);
 	MelderInfo_writeLine (U"Time step: ", dx, U" seconds");
 	MelderInfo_writeLine (U"First frame at: ", x1, U" seconds");
-	MelderInfo_writeLine (U"Number of coefficients: ", maximumNumberOfCoefficients);
-	MelderInfo_writeLine (U"Minimum frequency: ", fmin, U" Hz");
-	MelderInfo_writeLine (U"Maximum frequency: ", fmax, U" Hz");
+	MelderInfo_writeLine (U"Maximum number of coefficients possible: ", maximumNumberOfCoefficients);
+	MelderInfo_writeLine (U"Maximum number of coefficients used: ", CC_getMaximumNumberOfCoefficientsUsed (this));
 }
 
 void CC_Frame_init (CC_Frame me, long numberOfCoefficients) {
diff --git a/dwtools/MFCC.cpp b/dwtools/MFCC.cpp
index aa176e7..e53adc7 100644
--- a/dwtools/MFCC.cpp
+++ b/dwtools/MFCC.cpp
@@ -30,6 +30,12 @@
 
 Thing_implement (MFCC, CC, 1);
 
+void structMFCC :: v_info () {
+	structCC :: v_info ();
+	MelderInfo_writeLine (U"Minimum frequency: ", fmin, U" mel");
+	MelderInfo_writeLine (U"Maximum frequency: ", fmax, U" mel");
+}
+
 autoMFCC MFCC_create (double tmin, double tmax, long nt, double dt, double t1, long maximumNumberOfCoefficients, double fmin_mel, double fmax_mel) {
 	try {
 		autoMFCC me = Thing_new (MFCC);
diff --git a/dwtools/MFCC.h b/dwtools/MFCC.h
index 3a7a9b0..f115eba 100644
--- a/dwtools/MFCC.h
+++ b/dwtools/MFCC.h
@@ -31,6 +31,8 @@
 #include "TableOfReal.h"
 
 Thing_define (MFCC, CC) {
+	void v_info ()
+		override;
 };
 
 /*
diff --git a/dwtools/manual_dwtools.cpp b/dwtools/manual_dwtools.cpp
index 1118ba3..1b6cc0c 100644
--- a/dwtools/manual_dwtools.cpp
+++ b/dwtools/manual_dwtools.cpp
@@ -3511,7 +3511,7 @@ SCRIPT (5.4, Manual_SETTINGS_WINDOW_HEIGHT (5), U""
 	Manual_DRAW_SETTINGS_WINDOW ("Sound: Draw where...", 5)
 	Manual_DRAW_SETTINGS_WINDOW_RANGE("Time range (s)", "0.0", "0.0 (= all)")
 	Manual_DRAW_SETTINGS_WINDOW_RANGE ("Vertical range", "0.0", "0.0 (= all)")
-	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN("Garnish", true)
+	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN("Garnish", 1)
 	Manual_DRAW_SETTINGS_WINDOW_OPTIONMENU("Drawing method", "Curve")
 	Manual_DRAW_SETTINGS_WINDOW_TEXT ("Draw only those parts where the following condition holds",
 		"x < xmin + (xmax - xmin) / 2; first half")
@@ -3669,7 +3669,7 @@ SCRIPT (5.4, Manual_SETTINGS_WINDOW_HEIGHT (6), U""
 	Manual_DRAW_SETTINGS_WINDOW_RANGE("Time range (s)", "0.0", "0.0 (= all)")
 	Manual_DRAW_SETTINGS_WINDOW_RANGE ("Vertical range", "0.0", "0.0 (= all)")
 	Manual_DRAW_SETTINGS_WINDOW_FIELD ("Fill from level", "0")
-	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN("Garnish", true)
+	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN("Garnish", 1)
 	Manual_DRAW_SETTINGS_WINDOW_TEXT ("Paint only those parts where the following condition holds",
 		"1; always")
 )
@@ -3736,7 +3736,7 @@ SCRIPT (5.4, Manual_SETTINGS_WINDOW_HEIGHT (4), U""
 	Manual_DRAW_SETTINGS_WINDOW_FIELD ("Colour (0-1, name, {r,g,b})", "0.5")
 	Manual_DRAW_SETTINGS_WINDOW_RANGE("Time range (s)", "0.0", "0.0 (= all)")
 	Manual_DRAW_SETTINGS_WINDOW_RANGE ("Vertical range", "0.0", "0.0 (= all)")
-	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN("Garnish", true)
+	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN("Garnish", 1)
 )
 TAG (U"##Colour")
 DEFINITION (U"defines the @@Colour|colour@ of the paint.")
@@ -4230,7 +4230,7 @@ SCRIPT (6, Manual_SETTINGS_WINDOW_HEIGHT (10), U""
 	Manual_DRAW_SETTINGS_WINDOW_FIELD ("Distance between bars within group", "0.0")
 	Manual_DRAW_SETTINGS_WINDOW_FIELD ("Colours (0-1, name, {r,g,b})", "Grey")
 	Manual_DRAW_SETTINGS_WINDOW_FIELD ("Label text angle (degrees)", "0.0")
-	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN("Garnish", true)
+	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN("Garnish", 1)
 	Manual_DRAW_SETTINGS_WINDOW_TEXT("Formula:", "row>1 and row < 10")
 )
 TAG (U"##Vertical column(s)")
@@ -4320,7 +4320,7 @@ SCRIPT (7, Manual_SETTINGS_WINDOW_HEIGHT (8), U""
 	Manual_DRAW_SETTINGS_WINDOW_RANGE ("Horizontal range", "0.0", "0.0 (= autoscaling)")
 	Manual_DRAW_SETTINGS_WINDOW_FIELD ("Text", "+")
 	Manual_DRAW_SETTINGS_WINDOW_FIELD ("Label text angle (degrees)", "0.0")
-	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN("Garnish", true)
+	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN("Garnish", 1)
 	Manual_DRAW_SETTINGS_WINDOW_TEXT("Formula:", "1; (= everything)")
 )
 TAG (U"##Vertical column")
@@ -4424,7 +4424,7 @@ SCRIPT (6, Manual_SETTINGS_WINDOW_HEIGHT (9), U""
 	Manual_DRAW_SETTINGS_WINDOW_FIELD ("Lower error value column", "")
 	Manual_DRAW_SETTINGS_WINDOW_FIELD ("Upper error value column", "")
 	Manual_DRAW_SETTINGS_WINDOW_FIELD ("Bar size (mm)", "1.0")
-	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN("Garnish", true)
+	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN("Garnish", 1)
 	Manual_DRAW_SETTINGS_WINDOW_TEXT("Formula", "1; (= everything)")
 )
 TAG (U"##Horizontal column")
diff --git a/fon/FunctionEditor.cpp b/fon/FunctionEditor.cpp
index 90df72e..8de3dab 100644
--- a/fon/FunctionEditor.cpp
+++ b/fon/FunctionEditor.cpp
@@ -1,6 +1,6 @@
 /* FunctionEditor.cpp
  *
- * Copyright (C) 1992-2011,2012,2013,2014,2015 Paul Boersma
+ * Copyright (C) 1992-2011,2012,2013,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -1023,11 +1023,22 @@ static void gui_drawingarea_cb_expose (FunctionEditor me, GuiDrawingArea_ExposeE
 static void gui_drawingarea_cb_click (FunctionEditor me, GuiDrawingArea_ClickEvent event) {
 	if (! my d_graphics) return;   // could be the case in the very beginning
 	my shiftKeyPressed = event -> shiftKeyPressed;
-	Graphics_setWindow (my d_graphics.get(), my functionViewerLeft, my functionViewerRight, 0.0, my height);
+	Graphics_setWindow (my d_graphics.get(), my functionViewerLeft, my selectionViewerRight, 0.0, my height);
 	double xWC, yWC;
 	Graphics_DCtoWC (my d_graphics.get(), event -> x, event -> y, & xWC, & yWC);
 
-	if (yWC > BOTTOM_MARGIN + space * 3 && yWC < my height - (TOP_MARGIN + space)) {   // in signal region?
+	if (xWC > my selectionViewerLeft)
+	{
+		Graphics_setViewport (my d_graphics.get(), my selectionViewerLeft + MARGIN, my selectionViewerRight - MARGIN,
+			BOTTOM_MARGIN + space * 3, my height - (TOP_MARGIN + space));
+		Graphics_setWindow (my d_graphics.get(), 0.0, 1.0, 0.0, 1.0);
+		Graphics_DCtoWC (my d_graphics.get(), event -> x, event -> y, & xWC, & yWC);
+		my v_clickSelectionViewer (xWC, yWC);
+		//my v_updateText ();
+		drawNow (me);
+		updateGroup (me);
+	}
+	else if (yWC > BOTTOM_MARGIN + space * 3 && yWC < my height - (TOP_MARGIN + space)) {   // in signal region?
 		int needsUpdate;
 		Graphics_setViewport (my d_graphics.get(), my functionViewerLeft + MARGIN, my functionViewerRight - MARGIN,
 			BOTTOM_MARGIN + space * 3, my height - (TOP_MARGIN + space));
@@ -1399,6 +1410,9 @@ bool structFunctionEditor :: v_clickE (double xWC, double /* yWC */) {
 	return FunctionEditor_UPDATE_NEEDED;
 }
 
+void structFunctionEditor :: v_clickSelectionViewer (double /* xWC */, double /* yWC */) {
+}
+
 void FunctionEditor_insetViewport (FunctionEditor me) {
 	Graphics_setViewport (my d_graphics.get(), my functionViewerLeft + MARGIN, my functionViewerRight - MARGIN,
 		BOTTOM_MARGIN + space * 3, my height - (TOP_MARGIN + space));
diff --git a/fon/FunctionEditor.h b/fon/FunctionEditor.h
index 06b8001..4ae9f49 100644
--- a/fon/FunctionEditor.h
+++ b/fon/FunctionEditor.h
@@ -2,7 +2,7 @@
 #define _FunctionEditor_h_
 /* FunctionEditor.h
  *
- * Copyright (C) 1992-2011,2012,2013,2014,2015 Paul Boersma
+ * Copyright (C) 1992-2011,2012,2013,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -110,6 +110,7 @@ Thing_define (FunctionEditor, Editor) {
 		 * Behaviour of FunctionEditor::click ():
 		 *    moves the cursor to 'xWC', drags to create a selection, or extends the selection.
 		 */
+	virtual void v_clickSelectionViewer (double xWC, double yWC);
 	virtual bool v_clickB (double xWC, double yWC);
 	virtual bool v_clickE (double xWC, double yWC);
 	virtual int v_playCallback (int phase, double tmin, double tmax, double t);
diff --git a/fon/RunnerMFC.cpp b/fon/RunnerMFC.cpp
index f780e16..31775f6 100644
--- a/fon/RunnerMFC.cpp
+++ b/fon/RunnerMFC.cpp
@@ -1,6 +1,6 @@
 /* RunnerMFC.cpp
  *
- * Copyright (C) 2001-2011,2013,2015,2016 Paul Boersma
+ * Copyright (C) 2001-2011,2013,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -60,8 +60,7 @@ static void drawControlButton (RunnerMFC me, double left, double right, double b
 	Graphics_text (my graphics.get(), 0.5 * (left + right), 0.5 * (bottom + top), visibleText);
 }
 
-static void gui_drawingarea_cb_expose (RunnerMFC me, GuiDrawingArea_ExposeEvent event) {
-	Melder_assert (event -> widget == my d_drawingArea);
+static void drawNow (RunnerMFC me) {
 	if (! my graphics) return;   // could be the case in the very beginning
 	ExperimentMFC experiment = (ExperimentMFC) my data;
 	long iresponse;
@@ -69,6 +68,7 @@ static void gui_drawingarea_cb_expose (RunnerMFC me, GuiDrawingArea_ExposeEvent
 	Graphics_setGrey (my graphics.get(), 0.8);
 	Graphics_fillRectangle (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
 	Graphics_setGrey (my graphics.get(), 0.0);
+	if (my blanked) return;
 	if (experiment -> trial == 0) {
 		Graphics_setTextAlignment (my graphics.get(), Graphics_CENTRE, Graphics_HALF);
 		Graphics_setFontSize (my graphics.get(), 24);
@@ -176,6 +176,11 @@ static void gui_drawingarea_cb_expose (RunnerMFC me, GuiDrawingArea_ExposeEvent
 	}
 }
 
+static void gui_drawingarea_cb_expose (RunnerMFC me, GuiDrawingArea_ExposeEvent event) {
+	Melder_assert (event -> widget == my d_drawingArea);
+	drawNow (me);
+}
+
 static void gui_drawingarea_cb_resize (RunnerMFC me, GuiDrawingArea_ResizeEvent event) {
 	if (! my graphics) return;
 	Graphics_setWsViewport (my graphics.get(), 0.0, event -> width, 0.0, event -> height);
@@ -200,18 +205,18 @@ static void do_ok (RunnerMFC me) {
 		experiment -> trial ++;
 		Editor_broadcastDataChanged (me);
 		if (experiment -> blankWhilePlaying) {
-			Graphics_setGrey (my graphics.get(), 0.8);
-			Graphics_fillRectangle (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
-			Graphics_setGrey (my graphics.get(), 0.0);
+			my blanked = true;
+			drawNow (me);
 			Graphics_flushWs (my graphics.get());
 		}
-		Graphics_updateWs (my graphics.get());
 		if (experiment -> stimuliAreSounds) {
 			autoMelderAudioSaveMaximumAsynchronicity saveMaximumAsynchronicity;
 			if (experiment -> blankWhilePlaying)
 				 MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel_SYNCHRONOUS);
 			ExperimentMFC_playStimulus (experiment, experiment -> stimuli [experiment -> trial]);
 		}
+		my blanked = false;
+		Graphics_updateWs (my graphics.get());
 	}
 }
 
@@ -229,18 +234,18 @@ static void do_oops (RunnerMFC me) {
 	my numberOfReplays = 0;
 	Editor_broadcastDataChanged (me);
 	if (experiment -> blankWhilePlaying) {
-		Graphics_setGrey (my graphics.get(), 0.8);
-		Graphics_fillRectangle (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
-		Graphics_setGrey (my graphics.get(), 0.0);
+		my blanked = true;
+		drawNow (me);
 		Graphics_flushWs (my graphics.get());
 	}
-	Graphics_updateWs (my graphics.get());
 	if (experiment -> stimuliAreSounds) {
 		autoMelderAudioSaveMaximumAsynchronicity saveMaximumAsynchronicity;
 		if (experiment -> blankWhilePlaying)
 			MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel_SYNCHRONOUS);
 		ExperimentMFC_playStimulus (experiment, experiment -> stimuli [experiment -> trial]);
 	}
+	my blanked = false;
+	Graphics_updateWs (my graphics.get());
 }
 
 static void do_replay (RunnerMFC me) {
@@ -249,18 +254,18 @@ static void do_replay (RunnerMFC me) {
 	my numberOfReplays ++;
 	Editor_broadcastDataChanged (me);
 	if (experiment -> blankWhilePlaying) {
-		Graphics_setGrey (my graphics.get(), 0.8);
-		Graphics_fillRectangle (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
-		Graphics_setGrey (my graphics.get(), 0.0);
+		my blanked = true;
+		drawNow (me);
 		Graphics_flushWs (my graphics.get());
 	}
-	Graphics_updateWs (my graphics.get());
 	if (experiment -> stimuliAreSounds) {
 		autoMelderAudioSaveMaximumAsynchronicity saveMaximumAsynchronicity;
 		if (experiment -> blankWhilePlaying)
 			MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel_SYNCHRONOUS);
 		ExperimentMFC_playStimulus (experiment, experiment -> stimuli [experiment -> trial]);
 	}
+	my blanked = false;
+	Graphics_updateWs (my graphics.get());
 }
 
 static void gui_drawingarea_cb_click (RunnerMFC me, GuiDrawingArea_ClickEvent event) {
@@ -276,12 +281,10 @@ static void gui_drawingarea_cb_click (RunnerMFC me, GuiDrawingArea_ClickEvent ev
 		experiment -> trial ++;
 		Editor_broadcastDataChanged (me);
 		if (experiment -> blankWhilePlaying) {
-			Graphics_setGrey (my graphics.get(), 0.8);
-			Graphics_fillRectangle (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
-			Graphics_setGrey (my graphics.get(), 0.0);
+			my blanked = true;
+			drawNow (me);
 			Graphics_flushWs (my graphics.get());
 		}
-		Graphics_updateWs (my graphics.get());
 		if (experiment -> stimuliAreSounds) {
 			if (experiment -> numberOfTrials < 1) {
 				Melder_flushError (U"There are zero trials in this experiment.");
@@ -293,6 +296,8 @@ static void gui_drawingarea_cb_click (RunnerMFC me, GuiDrawingArea_ClickEvent ev
 				MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel_SYNCHRONOUS);
 			ExperimentMFC_playStimulus (experiment, experiment -> stimuli [1]);   // works only if there is at least one trial
 		}
+		my blanked = false;
+		Graphics_updateWs (my graphics.get());
 	} else if (experiment -> pausing) {   // a click to leave the break
 		if (x > experiment -> oops_left && x < experiment -> oops_right &&
 			y > experiment -> oops_bottom && y < experiment -> oops_top && experiment -> trial > 1)
@@ -303,18 +308,18 @@ static void gui_drawingarea_cb_click (RunnerMFC me, GuiDrawingArea_ClickEvent ev
 			experiment -> trial ++;
 			Editor_broadcastDataChanged (me);
 			if (experiment -> blankWhilePlaying) {
-				Graphics_setGrey (my graphics.get(), 0.8);
-				Graphics_fillRectangle (my graphics.get(), 0.0, 1.0, 0.0, 1.0);
-				Graphics_setGrey (my graphics.get(), 0.0);
+				my blanked = true;
+				drawNow (me);
 				Graphics_flushWs (my graphics.get());
 			}
-			Graphics_updateWs (my graphics.get());
 			if (experiment -> stimuliAreSounds) {
 				autoMelderAudioSaveMaximumAsynchronicity saveMaximumAsynchronicity;
 				if (experiment -> blankWhilePlaying)
 					MelderAudio_setOutputMaximumAsynchronicity (kMelder_asynchronicityLevel_SYNCHRONOUS);
 				ExperimentMFC_playStimulus (experiment, experiment -> stimuli [experiment -> trial]);
 			}
+			my blanked = false;
+			Graphics_updateWs (my graphics.get());
 		}
 	} else if (experiment -> trial <= experiment -> numberOfTrials) {
 		if (x > experiment -> ok_left && x < experiment -> ok_right &&
diff --git a/fon/RunnerMFC.h b/fon/RunnerMFC.h
index 67ed313..acb9b33 100644
--- a/fon/RunnerMFC.h
+++ b/fon/RunnerMFC.h
@@ -2,7 +2,7 @@
 #define _RunnerMFC_h_
 /* RunnerMFC.h
  *
- * Copyright (C) 2001-2011,2012,2015 Paul Boersma
+ * Copyright (C) 2001-2011,2012,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -27,6 +27,7 @@ Thing_define (RunnerMFC, Editor) {
 	long iexperiment;
 	autoGraphics graphics;
 	long numberOfReplays;
+	bool blanked;
 
 	void v_destroy () noexcept
 		override;
diff --git a/fon/SoundRecorder.cpp b/fon/SoundRecorder.cpp
index fca57cb..b66b493 100644
--- a/fon/SoundRecorder.cpp
+++ b/fon/SoundRecorder.cpp
@@ -644,7 +644,7 @@ static void gui_button_cb_ok (SoundRecorder me, GuiButtonEvent /* event */) {
 static void initialize (SoundRecorder me) {
 	try {
 		if (my inputUsesPortAudio) {
-			#if defined (macintosh)
+			#if defined (macintoshXXX)
 				my fsamp_ [SoundRecorder_IFSAMP_8000]. canDo = false;
 				my fsamp_ [SoundRecorder_IFSAMP_11025]. canDo = false;
 				my fsamp_ [SoundRecorder_IFSAMP_12000]. canDo = false;
diff --git a/fon/TextGridEditor.cpp b/fon/TextGridEditor.cpp
index 5d2a3bf..ac63cc1 100644
--- a/fon/TextGridEditor.cpp
+++ b/fon/TextGridEditor.cpp
@@ -1636,6 +1636,39 @@ void structTextGridEditor :: v_draw () {
 	v_updateMenuItems_file ();
 }
 
+static const char32 *characters [12] [10] = {
+	{ U"ɑ", U"ɐ", U"ɒ", U"æ", U"ʙ", U"ɓ", U"β", U"ç", U"ɕ", U"ð" },
+	{ U"ɗ", U"ɖ", U"ɛ", U"ɜ", U"ə", U"ɢ", U"ɠ", U"ʛ", U"ɣ", U"ɤ" },
+	{ U"ˠ", U"ʜ", U"ɦ", U"ħ", U"ʰ", U"ɧ", U"ɪ", U"ɨ", U"ı", U"ɟ" },
+	{ U"ʝ", U"ʄ", U"ᴊ", U"ʲ", U"ʟ", U"ɬ", U"ɭ", U"ɮ", U"ɫ", U"ˡ" },
+	{ U"ɰ", U"ɯ", U"ɱ", U"ɴ", U"ŋ", U"ɲ", U"ɳ", U"ⁿ", U"ɔ", U"ɵ" },
+
+	{ U"ø", U"œ", U"ɶ", U"ɸ", U"ɹ", U"ʀ", U"ʁ", U"ɾ", U"ɽ", U"ɺ" },
+	{ U"ɻ", U"ʃ", U"ʂ", U"θ", U"ʈ", U"ʉ", U"ʊ", U"ʌ", U"ʋ", U"ʍ" },
+	{ U"ʷ", U"χ", U"ʎ", U"ʏ", U"ɥ", U"ʒ", U"ʐ", U"ʑ", U"ˑ", U"ː" },
+	{ U"ʔ", U"ʡ", U"ʕ", U"ʢ", U"ˤ", U"ǃ", U"ʘ", U"ǀ", U"ǁ", U"ǂ" },
+	{ U"ʤ", U"ɘ", U"ɚ", U"ɝ", U"ʱ", U"ˢ", U"ʧ", U"ɞ", U"ʦ", U"ʣ" },
+
+	{ U"ʨ", U"ʥ", U"z̊", U"z̥", U"z̰", U"z̪", U"z̻", U"z̩", U"z̝", U"z̞" },
+	{ U"ý", U"ȳ", U"ỳ", U"ÿ", U"ỹ", U"o̟", U"o̱", U"o̹", U"o̜", U"t̚" },
+};
+
+void structTextGridEditor :: v_drawSelectionViewer () {
+	TextGrid grid = (TextGrid) our data;
+	Graphics_setWindow (our d_graphics.get(), 0.5, 10.5, 0.5, 12.5);
+	Graphics_setColour (our d_graphics.get(), Graphics_WHITE);
+	Graphics_fillRectangle (our d_graphics.get(), 0.5, 10.5, 0.5, 12.5);
+	Graphics_setColour (our d_graphics.get(), Graphics_BLACK);
+	Graphics_setFont (our d_graphics.get(), kGraphics_font_TIMES);
+	Graphics_setFontSize (our d_graphics.get(), 12);
+	Graphics_setTextAlignment (our d_graphics.get(), Graphics_CENTRE, Graphics_HALF);
+	for (int irow = 1; irow <= 12; irow ++) {
+		for (int icol = 1; icol <= 10; icol ++) {
+			Graphics_text (our d_graphics.get(), icol, 13-irow, characters [irow-1] [icol-1]);
+		}
+	}
+}
+
 static void do_drawWhileDragging (TextGridEditor me, double numberOfTiers, bool selectedTier [], double x, double soundY) {
 	long itier;
 	for (itier = 1; itier <= numberOfTiers; itier ++) if (selectedTier [itier]) {
@@ -2020,6 +2053,64 @@ bool structTextGridEditor :: v_clickE (double t, double yWC) {
 	return FunctionEditor_UPDATE_NEEDED;
 }
 
+void structTextGridEditor :: v_clickSelectionViewer (double xWC, double yWC) {
+	TextGrid grid = (TextGrid) our data;
+	int rowNumber = 1 + (int) ((1.0-yWC) * 12.0);
+	int columnNumber = 1 + (int) (xWC * 10.0);
+	if (rowNumber < 1 || rowNumber > 12) return;
+	if (columnNumber < 1 || columnNumber > 10) return;
+	const char32 *character = characters [rowNumber-1] [columnNumber-1];
+	character += str32len (character) - 1;
+	if (our text) {
+		long first = 0, last = 0;
+		char32 *oldText = GuiText_getStringAndSelectionPosition (our text, & first, & last);
+		static MelderString newText { 0 };
+		MelderString_empty (& newText);
+		MelderString_ncopy (& newText, oldText, first);
+		MelderString_append (& newText, character);
+		MelderString_append (& newText, oldText + last);
+		Melder_free (oldText);
+		if (our selectedTier) {
+			IntervalTier intervalTier;
+			TextTier textTier;
+			_AnyTier_identifyClass (grid -> tiers->at [our selectedTier], & intervalTier, & textTier);
+			if (intervalTier) {
+				long selectedInterval = getSelectedInterval (this);
+				if (selectedInterval) {
+					TextInterval interval = intervalTier -> intervals.at [selectedInterval];
+					TextInterval_setText (interval, newText.string);
+
+					our suppressRedraw = true;   // prevent valueChangedCallback from redrawing
+					trace (U"setting new text ", newText.string);
+					GuiText_setString (text, newText.string);
+					GuiText_setSelection (text, first + 1, first + 1);
+					our suppressRedraw = false;
+
+					FunctionEditor_redraw (this);
+					Editor_broadcastDataChanged (this);
+				}
+			} else {
+				long selectedPoint = getSelectedPoint (this);
+				if (selectedPoint) {
+					TextPoint point = textTier -> points.at [selectedPoint];
+					Melder_free (point -> mark);
+					if (str32spn (newText.string, U" \n\t") != str32len (newText.string))   // any visible characters?
+					point -> mark = Melder_dup_f (newText.string);
+
+					our suppressRedraw = true;   // prevent valueChangedCallback from redrawing
+					trace (U"setting new text ", newText.string);
+					GuiText_setString (text, newText.string);
+					GuiText_setSelection (text, first + 1, first + 1);
+					our suppressRedraw = false;
+
+					FunctionEditor_redraw (this);
+					Editor_broadcastDataChanged (this);
+				}
+			}
+		}
+	}
+}
+
 void structTextGridEditor :: v_play (double tmin, double tmax) {
 	if (our d_longSound.data) {
 		LongSound_playPart (our d_longSound.data, tmin, tmax, theFunctionEditor_playCallback, this);
diff --git a/fon/TextGridEditor.h b/fon/TextGridEditor.h
index 6231a8e..dc41fdf 100644
--- a/fon/TextGridEditor.h
+++ b/fon/TextGridEditor.h
@@ -2,7 +2,7 @@
 #define _TextGridEditor_h_
 /* TextGridEditor.h
  *
- * Copyright (C) 1992-2011,2012,2014,2015 Paul Boersma
+ * Copyright (C) 1992-2011,2012,2014,2015,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -52,6 +52,8 @@ Thing_define (TextGridEditor, TimeSoundAnalysisEditor) {
 		override;
 	void v_draw ()
 		override;
+	void v_drawSelectionViewer ()
+		override;
 	bool v_hasText ()
 		override { return true; }
 	bool v_click (double xWC, double yWC, bool shiftKeyPressed)
@@ -60,6 +62,8 @@ Thing_define (TextGridEditor, TimeSoundAnalysisEditor) {
 		override;
 	bool v_clickE (double xWC, double yWC)
 		override;
+	void v_clickSelectionViewer (double xWC, double yWC)
+		override;
 	void v_play (double tmin, double tmax)
 		override;
 	void v_updateText ()
diff --git a/fon/manual_Script.cpp b/fon/manual_Script.cpp
index 8fccbe1..c1a9349 100644
--- a/fon/manual_Script.cpp
+++ b/fon/manual_Script.cpp
@@ -1499,7 +1499,7 @@ SCRIPT (5.4, Manual_SETTINGS_WINDOW_HEIGHT (4), U""
 	Manual_DRAW_SETTINGS_WINDOW ("Sound: Draw", 4)
 	Manual_DRAW_SETTINGS_WINDOW_RANGE ("Time range (s)", "0.0", "0.0 (= all)")
 	Manual_DRAW_SETTINGS_WINDOW_RANGE ("Vertical range", "0.0", "0.0 (= auto)")
-	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN ("Garnish", true)
+	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN ("Garnish", 1)
 	Manual_DRAW_SETTINGS_WINDOW_OPTIONMENU ("Drawing method", "Curve")
 )
 NORMAL (U"In this example, all the settings have their standard values: you want to draw the whole time domain of the Sound, "
@@ -1516,7 +1516,7 @@ SCRIPT (5.4, Manual_SETTINGS_WINDOW_HEIGHT (4), U""
 	Manual_DRAW_SETTINGS_WINDOW ("Sound: Draw", 4)
 	Manual_DRAW_SETTINGS_WINDOW_RANGE ("Time range (s)", "1.0", "3.2")
 	Manual_DRAW_SETTINGS_WINDOW_RANGE ("Vertical range", "-1", "1")
-	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN ("Garnish", false)
+	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN ("Garnish", 0)
 	Manual_DRAW_SETTINGS_WINDOW_OPTIONMENU ("Drawing method", "Poles")
 )
 NORMAL (U"In a script this would look like")
@@ -1536,7 +1536,7 @@ SCRIPT (5.4, Manual_SETTINGS_WINDOW_HEIGHT (6.1), U""   // 7 - 3 * 0.3 (three is
 	Manual_DRAW_SETTINGS_WINDOW ("Sound: Draw", 6.1)
 	Manual_DRAW_SETTINGS_WINDOW_RANGE ("Time range (s)", "1.0", "3.2")
 	Manual_DRAW_SETTINGS_WINDOW_RANGE ("Vertical range", "-1", "1")
-	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN ("Garnish", false)
+	Manual_DRAW_SETTINGS_WINDOW_BOOLEAN ("Garnish", 0)
 	Manual_DRAW_SETTINGS_WINDOW_RADIO ("Drawing method", "Curve", 0)
 	"y -= 12\n"
 	Manual_DRAW_SETTINGS_WINDOW_RADIO ("", "Bars", 0)
diff --git a/fon/manual_tutorials.cpp b/fon/manual_tutorials.cpp
index 178462e..d7f874e 100644
--- a/fon/manual_tutorials.cpp
+++ b/fon/manual_tutorials.cpp
@@ -1,6 +1,6 @@
 /* manual_tutorials.cpp
  *
- * Copyright (C) 1992-2012,2013,2014,2015,2016 Paul Boersma
+ * Copyright (C) 1992-2012,2013,2014,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -22,10 +22,23 @@
 void manual_tutorials_init (ManPages me);
 void manual_tutorials_init (ManPages me) {
 
-MAN_BEGIN (U"What's new?", U"ppgb", 20161115)
+MAN_BEGIN (U"What's new?", U"ppgb", 20170302)
 INTRO (U"Latest changes in Praat.")
 //LIST_ITEM (U"• Manual page about @@drawing a vowel triangle at .")
 
+NORMAL (U"##6.0.26# (2 March 2017)")
+LIST_ITEM (U"• Mac: more corrections in $$demoShow()$ and $$blankWhilePlaying$.")
+LIST_ITEM (U"• PraatBarren: better error message when an attempt is made to run PraatBarren interactively.")
+NORMAL (U"##6.0.25# (11 February 2017)")
+LIST_ITEM (U"• Mac: made $$demoShow()$ and $$blankWhilePlaying$ compatible with MacOS 10.12 Sierra.")
+LIST_ITEM (U"• Mac SoundRecorder: more sampling frequencies, on behalf of external USB microphones.")
+NORMAL (U"##6.0.24# (23 January 2017)")
+LIST_ITEM (U"• Fixed a bug by which ##Remove right boundary# would choose the wrong tier.")
+LIST_ITEM (U"• TextGrid window: click to insert a phonetic symbol from an IPA chart.")
+NORMAL (U"##6.0.23# (12 December 2016)")
+LIST_ITEM (U"• Linux: fixed a bug that caused Praat to crash when playing a sound of more than 7 channels.")
+LIST_ITEM (U"• Change Gender: fixed a bug introduced in 6.0.22 by which the pitch range factor could not be 0.")
+LIST_ITEM (U"• Improvements in the manual and in texts.")
 NORMAL (U"##6.0.22# (15 November 2016)")
 LIST_ITEM (U"• Scripting: correct error messages for expressions like: 5 + \"hello\"")
 LIST_ITEM (U"• Command line: the --open option works correctly in the GUI if you open multiple files.")
@@ -1823,7 +1836,7 @@ ENTRY (U"Known bugs in the Windows version")
 	LIST_ITEM (U"• Cannot stand infinitesimal zooming in SpectrogramEditor.")
 */
  
-MAN_BEGIN (U"Acknowledgments", U"ppgb", 20151103)
+MAN_BEGIN (U"Acknowledgments", U"ppgb", 20161227)
 NORMAL (U"The following people contributed source code to Praat:")
 LIST_ITEM (U"Paul Boersma: user interface, graphics, @printing, @@Intro|sound@, "
 	"@@Intro 3. Spectral analysis|spectral analysis@, @@Intro 4. Pitch analysis|pitch analysis@, "
@@ -1844,16 +1857,18 @@ LIST_ITEM (U"Rafael Laboissière: adaptation of XIPA, audio bug fixes for Linux.
 LIST_ITEM (U"Darryl Purnell created the first version of audio for Praat for Linux.")
 NORMAL (U"We included the following freely available software libraries in Praat (sometimes with adaptations):")
 LIST_ITEM (U"XIPA: IPA font for Unix by Fukui Rei (GPL).")
-LIST_ITEM (U"GSL: GNU Scientific Library by Gerard Jungman and Brian Gough (GPL).")
-LIST_ITEM (U"GLPK: GNU Linear Programming Kit by Andrew Makhorin (GPL).")
-LIST_ITEM (U"PortAudio: Portable Audio Library by Ross Bencina, Phil Burk, Bjorn Roche, Dominic Mazzoni, Darren Gibbs.")
-LIST_ITEM (U"Espeak: text-to-speech synthesizer by Jonathan Duddington (GPL).")
-LIST_ITEM (U"MAD: MPEG Audio Decoder by Underbit Technologies (GPL).")
-LIST_ITEM (U"FLAC: Free Lossless Audio Codec by Josh Coalson.")
+LIST_ITEM (U"GSL: GNU Scientific Library by Gerard Jungman and Brian Gough (GPL 3 or later).")
+LIST_ITEM (U"GLPK: GNU Linear Programming Kit by Andrew Makhorin (GPL 3 or later); "
+	"contains AMD software by the same author (LGPL 2.1 or later).")
+LIST_ITEM (U"PortAudio: Portable Audio Library by Ross Bencina, Phil Burk, Bjorn Roche, Dominic Mazzoni, Darren Gibbs "
+	"(CC-BY-like license).")
+LIST_ITEM (U"Espeak: text-to-speech synthesizer by Jonathan Duddington (GPL 3 or later).")
+LIST_ITEM (U"MAD: MPEG Audio Decoder by Underbit Technologies (GPL 2 or later).")
+LIST_ITEM (U"FLAC: Free Lossless Audio Codec by Josh Coalson (BSD 3-clause license).")
 LIST_ITEM (U"fftpack: public domain Fourier transforms by Paul Swarztrauber and Christopher Montgomery.")
 LIST_ITEM (U"LAPACK: public domain numeric algorithms by Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd., "
 	"Courant Institute, Argonne National Lab, and Rice University.")
-LIST_ITEM (U"Regular expressions by Henry Spencer, Mark Edel, Christopher Conrad, Eddy De Greef (GPL).")
+LIST_ITEM (U"Regular expressions by Henry Spencer, Mark Edel, Christopher Conrad, Eddy De Greef (GPL 2 or later).")
 NORMAL (U"For their financial support during the development of Praat:")
 LIST_ITEM (U"Netherlands Organization for Scientific Research (NWO) (1996–1999).")
 LIST_ITEM (U"Nederlandse Taalunie (2006–2008).")
@@ -1882,8 +1897,9 @@ LIST_ITEM (U"José Joaquín Atria and Ingmar Steiner, for setting up the source-
 LIST_ITEM (U"Hundreds of Praat users, for sending suggestions and notifying us of problems and thus helping us to improve Praat.")
 MAN_END
 
-MAN_BEGIN (U"Praat menu", U"ppgb", 20050822)
-INTRO (U"The first menu in the @@Object window at . On MacOS X, this menu is in the main menu bar.")
+MAN_BEGIN (U"Praat menu", U"ppgb", 20161227)
+INTRO (U"The first menu in the @@Object window at . "
+	"In macOS, this menu is in the main menu bar along the top of the screen.")
 MAN_END
 
 MAN_BEGIN (U"Copy...", U"ppgb", 20111018)
diff --git a/fon/praat_TextGrid_init.cpp b/fon/praat_TextGrid_init.cpp
index 66d56a1..a59f1b9 100644
--- a/fon/praat_TextGrid_init.cpp
+++ b/fon/praat_TextGrid_init.cpp
@@ -1230,12 +1230,12 @@ DO
 			Melder_throw (U"You cannot remove a boundary from tier ", tierNumber, U" of ", me,
 				U", because that tier is a point tier instead of an interval tier.");
 		if (tierNumber > intervalTier -> intervals.size)
-			Melder_throw (U"You cannot remove a boundary from interval ", tierNumber, U" of tier ", tierNumber, U" of ", me,
+			Melder_throw (U"You cannot remove a boundary from interval ", intervalNumber, U" of tier ", tierNumber, U" of ", me,
 				U", because that tier has only ", intervalTier -> intervals.size, U" intervals.");
 		if (tierNumber == intervalTier -> intervals.size)
-			Melder_throw (U"You cannot remove the right boundary from interval ", tierNumber, U" of tier ", tierNumber, U" of ", me,
+			Melder_throw (U"You cannot remove the right boundary from interval ", intervalNumber, U" of tier ", tierNumber, U" of ", me,
 				U", because this is at the right edge of the tier.");
-		IntervalTier_removeLeftBoundary (intervalTier, tierNumber + 1);
+		IntervalTier_removeLeftBoundary (intervalTier, intervalNumber + 1);
 	MODIFY_EACH_END
 }
 
diff --git a/kar/longchar.cpp b/kar/longchar.cpp
index f3bedcc..b227a0f 100644
--- a/kar/longchar.cpp
+++ b/kar/longchar.cpp
@@ -1,6 +1,6 @@
 /* longchar.cpp
  *
- * Copyright (C) 1992-2011,2015,2016 Paul Boersma
+ * Copyright (C) 1992-2011,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -618,7 +618,7 @@ static struct { char first, second; } genericDigraph [1+UNICODE_TOP_GENERICIZABL
 
 static void init () {
 	Longchar_Info data;
-	int i;
+	short i;
 	for (i = 0, data = & Longchar_database [0]; data -> first != '\0'; i ++, data ++) {
 		short *location = & where [data -> first - 32] [data -> second - 32];
 		if (*location) {
@@ -652,7 +652,7 @@ char32_t * Longchar_nativize32 (const char32_t *generic, char32_t *native, int e
 				continue;
 			}
 		}
-		if (kar == '\\' && (kar1 = generic [0]) >= 32 && kar1 <= 126 && (kar2 = generic [1]) >= 32 && kar2 <= 126) {
+		if (kar == U'\\' && (kar1 = generic [0]) >= 32 && kar1 <= 126 && (kar2 = generic [1]) >= 32 && kar2 <= 126) {
 			long location = where [kar1 - 32] [kar2 - 32];
 			if (location == 0) {
 				*native++ = kar;
@@ -687,7 +687,7 @@ char32_t *Longchar_genericize32 (const char32_t *native, char32_t *g) {
 	return g;
 }
 
-Longchar_Info Longchar_getInfo (char kar1, char kar2) {
+Longchar_Info Longchar_getInfo (char32_t kar1, char32_t kar2) {
 	if (! inited) init ();
 	short position = kar1 < 32 || kar1 > 126 || kar2 < 32 || kar2 > 126 ?
 		0 :   /* Return the 'space' character. */
@@ -697,7 +697,7 @@ Longchar_Info Longchar_getInfo (char kar1, char kar2) {
 
 Longchar_Info Longchar_getInfoFromNative (char32_t kar) {
 	if (! inited) init ();
-	return kar > UNICODE_TOP_GENERICIZABLE ? Longchar_getInfo (' ', ' ') : Longchar_getInfo (genericDigraph [kar]. first, genericDigraph [kar]. second);
+	return kar > UNICODE_TOP_GENERICIZABLE ? Longchar_getInfo (U' ', U' ') : Longchar_getInfo (genericDigraph [kar]. first, genericDigraph [kar]. second);
 }
 
 /* End of file longchar.cpp */
diff --git a/kar/longchar.h b/kar/longchar.h
index 8f9d8f7..b3290d1 100644
--- a/kar/longchar.h
+++ b/kar/longchar.h
@@ -2,7 +2,7 @@
 #define _longchar_h_
 /* longchar.h
  *
- * Copyright (C) 1992-2011,2015,2016 Paul Boersma
+ * Copyright (C) 1992-2011,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -83,7 +83,7 @@ typedef struct structLongchar_Info {
 }
 	*Longchar_Info;
 
-Longchar_Info Longchar_getInfo (char kar1, char kar2);
+Longchar_Info Longchar_getInfo (char32_t kar1, char32_t kar2);
 Longchar_Info Longchar_getInfoFromNative (char32_t kar);
 /* If no info found, these two routines return the info for a space. */
 
diff --git a/makefiles/makefile.defs.mingw32 b/makefiles/makefile.defs.mingw32
index 44b08b6..85780a2 100644
--- a/makefiles/makefile.defs.mingw32
+++ b/makefiles/makefile.defs.mingw32
@@ -1,7 +1,7 @@
 # File: makefile.defs.mingw32
 
 # System: MinGW on Windows under Cygwin
-# Paul Boersma, 8 January 2016
+# Paul Boersma, 12 February 2017
 
 PREFIX = i686-w64-mingw32-
 
@@ -18,7 +18,7 @@ LINK = $(PREFIX)g++
 
 EXECUTABLE = Praat.exe
 
-LIBS = -lwinmm -lwsock32 -lcomctl32 -lole32 -lgdi32 -lgdiplus -lcomdlg32 -static-libgcc -static-libstdc++ -mwindows
+LIBS = -lwinmm -lwsock32 -lcomctl32 -lole32 -lgdi32 -lgdiplus -lcomdlg32 -static-libgcc -static-libstdc++ -mwindows -static
 
 AR = $(PREFIX)ar
 RANLIB = $(PREFIX)ranlib
diff --git a/makefiles/makefile.defs.mingw64 b/makefiles/makefile.defs.mingw64
index 8d269a1..c66c8ae 100644
--- a/makefiles/makefile.defs.mingw64
+++ b/makefiles/makefile.defs.mingw64
@@ -1,7 +1,7 @@
 # File: makefile.defs.mingw64
 
 # System: MinGW on Windows under Cygwin
-# Paul Boersma, 8 January 2016
+# Paul Boersma, 12 February 2017
 
 PREFIX = x86_64-w64-mingw32-
 
@@ -18,7 +18,7 @@ LINK = $(PREFIX)g++
 
 EXECUTABLE = Praat.exe
 
-LIBS = -lwinmm -lwsock32 -lcomctl32 -lole32 -lgdi32 -lgdiplus -lcomdlg32 -static-libgcc -static-libstdc++ -mwindows
+LIBS = -lwinmm -lwsock32 -lcomctl32 -lole32 -lgdi32 -lgdiplus -lcomdlg32 -static-libgcc -static-libstdc++ -mwindows -static
 
 AR = $(PREFIX)ar
 RANLIB = $(PREFIX)ranlib
diff --git a/sys/DemoEditor.cpp b/sys/DemoEditor.cpp
index b9b88a6..1e260f6 100644
--- a/sys/DemoEditor.cpp
+++ b/sys/DemoEditor.cpp
@@ -180,9 +180,6 @@ int Demo_show () {
 	if (! theReferenceToTheOnlyDemoEditor) return 0;
 	autoDemoOpen demo;
 	GuiThing_show (theReferenceToTheOnlyDemoEditor -> d_windowForm);
-	#if defined (macintosh)
-		Graphics_updateWs (theReferenceToTheOnlyDemoEditor -> graphics.get());
-	#endif
 	GuiShell_drain (theReferenceToTheOnlyDemoEditor -> d_windowForm);
 	return 1;
 }
@@ -234,14 +231,6 @@ void Demo_waitForInput (Interpreter interpreter) {
 				} while (! theReferenceToTheOnlyDemoEditor -> clicked &&
 				         ! theReferenceToTheOnlyDemoEditor -> keyPressed &&
 						 ! theReferenceToTheOnlyDemoEditor -> userWantsToClose);
-			#elif defined (macintosh)
-				do {
-					XEvent event;
-					GuiNextEvent (& event);
-					XtDispatchEvent (& event);
-				} while (! theReferenceToTheOnlyDemoEditor -> clicked &&
-				         ! theReferenceToTheOnlyDemoEditor -> keyPressed &&
-						 ! theReferenceToTheOnlyDemoEditor -> userWantsToClose);
 			#endif
 		} catch (MelderError) {
 			Melder_flushError (U"An error made it to the outer level in the Demo window; should not occur! Please write to paul.boersma at uva.nl");
diff --git a/sys/Formula.cpp b/sys/Formula.cpp
index 53b32e0..f4f953f 100644
--- a/sys/Formula.cpp
+++ b/sys/Formula.cpp
@@ -133,7 +133,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_, RUN_SYSTEM_, RUN_SYSTEM_NOCHECK_,
+		PAUSE_SCRIPT_, EXIT_SCRIPT_, RUN_SCRIPT_, RUN_SYSTEM_, RUN_SYSTEM_NOCHECK_, RUN_SUBPROCESS_,
 		MIN_, MAX_, IMIN_, IMAX_,
 		LEFTSTR_, RIGHTSTR_, MIDSTR_,
 		SELECTED_, SELECTEDSTR_, NUMBER_OF_SELECTED_, SELECT_OBJECT_, PLUS_OBJECT_, MINUS_OBJECT_, REMOVE_OBJECT_,
@@ -243,7 +243,7 @@ static const char32 *Formula_instructionNames [1 + hoogsteSymbool] = { U"",
 	U"do", U"do$",
 	U"writeInfo", U"writeInfoLine", U"appendInfo", U"appendInfoLine",
 	U"writeFile", U"writeFileLine", U"appendFile", U"appendFileLine",
-	U"pauseScript", U"exitScript", U"runScript", U"runSystem", U"runSystem_nocheck",
+	U"pauseScript", U"exitScript", U"runScript", U"runSystem", U"runSystem_nocheck", U"runSubprocess",
 	U"min", U"max", U"imin", U"imax",
 	U"left$", U"right$", U"mid$",
 	U"selected", U"selected$", U"numberOfSelected", U"selectObject", U"plusObject", U"minusObject", U"removeObject",
@@ -3344,6 +3344,31 @@ static void do_runSystem_nocheck () {
 	}
 	pushNumber (1);
 }
+static void do_runSubprocess () {
+	if (theCurrentPraatObjects != & theForegroundPraatObjects)
+		Melder_throw (U"The function \"runSubprocess\" is not available inside manuals.");
+	Stackel narg = pop;
+	Melder_assert (narg->which == Stackel_NUMBER);
+	int numberOfArguments = lround (narg->number);
+	w -= numberOfArguments;
+	Stackel commandFile = & theStack [w + 1];
+	if (commandFile->which != Stackel_STRING)
+		Melder_throw (U"The first argument to \"runSubprocess\" has to be a command name.");
+	autostring32vector arguments (1, numberOfArguments - 1);
+	for (int iarg = 1; iarg < numberOfArguments; iarg ++) {
+		Stackel arg = & theStack [w + 1 + iarg];
+		if (arg->which == Stackel_NUMBER)
+			arguments [iarg] = Melder_dup (Melder_double (arg->number));
+		else if (arg->which == Stackel_STRING)
+			arguments [iarg] = Melder_dup (arg->string);
+	}
+	try {
+		Melder_execv (commandFile->string, numberOfArguments - 1, arguments.peek());
+	} catch (MelderError) {
+		Melder_throw (U"Command \"", commandFile->string, U"\" returned error status.");
+	}
+	pushNumber (1);
+}
 static void do_min () {
 	Stackel n = pop, last;
 	double result;
@@ -5564,6 +5589,7 @@ case NUMBER_: { pushNumber (f [programPointer]. content.number);
 } break; case RUN_SCRIPT_: { do_runScript ();
 } break; case RUN_SYSTEM_: { do_runSystem ();
 } break; case RUN_SYSTEM_NOCHECK_: { do_runSystem_nocheck ();
+} break; case RUN_SUBPROCESS_: { do_runSubprocess ();
 } break; case MIN_: { do_min ();
 } break; case MAX_: { do_max ();
 } break; case IMIN_: { do_imin ();
diff --git a/sys/GraphicsScreen.cpp b/sys/GraphicsScreen.cpp
index e9f85b1..3c14a91 100644
--- a/sys/GraphicsScreen.cpp
+++ b/sys/GraphicsScreen.cpp
@@ -219,25 +219,14 @@ void structGraphicsScreen :: v_flushWs () {
 		if (d_drawingArea) {
 			GuiShell shell = d_drawingArea -> d_shell;
 			Melder_assert (shell);
-			#if 0
-				//GuiCocoaDrawingArea *cocoaDrawingArea = (GuiCocoaDrawingArea *) d_drawingArea -> d_widget;
-				//[cocoaDrawingArea display];
-				//GuiShell_drain (shell);
-				//CGContextFlush (our d_macGraphicsContext);
-				v_updateWs ();
-				NSEvent *nsEvent = [[d_macView window]
-					nextEventMatchingMask: NSAnyEventMask
-					untilDate: [NSDate distantPast]
-					inMode: NSDefaultRunLoopMode
-					dequeue: NO
-					];
-				Melder_assert (shell -> d_cocoaShell);
-				[shell -> d_cocoaShell   flushWindow];
-				//[[NSGraphicsContext currentContext] flushGraphics];
-			#else
-				Melder_assert (shell -> d_cocoaShell);
-				[shell -> d_cocoaShell   flushWindow];
-			#endif
+			Melder_assert (shell -> d_cocoaShell);
+			[shell -> d_cocoaShell   flushWindow];
+			NSEvent *nsEvent = [[d_macView window]
+				nextEventMatchingMask: NSAnyEventMask
+				untilDate: [NSDate distantPast]
+				inMode: NSDefaultRunLoopMode
+				dequeue: NO
+				];
 		}
 	#elif win
 		/*GdiFlush ();*/
@@ -311,7 +300,7 @@ void structGraphicsScreen :: v_clearWs () {
             //CGContextSynchronize (context);
             CGContextRestoreGState (context);
 			[cocoaDrawingArea unlockFocus];
-			[cocoaDrawingArea setNeedsDisplay: YES];
+			//[cocoaDrawingArea setNeedsDisplay: YES];
 			//[cocoaDrawingArea display];
         }
 	#elif win
diff --git a/sys/GuiShell.cpp b/sys/GuiShell.cpp
index 5d683ee..2188f7d 100644
--- a/sys/GuiShell.cpp
+++ b/sys/GuiShell.cpp
@@ -111,14 +111,14 @@ void GuiShell_drain (GuiShell me) {
 		//gdk_window_flush (gtk_widget_get_window (my d_gtkWindow));
 		gdk_flush ();
 	#elif cocoa
+		Melder_assert (my d_cocoaShell);
+        [my d_cocoaShell   display];   // not just flushWindow
 		NSEvent *nsEvent = [NSApp
 			nextEventMatchingMask: NSAnyEventMask
 			untilDate: [NSDate distantPast]
 			inMode: NSDefaultRunLoopMode
 			dequeue: NO
 			];
-		Melder_assert (my d_cocoaShell);
-        [my d_cocoaShell   flushWindow];
 	#elif win
 	#endif
 }
diff --git a/sys/InfoEditor.cpp b/sys/InfoEditor.cpp
index bf095df..6b451fe 100644
--- a/sys/InfoEditor.cpp
+++ b/sys/InfoEditor.cpp
@@ -48,8 +48,19 @@ void gui_information (const char32 *message) {
 	GuiText_setString (editor -> textWidget, message);
 	GuiThing_show (editor -> d_windowForm);
 	/*
-	 * Try to make sure that the invalidated text widget and the elements of the fronted window are redrawn before the next event.
-	 */
+		Try to make sure that the invalidated text widget and the elements of the fronted window are
+		redrawn before the next event.
+
+		The following Praat script can test this:
+
+		writeInfoLine: "hoi"
+		for i to 100
+			appendInfoLine: i
+		endfor
+		
+		The Info window should scroll continuously while the lines are added,
+		not just show the end result.
+	*/
 	#if cocoa
 		#if 1
 			NSEvent *nsEvent = [NSApp
@@ -69,13 +80,13 @@ void gui_information (const char32 *message) {
 				It would be nice not to actually have to wait for events (with nextEventMatchingMask),
 				because we are not interested in the events; we're interested only in the graphics update.
 			*/
-			//[editor -> d_windowForm -> d_cocoaWindow   displayIfNeeded];   // apparently, this does not suffice
+			//[editor -> d_windowForm -> d_cocoaShell   displayIfNeeded];   // apparently, this does not suffice
 			//[editor -> textWidget -> d_cocoaTextView   lockFocus];   // this displays the menu as well as the text
-			[editor -> d_windowForm -> d_cocoaWindow   display];   // this displays the menu as well as the text
+			[editor -> d_windowForm -> d_cocoaShell   display];   // this displays the menu as well as the text
 			//[editor -> textWidget -> d_cocoaTextView   displayIfNeeded];   // this displays only the text
 			//[editor -> textWidget -> d_cocoaTextView   display];
 			//[editor -> textWidget -> d_cocoaTextView   unlockFocus];   // this displays the menu as well as the text
-			[editor -> d_windowForm -> d_cocoaWindow   flushWindow];
+			[editor -> d_windowForm -> d_cocoaShell   flushWindow];
 			[NSApp  updateWindows];   // called automatically?
 		#endif
 	#elif defined (macintosh)
diff --git a/sys/ManPages.cpp b/sys/ManPages.cpp
index 251d861..a58b296 100644
--- a/sys/ManPages.cpp
+++ b/sys/ManPages.cpp
@@ -1,6 +1,6 @@
 /* ManPages.cpp
  *
- * Copyright (C) 1996-2012,2014,2015,2016 Paul Boersma
+ * Copyright (C) 1996-2012,2014,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,11 +26,11 @@ Thing_implement (ManPages, Daata, 0);
 
 #define LONGEST_FILE_NAME  55
 
-static int isAllowedFileNameCharacter (int c) {
-	return isalnum (c) || c == '_' || c == '-' || c == '+';
+static bool isAllowedFileNameCharacter (char32 c) {
+	return isalnum ((int) c) || c == U'_' || c == U'-' || c == U'+';
 }
-static int isSingleWordCharacter (int c) {
-	return isalnum (c) || c == '_';
+static bool isSingleWordCharacter (char32 c) {
+	return isalnum ((int) c) || c == U'_';
 }
 
 static long lookUp_unsorted (ManPages me, const char32 *title);
@@ -82,11 +82,11 @@ static const char32 *extractLink (const char32 *text, const char32 *p, char32 *l
 			}
 			*to ++ = *from ++;
 		}
-		if (*from == '|') { from ++; while (*from != '@' && *from != '\0') from ++; }
+		if (*from == U'|') { from ++; while (*from != U'@' && *from != U'\0') from ++; }
 		if (*from) p = from + 1; else p = from;   /* Skip '@' but not '\0'. */
 	} else {
 		const char32 *from = p + 1;
-		while (isSingleWordCharacter ((int) *from)) {
+		while (isSingleWordCharacter (*from)) {
 			if (to >= max) {
 				Melder_throw (U"(ManPages::grind:) Link starting with \"@@\" is too long:\n", text);
 			}
@@ -188,7 +188,7 @@ static void readOnePage (ManPages me, MelderReadText text) {
 					*q = '\0';
 				} else {
 					char32 *q = fileName;
-					while (*p != U' ' && *p != U'\0') * q ++ = * p ++;   // One word, up to the next space.
+					while (*p != U' ' && *p != U'\0') * q ++ = * p ++;   // one word, up to the next space
 					*q = '\0';
 				}
 				MelderDir_relativePathToFile (& my rootDirectory, fileName, & file2);
@@ -216,7 +216,7 @@ static void readOnePage (ManPages me, MelderReadText text) {
 					 * Second try: with upper case.
 					 */
 					Melder_clearError ();
-					link [0] = toupper ((int) link [0]);
+					link [0] = toupper32 (link [0]);
 					Melder_sprint (fileName,256, link, U".man");
 					MelderDir_getFile (& my rootDirectory, fileName, & file2);
 					autoMelderReadText text2 = MelderReadText_createFromFile (& file2);
@@ -229,8 +229,8 @@ static void readOnePage (ManPages me, MelderReadText text) {
 			}
 		}
 	}
-	++ par;   // Room for the last paragraph (because counting starts at 0).
-	++ par;   // Room for the final zero-type paragraph.
+	++ par;   // room for the last paragraph (because counting starts at 0)
+	++ par;   // room for the final zero-type paragraph
 	page -> paragraphs = (ManPage_Paragraph) Melder_realloc (page -> paragraphs, (int64) sizeof (struct structManPage_Paragraph) * (par - page -> paragraphs));
 }
 void structManPages :: v_readText (MelderReadText text, int /*formatVersion*/) {
@@ -259,13 +259,13 @@ static int pageCompare (const void *first, const void *second) {
 	ManPage me = * (ManPage *) first, thee = * (ManPage *) second;
 	const char32 *p = my title, *q = thy title;
 	for (;;) {
-		int plower = tolower (*p), qlower = tolower (*q);
+		char32 plower = tolower32 (*p), qlower = tolower32 (*q);
 		if (plower < qlower) return -1;
 		if (plower > qlower) return 1;
 		if (plower == '\0') return str32cmp (my title, thy title);
 		p ++, q ++;
 	}
-	return 0;   /* Should not occur. */
+	return 0;   // should not occur
 }
 
 static long lookUp_unsorted (ManPages me, const char32 *title) {
@@ -282,10 +282,10 @@ static long lookUp_unsorted (ManPages me, const char32 *title) {
 	/*
 	 * If that fails, try to find the upper-case variant.
 	 */
-	if (islower (title [0])) {
+	if (islower32 (title [0])) {
 		char32 upperTitle [300];
 		Melder_sprint (upperTitle,300, title);
-		upperTitle [0] = toupper (upperTitle [0]);
+		upperTitle [0] = toupper32 (upperTitle [0]);
 		for (i = 1; i <= my pages.size; i ++) {
 			ManPage page = my pages.at [i];
 			if (str32equ (page -> title, upperTitle)) return i;
@@ -302,10 +302,10 @@ static long lookUp_sorted (ManPages me, const char32 *title) {
 	page = (ManPage *) bsearch (& dummy, & my pages.at [1], my pages.size, sizeof (ManPage), pageCompare);   // noexcept
 	dummy -> title = nullptr;   // undangle
 	if (page) return (page - & my pages.at [1]) + 1;
-	if (islower (title [0]) || isupper (title [0])) {
+	if (islower32 (title [0]) || isupper32 (title [0])) {
 		char32 caseSwitchedTitle [300];
 		Melder_sprint (caseSwitchedTitle,300, title);
-		caseSwitchedTitle [0] = islower (title [0]) ? toupper (caseSwitchedTitle [0]) : tolower (caseSwitchedTitle [0]);
+		caseSwitchedTitle [0] = islower32 (title [0]) ? toupper32 (caseSwitchedTitle [0]) : tolower32 (caseSwitchedTitle [0]);
 		dummy -> title = caseSwitchedTitle;
 		page = (ManPage *) bsearch (& dummy, & my pages.at [1], my pages.size, sizeof (ManPage), pageCompare);   // noexcept
 		dummy -> title = nullptr;   // undangle
@@ -673,7 +673,7 @@ static void writeParagraphsAsHtml (ManPages me, MelderFile file, ManPage_Paragra
 				} else {
 					q = link;
 					if (! ManPages_lookUp_caseSensitive (me, link)) {
-						MelderString_appendCharacter (buffer, toupper (link [0]));
+						MelderString_appendCharacter (buffer, toupper32 (link [0]));
 						if (*q) q ++;   // first letter already written
 					}
 					while (*q && q - link < LONGEST_FILE_NAME) {
@@ -741,7 +741,7 @@ static void writeParagraphsAsHtml (ManPages me, MelderFile file, ManPage_Paragra
 				if (wordBold && ! isSingleWordCharacter (*p)) { MelderString_append (buffer, U"</b>"); wordBold = false; }
 				if (wordCode && ! isSingleWordCharacter (*p)) { MelderString_append (buffer, U"</code>"); wordCode = false; }*/
 				if (*p == U'\\') {
-					int kar1 = *++p, kar2 = *++p;
+					char32 kar1 = *++p, kar2 = *++p;
 					Longchar_Info info = Longchar_getInfo (kar1, kar2);
 					if (info -> unicode < 127) {
 						MelderString_appendCharacter (buffer, info -> unicode ? info -> unicode : U'?');
diff --git a/sys/Manual.cpp b/sys/Manual.cpp
index 72fe92e..3f65e1e 100644
--- a/sys/Manual.cpp
+++ b/sys/Manual.cpp
@@ -1,6 +1,6 @@
 /* Manual.cpp
  *
- * Copyright (C) 1996-2011,2014,2015,2016 Paul Boersma
+ * Copyright (C) 1996-2011,2014,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -268,7 +268,7 @@ static double searchToken (ManPages me, long ipage, char32 *token) {
 	 */
 	static MelderString buffer { 0 };
 	MelderString_copy (& buffer, page -> title);
-	for (char32 *p = & buffer.string [0]; *p != U'\0'; p ++) *p = towlower ((int) *p);
+	for (char32 *p = & buffer.string [0]; *p != U'\0'; p ++) *p = tolower32 (*p);
 	if (str32str (buffer.string, token)) {
 		goodness += 300.0;   // lots of points for a match in the title!
 		if (str32equ (buffer.string, token))
@@ -281,7 +281,7 @@ static double searchToken (ManPages me, long ipage, char32 *token) {
 		if (par -> text) {
 			char32 *ptoken;
 			MelderString_copy (& buffer, par -> text);
-			for (char32 *p = & buffer.string [0]; *p != '\0'; p ++) *p = towlower ((int) *p);
+			for (char32 *p = & buffer.string [0]; *p != '\0'; p ++) *p = tolower32 (*p);
 			ptoken = str32str (buffer.string, token);
 			if (ptoken) {
 				goodness += 10.0;   // ten points for every paragraph with a match!
@@ -302,7 +302,7 @@ static void search (Manual me, const char32 *query) {
 	MelderString_copy (& searchText, query);
 	for (char32 *p = & searchText.string [0]; *p != U'\0'; p ++) {
 		if (*p == U'\n') *p = U' ';
-		*p = towlower ((int) *p);
+		*p = tolower32 (*p);
 	}
 	if (! goodnessOfMatch)
 		goodnessOfMatch = NUMvector <double> (1, numberOfPages);
diff --git a/sys/Ui.cpp b/sys/Ui.cpp
index 1e44327..a307848 100644
--- a/sys/Ui.cpp
+++ b/sys/Ui.cpp
@@ -1,6 +1,6 @@
 /* Ui.cpp
  *
- * Copyright (C) 1992-2012,2013,2015,2016 Paul Boersma
+ * Copyright (C) 1992-2012,2013,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -320,8 +320,8 @@ static void UiField_stringToValue (UiField me, const char32 *string, Interpreter
 					UiOption b = my options.at [i];
 					char32 name2 [100];
 					str32cpy (name2, b -> name);
-					if (islower ((int) name2 [0])) name2 [0] = (char32) toupper ((int) name2 [0]);
-					else if (isupper ((int) name2 [0])) name2 [0] = (char32) tolower ((int) name2 [0]);
+					if (islower32 (name2 [0])) name2 [0] = toupper32 (name2 [0]);
+					else if (isupper32 (name2 [0])) name2 [0] = tolower32 (name2 [0]);
 					if (str32equ (string, name2))
 						my integerValue = i;
 				}
@@ -1107,7 +1107,7 @@ static void UiField_api_header_C (UiField me, UiField next, bool isLastNonLabelF
 	char32 cName [100], *q = & cName [0];
 	Melder_assert (my formLabel);
 	const char32 *p = & my formLabel [0];
-	*q ++ = tolower (*p ++);
+	*q ++ = tolower32 (*p ++);
 	bool up = false;
 	for (; *p != U'\0'; p ++) {
 		if (*p == U'(') {
@@ -1123,7 +1123,7 @@ static void UiField_api_header_C (UiField me, UiField next, bool isLastNonLabelF
 			*q ++ = U'a';
 			*q ++ = U'r';
 		} else if (up) {
-			*q ++ = toupper (*p);
+			*q ++ = toupper32 (*p);
 			up = false;
 		} else {
 			*q ++ = *p;
@@ -1299,8 +1299,8 @@ static void UiField_argToValue (UiField me, Stackel arg, Interpreter /* interpre
 					UiOption b = my options.at [i];
 					char32 name2 [100];
 					str32cpy (name2, b -> name);
-					if (iswlower ((int) name2 [0])) name2 [0] = (char32) towupper ((int) name2 [0]);
-					else if (iswupper ((int) name2 [0])) name2 [0] = (char32) towlower ((int) name2 [0]);
+					if (islower32 (name2 [0])) name2 [0] = toupper32 (name2 [0]);
+					else if (isupper32 (name2 [0])) name2 [0] = tolower32 (name2 [0]);
 					if (str32equ (arg -> string, name2))
 						my integerValue = i;
 				}
@@ -1670,7 +1670,7 @@ void UiForm_Interpreter_addVariables (UiForm me, Interpreter interpreter) {
 		/*
 		 * Change e.g. "Number of people" to "number_of_people".
 		 */
-		lowerCaseFieldName.string [0] = (char32) towlower ((int) lowerCaseFieldName.string [0]);   // BUG for non-BMP characters
+		lowerCaseFieldName.string [0] = tolower32 (lowerCaseFieldName.string [0]);   // BUG for non-BMP characters
 		for (char32 *p = & lowerCaseFieldName.string [0]; *p != U'\0'; p ++) {
 			if (*p == U' ') *p = U'_';
 		}
diff --git a/sys/melder.h b/sys/melder.h
index 5a5b126..3e81e04 100644
--- a/sys/melder.h
+++ b/sys/melder.h
@@ -171,6 +171,10 @@ cont:
 			goto cont;
 	return p - 1 - string1;
 }
+inline static bool islower32 (char32 kar) { return iswlower ((int) kar); }
+inline static bool isupper32 (char32 kar) { return iswupper ((int) kar); }
+inline static char32 tolower32 (char32 kar) { return (char32) towlower ((int) kar); }
+inline static char32 toupper32 (char32 kar) { return (char32) towupper ((int) kar); }
 extern "C" char * Melder_peek32to8 (const char32 *string);
 inline static long a32tol (const char32 *string) {
 	if (sizeof (wchar_t) == 4) {
@@ -1321,6 +1325,7 @@ const char32 * MelderQuantity_getShortUnitText (int quantity);   // e.g. "s"
 
 char32 * Melder_getenv (const char32 *variableName);
 void Melder_system (const char32 *command);   // spawn a system command
+void Melder_execv (const char32 *executableFileName, int narg, char32 **args);   // spawn a subprocess
 double Melder_clock ();   // seconds since 1969
 
 struct autoMelderProgressOff {
diff --git a/sys/melder_sysenv.cpp b/sys/melder_sysenv.cpp
index 97f9dd3..6e1cf81 100644
--- a/sys/melder_sysenv.cpp
+++ b/sys/melder_sysenv.cpp
@@ -1,6 +1,6 @@
 /* melder_sysenv.cpp
  *
- * Copyright (C) 1992-2011,2015 Paul Boersma
+ * Copyright (C) 1992-2011,2015,2016 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -23,11 +23,6 @@
  * pb 2011/04/05 C++
  */
 
-/*
- * This is a replacement for the CodeWarrior routines getenv and system,
- * into which many bugs were introduced in the year 2000.
- */
-
 #if defined (_WIN32)
 	#if ! defined (__CYGWIN__) && ! defined (__MINGW32__)
 		#include <crtl.h>
@@ -35,8 +30,16 @@
 	#include <windows.h>
 	#include <errno.h>
 	#include <stdlib.h>
+#else
+	#if defined (linux)
+		#include  <sys/wait.h>
+	#endif
+	#include <unistd.h>
+	#include <sys/types.h>
+	#include <sys/wait.h>
 #endif
 #include "melder.h"
+#include "NUM.h"
 
 char32 * Melder_getenv (const char32 *variableName) {
 	#if defined (macintosh) || defined (UNIX) || defined (__MINGW32__) || defined (__CYGWIN__)
@@ -98,4 +101,29 @@ void Melder_system (const char32 *command) {
 	#endif
 }
 
+void Melder_execv (const char32 *executableFileName, int narg, char32 ** args) {
+	#if defined (macintosh) || defined (UNIX)
+		Melder_casual (U"Command: <<", executableFileName, U">>");
+		autostring8vector args8 (0, narg + 1);
+		args8 [0] = Melder_32to8 (executableFileName);
+		for (int i = 1; i <= narg; i ++) {
+			Melder_casual (U"Argument ", i, U": <<", args [i], U">>");
+			args8 [i] = Melder_32to8 (args [i]);
+		}
+		args8 [narg + 1] = nullptr;
+		pid_t processID = fork ();
+		if (processID == 0) {   // we are in the child process
+			execvp (Melder_peek32to8 (executableFileName), args8.peek());
+			/* if we arrive here, some error occurred */
+			fprintf (stderr, "Some error occurred");
+			_exit (EXIT_FAILURE);
+		} else if (processID > 0) {   // we are still in the calling Praat
+			waitpid (processID, nullptr, 0);
+		} else {
+			Melder_throw (U"Could not fork.");
+		}
+	#elif defined (_WIN32)
+	#endif
+}
+
 /* End of file melder_sysenv.cpp */
diff --git a/sys/praat.cpp b/sys/praat.cpp
index 00a61fc..39bff6d 100644
--- a/sys/praat.cpp
+++ b/sys/praat.cpp
@@ -1,6 +1,6 @@
 /* praat.cpp
  *
- * Copyright (C) 1992-2012,2013,2014,2015,2016 Paul Boersma
+ * Copyright (C) 1992-2012,2013,2014,2015,2016,2017 Paul Boersma
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -1229,6 +1229,13 @@ void praat_init (const char32 *title, int argc, char **argv)
 		Melder_tracingToFile (& tracingFile);
 	}
 
+	#if defined (NO_GRAPHICS)
+		if (! Melder_batch) {
+			fprintf (stderr, "The barren edition of Praat cannot be used interactively. "
+				"Supply \"--run\" and a script file name on the command line.\n");
+			exit (1);
+		}
+	#endif
 	#if defined (UNIX)
 		if (! Melder_batch) {
 			/*
@@ -1522,10 +1529,16 @@ void praat_run () {
 		Melder_assert ((double) dummy == 40000.0);
 		Melder_assert ((double) (int16_t) dummy == -25536.0);
 	}
+	{ unsigned int dummy = 40000;
+		Melder_assert ((int) (int16_t) dummy == -25536);
+		Melder_assert ((short) (int16_t) dummy == -25536);
+		Melder_assert ((double) dummy == 40000.0);
+		Melder_assert ((double) (int16_t) dummy == -25536.0);
+	}
 	{
 		int64 dummy = 1000000000000;
 		if (! str32equ (Melder_integer (dummy), U"1000000000000"))
-			Melder_fatal (U"The number 1000000000000 is mistaken written on this machine as ", dummy, U".");
+			Melder_fatal (U"The number 1000000000000 is mistakenly written on this machine as ", dummy, U".");
 	}
 	{ uint32_t dummy = 0xffffffff;
 		Melder_assert ((int64) dummy == 4294967295LL);
diff --git a/sys/praat_logo.cpp b/sys/praat_logo.cpp
index 5ac2240..739bf84 100644
--- a/sys/praat_logo.cpp
+++ b/sys/praat_logo.cpp
@@ -1,6 +1,6 @@
 /* praat_logo.cpp
  *
- * Copyright (C) 1996-2012,2013,2014,2015,2016 Paul Boersma, 2008 Stefan de Konink
+ * Copyright (C) 1996-2012,2013,2014,2015,2016,2017 Paul Boersma, 2008 Stefan de Konink
  *
  * This code is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -34,7 +34,7 @@ static void logo_defaultDraw (Graphics g) {
 	Graphics_text (g, 0.5, 0.6, praatP.title);
 	Graphics_setFontStyle (g, 0);
 	Graphics_setFontSize (g, 12);
-	Graphics_text (g, 0.5, 0.25, U"\\s{Built on the} %%Praat shell%\\s{,© Paul Boersma, 1992-2016");
+	Graphics_text (g, 0.5, 0.25, U"\\s{Built on the} %%Praat shell%\\s{,© Paul Boersma, 1992-2017");
 }
 
 static struct {
diff --git a/sys/praat_version.h b/sys/praat_version.h
index af9f391..48942fc 100644
--- a/sys/praat_version.h
+++ b/sys/praat_version.h
@@ -1,5 +1,5 @@
-#define PRAAT_VERSION_STR 6.0.22
-#define PRAAT_VERSION_NUM 6022
-#define PRAAT_YEAR 2016
-#define PRAAT_MONTH November
-#define PRAAT_DAY 15
+#define PRAAT_VERSION_STR 6.0.26
+#define PRAAT_VERSION_NUM 6026
+#define PRAAT_YEAR 2017
+#define PRAAT_MONTH March
+#define PRAAT_DAY 2
diff --git a/test/fon ExperimentMFC/simplest.ExperimentMFC b/test/fon ExperimentMFC/simplest.ExperimentMFC
index 5a2371f..1f9f57f 100644
--- a/test/fon ExperimentMFC/simplest.ExperimentMFC	
+++ b/test/fon ExperimentMFC/simplest.ExperimentMFC	
@@ -1,7 +1,7 @@
 "ooTextFile"
 "ExperimentMFC 6"
 
-blankWhilePlaying? <no>
+blankWhilePlaying? <yes>
 stimuliAreSounds? <yes>
 stimulusFileNameHead = "Sounds/"
 stimulusFileNameTail = ".wav"
diff --git a/test/fon/resample16_8.praat b/test/fon/resample16_8.praat
new file mode 100644
index 0000000..006ca98
--- /dev/null
+++ b/test/fon/resample16_8.praat
@@ -0,0 +1,48 @@
+Erase all
+Times
+12
+depth = 200
+
+sweep = Create Sound from formula: "sweep", 1, 0, 10, 16000,
+	... "sin (2 * pi * 400 * x^2)"
+To Spectrogram: 0.05, 8000, 0.002, 20, "Gaussian"
+Select outer viewport: 0, 6, 0, 3
+Paint: 0, 0, 0, 8000, 100, "yes", 90, 0, 0, "yes"
+Remove
+selectObject: sweep
+sweep_8k = Resample: 8000, 50
+To Spectrogram: 0.05, 8000, 0.002, 20, "Gaussian"
+Select outer viewport: 0, 6, 3, 6
+Paint: 0, 0, 0, 8000, 100, "yes", 90, 0, 0, "yes"
+Remove
+
+cutoff = 3800
+
+filter_mat = Create Matrix: "filter1", -depth / 16000, depth / 16000,
+... depth*2, 1 / 16000, (-depth+0.5) / 16000,
+... 1, 1, 1, 1, 1, "if x = 0 then 1 else sin (2*pi*x*cutoff) / (2*pi*x*cutoff)
+... * (0.5 + 0.5 * cos (pi * x*16000 / depth)) fi"
+sum = Get sum
+Formula: "self / sum"
+filter = To Sound
+
+selectObject: sweep, filter
+sweep_low = Convolve: "sum", "zero"
+
+mooi = Create Sound from formula: "mooi", 1, 0, 10, 16000/2, "object [sweep_low, col*2]"
+To Spectrogram: 0.05, 8000, 0.002, 20, "Gaussian"
+Select outer viewport: 0, 6, 6, 9
+Paint: 0, 0, 0, 8000, 100, "yes", 90, 0, 0, "yes"
+Remove
+
+#
+# Write to Info window in base-0 C format.
+#
+writeInfo: "static double filter_2 [", depth*2, "] = { "
+for i to depth*2
+	value = object [filter, i]
+	appendInfo: if abs (value) < 1e-12 then 0 else fixed$ (value, 12) fi, ", "
+endfor
+appendInfoLine: "};"
+
+removeObject: filter_mat, filter, sweep_low, sweep, sweep_8k, mooi
\ No newline at end of file
diff --git a/test/fon/resample22_8.praat b/test/fon/resample22_8.praat
new file mode 100644
index 0000000..9ab7262
--- /dev/null
+++ b/test/fon/resample22_8.praat
@@ -0,0 +1,59 @@
+Erase all
+Times
+12
+depth = 200
+
+sweep = Create Sound from formula: "sweep", 1, 0, 10, 22050,
+	... "sin (2 * pi * 500 * x^2)"
+To Spectrogram: 0.05, 10000, 0.002, 20, "Gaussian"
+Select outer viewport: 0, 6, 0, 3
+Paint: 0, 0, 0, 10000, 100, "yes", 90, 0, 0, "yes"
+Remove
+selectObject: sweep
+sweep_8k = Resample: 8000, depth
+To Spectrogram: 0.05, 6000, 0.002, 20, "Gaussian"
+Select outer viewport: 0, 6, 3, 6
+Paint: 0, 0, 0, 6000, 100, "yes", 90, 0, 0, "yes"
+Remove
+
+cutoff = 3600
+
+for ifilter from 0 to 3
+	filter_mat [ifilter] = Create Matrix: "filter", -depth / 22050, depth / 22050,
+	... depth*2, 1 / 22050, (-depth+0.875-0.25*ifilter) / 22050,
+	... 1, 1, 1, 1, 1, "if x = 0 then 1 else sin (2*pi*x*cutoff) / (2*pi*x*cutoff)
+	... * (0.5 + 0.5 * cos (pi * x*22050 / depth)) fi"
+	sum = Get sum
+	Formula: "self / sum"
+	filter [ifilter] = To Sound
+	plusObject: sweep
+	sweep_low [ifilter] = Convolve: "sum", "zero"
+endfor
+
+mooi = Create Sound from formula: "mooi", 1, 0, 10, 22050/2.75,
+	... "object [sweep_low [col mod 4], (col*11+(col mod 4))/4]"
+To Spectrogram: 0.05, 6000, 0.002, 20, "Gaussian"
+Select outer viewport: 0, 6, 6, 9
+Paint: 0, 0, 0, 6000, 100, "yes", 90, 0, 0, "yes"
+Remove
+
+;exit
+
+#
+# Write to Info window in base-0 C format.
+#
+writeInfoLine: "static double filter_11_4 [4] [", depth*2, "] = {"
+for ifilter from 0 to 3
+	appendInfo: tab$, "{ "
+	for i to depth*2
+		value = object [filter [ifilter], i]
+		appendInfo: if abs (value) < 1e-12 then 0 else fixed$ (value, 12) fi, ", "
+	endfor
+	appendInfoLine: "},"
+endfor
+appendInfoLine: "};"
+
+for ifilter from 0 to 3
+	removeObject: filter_mat [ifilter], filter [ifilter], sweep_low [ifilter]
+endfor
+removeObject: sweep, sweep_8k, mooi
diff --git a/test/fon/resample44_8.praat b/test/fon/resample44_8.praat
new file mode 100644
index 0000000..d5966dc
--- /dev/null
+++ b/test/fon/resample44_8.praat
@@ -0,0 +1,60 @@
+Erase all
+Times
+12
+depth = 200
+
+sweep = Create Sound from formula: "sweep", 1, 0, 10, 44100,
+	... "sin (2 * pi * 1000 * x^2)"
+To Spectrogram: 0.05, 20000, 0.002, 20, "Gaussian"
+Select outer viewport: 0, 6, 0, 3
+Paint: 0, 0, 0, 20000, 100, "yes", 90, 0, 0, "yes"
+Remove
+selectObject: sweep
+sweep_8k = Resample: 8000, 50
+To Spectrogram: 0.05, 6000, 0.002, 20, "Gaussian"
+Select outer viewport: 0, 6, 3, 6
+Paint: 0, 0, 0, 6000, 100, "yes", 90, 0, 0, "yes"
+Remove
+
+cutoff = 3600
+
+for ifilter from 0 to 1
+	filter_mat [ifilter] = Create Matrix: "filter", -depth / 44100, depth / 44100,
+	... depth*2, 1 / 44100, (-depth+0.75-0.5*ifilter) / 44100,
+	... 1, 1, 1, 1, 1, "if x = 0 then 1 else sin (2*pi*x*cutoff) / (2*pi*x*cutoff)
+	... * (0.5 + 0.5 * cos (pi * x*44100 / depth)) fi"
+	sum = Get sum
+	Formula: "self / sum"
+	filter [ifilter] = To Sound
+	;Multiply by window: "Hanning"
+	plusObject: sweep
+	sweep_low [ifilter] = Convolve: "sum", "zero"
+endfor
+
+mooi = Create Sound from formula: "mooi", 1, 0, 10, 44100/5.5,
+	... "object [sweep_low [col mod 2], (col*11+(col mod 2))/2]"
+To Spectrogram: 0.05, 6000, 0.002, 20, "Gaussian"
+Select outer viewport: 0, 6, 6, 9
+Paint: 0, 0, 0, 6000, 100, "yes", 90, 0, 0, "yes"
+Remove
+
+;exit
+
+#
+# Write to Info window in base-0 C format.
+#
+writeInfoLine: "static double filter_11_2 [2] [", depth*2, "] = {"
+for ifilter from 0 to 1
+	appendInfo: tab$, "{ "
+	for i to depth*2
+		value = object [filter [ifilter], i]
+		appendInfo: if abs (value) < 1e-12 then 0 else fixed$ (value, 12) fi, ", "
+	endfor
+	appendInfoLine: "},"
+endfor
+appendInfoLine: "};"
+
+for ifilter from 0 to 1
+	removeObject: filter_mat [ifilter], filter [ifilter], sweep_low [ifilter]
+endfor
+removeObject: sweep, sweep_8k, mooi
\ No newline at end of file

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