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 <[email protected]>
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@.")
 
+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@. 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@. "
+       "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 
[email protected]");
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

_______________________________________________
debian-med-commit mailing list
[email protected]
http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/debian-med-commit

Reply via email to