[med-svn] [Git][med-team/praat][master] 5 commits: New upstream version 6.0.49

Rafael Laboissiere gitlab at salsa.debian.org
Mon Mar 11 17:15:00 GMT 2019


Rafael Laboissiere pushed to branch master at Debian Med / praat


Commits:
b71b6d8b by Rafael Laboissiere at 2019-03-10T22:07:49Z
New upstream version 6.0.49
- - - - -
29daecfe by Rafael Laboissiere at 2019-03-10T22:10:24Z
Merge tag 'upstream/6.0.49'

Upstream version 6.0.49

- - - - -
542eef41 by Rafael Laboissiere at 2019-03-10T22:36:57Z
d/copyright: Reflect upstream changes

- - - - -
83777a4d by Rafael Laboissiere at 2019-03-11T05:42:56Z
d/What_s_new_.html: Update upstream changelog

Gbp-Dch: Ignore

- - - - -
ca3058be by Rafael Laboissiere at 2019-03-11T17:14:00Z
Changelog entry for version 6.0.49-1

Gbp-Dch: Ignore

- - - - -


26 changed files:

- debian/What_s_new_.html
- debian/changelog
- debian/copyright
- dwtools/CC.cpp
- dwtools/CC.h
- dwtools/CCs_to_DTW.cpp
- dwtools/Sound_and_Spectrogram_extensions.cpp
- dwtools/praat_David_init.cpp
- fon/Makefile
- fon/Pitch.cpp
- fon/Pitch.h
- fon/Sound.cpp
- fon/Sound.h
- + fon/SoundSet.cpp
- + fon/SoundSet.h
- fon/manual_tutorials.cpp
- fon/praat_Fon.cpp
- fon/praat_Matrix.cpp
- fon/praat_Sound.cpp
- main/praat.plist
- melder/MAT.h
- melder/MelderString.h
- melder/melder_strings.cpp
- sys/Formula.cpp
- sys/praat_version.h
- + test/stat/Table_undefined.praat


Changes:

=====================================
debian/What_s_new_.html
=====================================
@@ -7,6 +7,18 @@ What's new?
 <p>
 Latest changes in Praat.</p>
 <p>
+<b>6.0.49</b> (2 March 2019)</p>
+<ul>
+<li>
+ Removed a bug introduced in 6.0.41 whereby a script could misreport an undefined table value.
+<li>
+ Removed a bug introduced in 6.0.44 whereby an MFCC's maximum frequency could be ignored.
+<li>
+ Pitch: Tabulate candidates.
+<li>
+ SoundSet.
+</ul>
+<p>
 <b>6.0.48</b> (17 February 2019)</p>
 <ul>
 <li>
@@ -644,7 +656,7 @@ What used to be new?</h3>
 </ul>
 <hr>
 <address>
-	<p>© ppgb, February 17, 2019</p>
+	<p>© ppgb, March 2, 2019</p>
 </address>
 </body>
 </html>


=====================================
debian/changelog
=====================================
@@ -1,3 +1,10 @@
+praat (6.0.49-1) unstable; urgency=medium
+
+  * New upstream version 6.0.49
+  * d/copyright: Reflect upstream changes
+
+ -- Rafael Laboissiere <rafael at debian.org>  Sun, 10 Mar 2019 19:37:06 -0300
+
 praat (6.0.48-1) unstable; urgency=medium
 
   * New upstream version 6.0.48


=====================================
debian/copyright
=====================================
@@ -5,7 +5,7 @@ Source: http://www.fon.hum.uva.nl/praat/download_sources.html
 
 Files: *
 Copyright: 1990-2019 Paul Boersma
-           1992-2018 David Weenink
+           1992-2019 David Weenink
            2007 Erez Volk
            2002 Hansjoerg Mixdorff
            2008, 2017 Stefan de Konink


=====================================
dwtools/CC.cpp
=====================================
@@ -1,6 +1,6 @@
 /* CC.cpp
  *
- * Copyright (C) 1993-2018 David Weenink
+ * Copyright (C) 1993-2019 David Weenink
  *
  * 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
@@ -49,7 +49,7 @@
 
 Thing_implement (CC, Sampled, 1);
 
-static integer CC_getMaximumNumberOfCoefficientsUsed (CC me) {
+integer CC_getMaximumNumberOfCoefficientsUsed (CC me) {
 	integer numberOfCoefficients = 0;
 	for (integer iframe = 1; iframe <= my nx; iframe ++) {
 		CC_Frame cf = & my frame [iframe];


=====================================
dwtools/CC.h
=====================================
@@ -2,7 +2,7 @@
 #define _CC_h_
 /* CC.h
  *
- * Copyright (C) 1993-2017 David Weenink
+ * Copyright (C) 1993-2019 David Weenink
  *
  * 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,6 +34,8 @@ void CC_init (CC me, double tmin, double tmax, integer nt, double dt, double t1,
 
 void CC_getNumberOfCoefficients_extrema (CC me, integer startframe, integer endframe, integer *min, integer *max);
 
+integer CC_getMaximumNumberOfCoefficientsUsed (CC me);
+
 integer CC_getMinimumNumberOfCoefficients (CC me, integer startframe, integer endframe);
 
 integer CC_getMaximumNumberOfCoefficients (CC me, integer startframe, integer endframe);


=====================================
dwtools/CCs_to_DTW.cpp
=====================================
@@ -56,7 +56,7 @@ autoDTW CCs_to_DTW (CC me, CC thee, double coefficientWeight, double logEnergyWe
 		integer nr = Melder_ifloor (regressionWindowLength / my dx);
 		
 		Melder_require (my maximumNumberOfCoefficients == thy maximumNumberOfCoefficients,
-			U"CC orders should be equal.");
+			U"The maximum number of coefficients should be equal.");
 		Melder_require (! (coefficientRegressionWeight != 0.0 && nr < 2), 
 			U"Time window for regression is too small.");
 


=====================================
dwtools/Sound_and_Spectrogram_extensions.cpp
=====================================
@@ -1,6 +1,6 @@
 /* Sound_and_Spectrogram_extensions.cpp
  *
- * Copyright (C) 1993-2018 David Weenink
+ * Copyright (C) 1993-2019 David Weenink
  *
  * 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
@@ -166,7 +166,7 @@ static void Sound_into_MelSpectrogram_frame (Sound me, MelSpectrogram thee, inte
 	autoSpectrum him = Sound_to_Spectrum_power (me);
 
 	for (integer ifilter = 1; ifilter <= thy ny; ifilter ++) {
-		double power = 0;
+		longdouble power = 0.0;
 		double fc_mel = thy y1 + (ifilter - 1) * thy dy;
 		double fc_hz = thy v_frequencyToHertz (fc_mel);
 		double fl_hz = thy v_frequencyToHertz (fc_mel - thy dy);
@@ -180,7 +180,7 @@ static void Sound_into_MelSpectrogram_frame (Sound me, MelSpectrogram thee, inte
 			double a = NUMtriangularfilter_amplitude (fl_hz, fc_hz, fh_hz, f);
 			power += a * his z [1] [i];
 		}
-		thy z [ifilter] [frame] = power;
+		thy z [ifilter] [frame] = double (power);
 	}
 }
 
@@ -195,8 +195,10 @@ autoMelSpectrogram Sound_to_MelSpectrogram (Sound me, double analysisWidth, doub
 
 		if (fmax_mel <= 0.0 || fmax_mel > fceiling)
 			fmax_mel = fceiling;
-		if (fmax_mel <= f1_mel)
-			f1_mel = fbottom; fmax_mel = fceiling;
+		if (fmax_mel <= f1_mel) {
+			f1_mel = fbottom;
+			fmax_mel = fceiling;
+		}
 		if (f1_mel <= 0.0)
 			f1_mel = fbottom;
 		if (df_mel <= 0.0)


=====================================
dwtools/praat_David_init.cpp
=====================================
@@ -4072,6 +4072,12 @@ DO
 	NUMBER_ONE_END (U"")
 }
 
+DIRECT (NUMMAT_PatternList_getAllValues) {
+	NUMMAT_ONE (PatternList)
+		autoMAT result = newMATcopy (my z.all());
+	NUMMAT_ONE_END
+}
+
 FORM (MODIFY_PatternList_formula, U"PatternList: Formula", nullptr) {
 	LABEL (U"# `col` is the node number, `row` is the pattern number")
 	LABEL (U"for row from 1 to nrow   ; for all patterns")
@@ -8159,6 +8165,7 @@ void praat_uvafon_David_init () {
 	praat_addAction1 (classPatternList, 0, MODIFY_BUTTON, nullptr, 0, 0);
 	praat_addAction1 (classPatternList, 0, U"Formula...", nullptr, 1, MODIFY_PatternList_formula);
 	praat_addAction1 (classPatternList, 0, U"Set value...", nullptr, 1, MODIFY_PatternList_setValue);
+	praat_addAction1 (classPatternList, 0, U"Get all values", nullptr, 0, NUMMAT_PatternList_getAllValues);
 	praat_addAction1 (classPatternList, 0, U"To Matrix", nullptr, 0, NEW_PatternList_to_Matrix);
 
 	praat_addAction2 (classPatternList, 1, classCategories, 1, U"To TableOfReal", nullptr, 0, NEW1_Matrix_Categories_to_TableOfReal);


=====================================
fon/Makefile
=====================================
@@ -1,5 +1,5 @@
 # Makefile of the library "fon"
-# Paul Boersma, 10 August 2018
+# Paul Boersma, 28 February 2019
 
 include ../makefile.defs
 
@@ -8,7 +8,7 @@ CPPFLAGS = -I ../kar -I ../melder -I ../sys -I ../dwsys -I ../stat -I ../dwtools
 OBJECTS = Transition.o Distributions_and_Transition.o \
    Function.o Sampled.o SampledXY.o Matrix.o Vector.o Polygon.o PointProcess.o \
    Matrix_and_PointProcess.o Matrix_and_Polygon.o AnyTier.o RealTier.o \
-   Sound.o LongSound.o Sound_files.o Sound_audio.o PointProcess_and_Sound.o Sound_PointProcess.o ParamCurve.o \
+   Sound.o LongSound.o SoundSet.o Sound_files.o Sound_audio.o PointProcess_and_Sound.o Sound_PointProcess.o ParamCurve.o \
    Pitch.o Harmonicity.o Intensity.o Matrix_and_Pitch.o Sound_to_Pitch.o \
    Sound_to_Intensity.o Sound_to_Harmonicity.o Sound_to_Harmonicity_GNE.o Sound_to_PointProcess.o \
    Pitch_to_PointProcess.o Pitch_to_Sound.o Pitch_Intensity.o \


=====================================
fon/Pitch.cpp
=====================================
@@ -926,4 +926,40 @@ void Pitch_step (Pitch me, double step, double precision, double tmin, double tm
 	}
 }
 
+static autoTable Pitch_Frame_tabulateCandidates (Pitch_Frame me) {
+	autoTable you = Table_createWithColumnNames (my nCandidates, U"frequency strength");
+	for (integer icand = 1; icand <= my nCandidates; icand ++) {
+		Pitch_Candidate candidate = & my candidate [icand];
+		Table_setNumericValue (you.get(), icand, 1, candidate -> frequency);
+		Table_setNumericValue (you.get(), icand, 2, candidate -> strength);
+	}
+	return you;
+}
+
+autoTable Pitch_tabulateCandidatesInFrame (Pitch me, integer frameNumber) {
+	Pitch_checkFrameNumber (me, frameNumber);
+	Pitch_Frame frame = & my frame [frameNumber];
+	return Pitch_Frame_tabulateCandidates (frame);
+}
+
+autoTable Pitch_tabulateCandidates (Pitch me) {
+	integer totalNumberOfCandidates = 0;
+	for (integer iframe = 1; iframe <= my nx; iframe ++) {
+		Pitch_Frame frame = & my frame [iframe];
+		totalNumberOfCandidates += frame -> nCandidates;
+	}
+	autoTable result = Table_createWithColumnNames(totalNumberOfCandidates, U"frame frequency strength");
+	integer rowNumber = 0;
+	for (integer iframe = 1; iframe <= my nx; iframe ++) {
+		Pitch_Frame frame = & my frame [iframe];
+		for (integer icand = 1; icand <= frame -> nCandidates; icand ++) {
+			Pitch_Candidate candidate = & frame -> candidate [icand];
+			Table_setNumericValue (result.get(), ++ rowNumber, 1, double (iframe));
+			Table_setNumericValue (result.get(), rowNumber, 2, candidate -> frequency);
+			Table_setNumericValue (result.get(), rowNumber, 3, candidate -> strength);
+		}
+	}
+	return result;
+}
+
 /* End of file Pitch.cpp */


=====================================
fon/Pitch.h
=====================================
@@ -2,7 +2,7 @@
 #define _Pitch_h_
 /* Pitch.h
  *
- * Copyright (C) 1992-2007,2009,2011,2012,2014-2018 Paul Boersma
+ * Copyright (C) 1992-2007,2009,2011,2012,2014-2019 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
@@ -20,6 +20,7 @@
 
 #include "Sampled.h"
 #include "Graphics.h"
+#include "Table.h"
 Thing_declare (Interpreter);
 
 #include "Pitch_enums.h"
@@ -203,5 +204,8 @@ void Pitch_step (Pitch me, double step, double precision, double tmin, double tm
 
 void Pitch_formula (Pitch me, conststring32 formula, Interpreter interpreter);
 
+autoTable Pitch_tabulateCandidatesInFrame (Pitch me, integer frameNumber);
+autoTable Pitch_tabulateCandidates (Pitch me);
+
 /* End of file Pitch.h */
 #endif


=====================================
fon/Sound.cpp
=====================================
@@ -1273,34 +1273,4 @@ autoSound Sounds_crossCorrelate_short (Sound me, Sound thee, double tmin, double
 
 Thing_implement (SoundList, Ordered, 0);
 
-integer SoundList_getMinimumNumberOfSamples (SoundList me) {
-	integer result = INTEGER_MAX;
-	for (integer isound = 1; isound <= my size; isound ++)
-		if (my at [isound] -> nx < result)
-			result = my at [isound] -> nx;
-	return result;
-}
-
-autoMAT SoundList_getRandomizedPatterns (SoundList me, integer numberOfPatterns, integer patternSize) {
-	try {
-		integer minimumNumberOfSamples = SoundList_getMinimumNumberOfSamples (me);
-		Melder_require (patternSize <= minimumNumberOfSamples,
-			U"The pattern size cannot be ", patternSize, U", because there is a Sound that is only ", minimumNumberOfSamples, U" samples long.");
-		autoMAT result = newMATzero (numberOfPatterns, patternSize);
-		for (integer ipattern = 1; ipattern <= numberOfPatterns; ipattern ++) {
-			integer soundNumber = NUMrandomInteger (1, my size);
-			Sound sound = my at [soundNumber];
-			integer numberOfSamples = sound -> nx;
-			integer endSample = NUMrandomInteger (patternSize, numberOfSamples);
-			integer startSample = endSample - (patternSize - 1);
-			Melder_assert (startSample >= 1);
-			constVEC const samples = sound -> z.row (1);
-			result.row (ipattern) <<= samples. part (startSample, endSample);
-		}
-		return result;
-	} catch (MelderError) {
-		Melder_throw (me, U": no randomize patterns gotten.");
-	}
-}
-
 /* End of file Sound.cpp */


=====================================
fon/Sound.h
=====================================
@@ -346,8 +346,5 @@ autoSound Sound_deepenBandModulation (Sound me, double enhancement_dB,
 Collection_define (SoundList, OrderedOf, Sound) {
 };
 
-integer SoundList_getMinimumNumberOfSamples (SoundList me);
-autoMAT SoundList_getRandomizedPatterns (SoundList me, integer numberOfPatterns, integer patternSize);
-
 /* End of file Sound.h */
 #endif


=====================================
fon/SoundSet.cpp
=====================================
@@ -0,0 +1,87 @@
+/* SoundSet.cpp
+ *
+ * Copyright (C) 2019 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
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "SoundSet.h"
+
+Thing_implement (SoundSet, Ordered, 0);
+
+integer SoundSet_getMinimumNumberOfSamples (SoundSet me) {
+	integer result = INTEGER_MAX;
+	for (integer isound = 1; isound <= my size; isound ++)
+		if (my at [isound] -> nx < result)
+			result = my at [isound] -> nx;
+	return result;
+}
+
+autoMAT SoundSet_getRandomizedPatterns (SoundSet me, integer numberOfPatterns, integer patternSize) {
+	try {
+		integer minimumNumberOfSamples = SoundSet_getMinimumNumberOfSamples (me);
+		Melder_require (patternSize <= minimumNumberOfSamples,
+			U"The pattern size cannot be ", patternSize, U", because there is a Sound that is only ", minimumNumberOfSamples, U" samples long.");
+		autoMAT result = newMATzero (numberOfPatterns, patternSize);
+		for (integer ipattern = 1; ipattern <= numberOfPatterns; ipattern ++) {
+			integer soundNumber = NUMrandomInteger (1, my size);
+			Sound sound = my at [soundNumber];
+			integer numberOfSamples = sound -> nx;
+			integer endSample = NUMrandomInteger (patternSize, numberOfSamples);
+			integer startSample = endSample - (patternSize - 1);
+			Melder_assert (startSample >= 1);
+			constVEC const samples = sound -> z.row (1);
+			result.row (ipattern) <<= samples. part (startSample, endSample);
+		}
+		return result;
+	} catch (MelderError) {
+		Melder_throw (me, U": no randomize patterns gotten.");
+	}
+}
+
+void SoundSet_Table_getRandomizedPatterns (SoundSet me, Table thee, conststring32 columnName, integer numberOfPatterns, integer inputSize, integer outputSize,
+	autoPatternList *out_inputs, autoPatternList *out_outputs)
+{
+	try {
+		Melder_require (thy rows.size == my size,
+			U"The number of rows of ", thee, U" should be equal to the number of elements of ", me);
+		integer columnNumber = Table_getColumnIndexFromColumnLabel (thee, columnName);
+		integer minimumNumberOfSamples = SoundSet_getMinimumNumberOfSamples (me);
+		Melder_require (inputSize <= minimumNumberOfSamples,
+			U"The input size cannot be ", inputSize, U", because there is a Sound that is only ", minimumNumberOfSamples, U" samples long.");
+		Table_numericize_Assert (thee, columnNumber);
+		autoPatternList inputs = PatternList_create (numberOfPatterns, inputSize);
+		autoPatternList outputs = PatternList_create (numberOfPatterns, outputSize);
+		for (integer ipattern = 1; ipattern <= numberOfPatterns; ipattern ++) {
+			integer soundNumber = NUMrandomInteger (1, my size);
+			Sound sound = my at [soundNumber];
+			integer numberOfSamples = sound -> nx;
+			integer endSample = NUMrandomInteger (inputSize, numberOfSamples);
+			integer startSample = endSample - (inputSize - 1);
+			Melder_assert (startSample >= 1);
+			constVEC const samples = sound -> z.row (1);
+			inputs -> z.row (ipattern) <<= samples. part (startSample, endSample);
+			integer classNumber = Melder_iround (thy rows.at [soundNumber] -> cells [columnNumber]. number);
+			Melder_require (classNumber >= 1 && classNumber <= outputSize,
+				U"The class number has to be betwene 1 and ", outputSize, U", not ", classNumber, U".");
+			outputs -> z [ipattern] [classNumber] = 1.0;
+		}
+		if (out_inputs) *out_inputs = inputs.move();
+		if (out_outputs) *out_outputs = outputs.move();
+	} catch (MelderError) {
+		Melder_throw (me, U" and ", thee, U": no randomized patterns gotten.");
+	}
+}
+
+/* End of file SoundSet.cpp */


=====================================
fon/SoundSet.h
=====================================
@@ -0,0 +1,37 @@
+#ifndef _SoundSet_h_
+#define _SoundSet_h_
+/* SoundSet.h
+ *
+ * Copyright (C) 2019 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
+ * the Free Software Foundation; either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this work. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Sound.h"
+#include "Table.h"
+#include "PatternList.h"
+
+/*
+	Abstract.
+*/
+Collection_define (SoundSet, OrderedOf, Sound) {
+};
+
+integer SoundSet_getMinimumNumberOfSamples (SoundSet me);
+autoMAT SoundSet_getRandomizedPatterns (SoundSet me, integer numberOfPatterns, integer patternSize);
+void SoundSet_Table_getRandomizedPatterns (SoundSet me, Table thee, conststring32 columnName, integer numberOfPatterns, integer inputSize, integer outputSize,
+	autoPatternList *out_inputs, autoPatternList *out_outputs);
+
+/* End of file Sound.h */
+#endif


=====================================
fon/manual_tutorials.cpp
=====================================
@@ -22,9 +22,14 @@
 void manual_tutorials_init (ManPages me);
 void manual_tutorials_init (ManPages me) {
 
-MAN_BEGIN (U"What's new?", U"ppgb", 20190217)
+MAN_BEGIN (U"What's new?", U"ppgb", 20190302)
 INTRO (U"Latest changes in Praat.")
 //LIST_ITEM (U"• Manual page about @@drawing a vowel triangle at .")
+NORMAL (U"##6.0.49# (2 March 2019)")
+LIST_ITEM (U"• Removed a bug introduced in 6.0.41 whereby a script could misreport an undefined table value.")
+LIST_ITEM (U"• Removed a bug introduced in 6.0.44 whereby an MFCC's maximum frequency could be ignored.")
+LIST_ITEM (U"• Pitch: Tabulate candidates.")
+LIST_ITEM (U"• SoundSet.")
 NORMAL (U"##6.0.48# (17 February 2019)")
 LIST_ITEM (U"• Removed a bug introduced in 6.0.44 whereby Praat could crash when drawing a function without any points.")
 LIST_ITEM (U"• Removed a bug whereby Praat would not start up on macOS 10.10 (because of required GPU libraries).")


=====================================
fon/praat_Fon.cpp
=====================================
@@ -1559,6 +1559,21 @@ DO
 	NUMMAT_ONE_END
 }
 
+FORM (NEW_Pitch_tabulateCandidatesInFrame, U"Pitch: Tabulate candidates in frame", nullptr) {
+	NATURAL (frameNumber, U"Frame number", U"1")
+	OK
+DO
+	CONVERT_EACH (Pitch)
+		autoTable result = Pitch_tabulateCandidatesInFrame (me, frameNumber);
+	CONVERT_EACH_END (my name.get(), U"_", frameNumber)
+}
+
+DIRECT (NEW_Pitch_tabulateCandidates) {
+	CONVERT_EACH (Pitch)
+		autoTable result = Pitch_tabulateCandidates (me);
+	CONVERT_EACH_END (my name.get())
+}
+
 FORM (REAL_Pitch_getMinimum, U"Pitch: Get minimum", nullptr) {
 	praat_TimeFunction_RANGE (fromTime, toTime)
 	OPTIONMENU_ENUM (kPitch_unit, unit, U"Unit", kPitch_unit::DEFAULT)
@@ -3211,6 +3226,8 @@ praat_addAction1 (classFormant, 0, U"Hack", nullptr, 0, nullptr);
 		praat_addAction1 (classPitch, 2, U"Count differences", nullptr, 1, INFO_Pitch_difference);
 		praat_addAction1 (classPitch, 2, U"-- hack --", nullptr, 1, nullptr);
 		praat_addAction1 (classPitch, 1, U"Internal", nullptr, 1, nullptr);
+			praat_addAction1 (classPitch, 0, U"Tabulate candidates", nullptr, 2, NEW_Pitch_tabulateCandidates);
+			praat_addAction1 (classPitch, 0, U"Tabulate candidates in frame...", nullptr, 2, NEW_Pitch_tabulateCandidatesInFrame);
 			praat_addAction1 (classPitch, 1, U"Get all candidates in frame...", nullptr, 2, NUMMAT_Pitch_getAllCandidatesInFrame);
 	praat_addAction1 (classPitch, 0, U"Modify -", nullptr, 0, nullptr);
 		praat_TimeFunction_modify_init (classPitch);


=====================================
fon/praat_Matrix.cpp
=====================================
@@ -355,6 +355,23 @@ DIRECT (REAL_Matrix_getSum) {
 	NUMBER_ONE_END (U" (sum)");
 }
 
+DIRECT (NUMMAT_Matrix_getAllValues) {
+	NUMMAT_ONE (Matrix)
+		autoMAT result = newMATcopy (my z.all());
+	NUMMAT_ONE_END
+}
+
+FORM (NUMVEC_Matrix_getAllValuesInRow, U"Get all values in row", nullptr) {
+	NATURAL (rowNumber, U"Row number", U"1")
+	OK
+DO
+	NUMVEC_ONE (Matrix)
+		Melder_require (rowNumber <= my ny,
+			U"The row number (", rowNumber, U") should not be greater than the number of rows (", my ny, U").");
+		autoVEC result = newVECcopy (my z.row (rowNumber));
+	NUMVEC_ONE_END
+}
+
 // MARK: Modify
 
 FORM (MODIFY_Matrix_formula, U"Matrix Formula", U"Formula...") {
@@ -829,6 +846,8 @@ void praat_Matrix_init () {
 		praat_addAction1 (classMatrix, 1, U"-- get value --", nullptr, 1, nullptr);
 		praat_addAction1 (classMatrix, 1, U"Get value in cell...", nullptr, 1, REAL_Matrix_getValueInCell);
 		praat_addAction1 (classMatrix, 1, U"Get value at xy...", nullptr, 1, REAL_Matrix_getValueAtXY);
+		praat_addAction1 (classMatrix, 1, U"Get all values", nullptr, 1, NUMMAT_Matrix_getAllValues);
+		praat_addAction1 (classMatrix, 1, U"Get all values in row...", nullptr, 1, NUMVEC_Matrix_getAllValuesInRow);
 		praat_addAction1 (classMatrix, 1, U"Get minimum", nullptr, 1, REAL_Matrix_getMinimum);
 		praat_addAction1 (classMatrix, 1, U"Get maximum", nullptr, 1, REAL_Matrix_getMaximum);
 		praat_addAction1 (classMatrix, 1, U"Get sum", nullptr, 1, REAL_Matrix_getSum);


=====================================
fon/praat_Sound.cpp
=====================================
@@ -1,6 +1,6 @@
 /* praat_Sound_init.cpp
  *
- * Copyright (C) 1992-2018 Paul Boersma
+ * Copyright (C) 1992-2019 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
@@ -31,6 +31,7 @@
 #include "Sound_to_PointProcess.h"
 #include "SoundEditor.h"
 #include "SoundRecorder.h"
+#include "SoundSet.h"
 #include "SpectrumEditor.h"
 #include "TextGrid_Sound.h"
 #include "mp3.h"
@@ -360,6 +361,14 @@ DIRECT (NEW1_Sounds_combineIntoSoundList) {
 		autoSoundList result = SoundList_create ();
 		for (integer iobject = 1; iobject <= list.size; iobject ++)
 			result -> addItem_move (Data_copy (list.at [iobject]));
+	CONVERT_LIST_END (U"list")
+}
+
+DIRECT (NEW1_Sounds_combineIntoSoundSet) {
+	CONVERT_LIST (Sound)
+		autoSoundSet result = SoundSet_create ();
+		for (integer iobject = 1; iobject <= list.size; iobject ++)
+			result -> addItem_move (Data_copy (list.at [iobject]));
 	CONVERT_LIST_END (U"ensemble")
 }
 
@@ -2000,13 +2009,38 @@ FORM_SAVE (SAVE_Sound_saveAsWavFile, U"Save as WAV file", nullptr, U"wav") {
 
 /***** SOUNDLIST *****/
 
-DIRECT (NEWMANY_Extract_all_Sounds) {
+DIRECT (NEWMANY_SoundList_extractAllSounds) {
 	CONVERT_EACH (SoundList)
 		autoSoundList result = Data_copy (me);
 		result -> classInfo = classCollection;   // YUCK, in order to force automatic unpacking
 	CONVERT_EACH_END (U"dummy")
 }
 
+/***** SOUNDSET *****/
+
+DIRECT (NEWMANY_SoundSet_extractAllSounds) {
+	CONVERT_EACH (SoundSet)
+		autoSoundSet result = Data_copy (me);
+		result -> classInfo = classCollection;   // YUCK, in order to force automatic unpacking
+	CONVERT_EACH_END (U"dummy")
+}
+
+FORM (NEW2_SoundSet_Table_getRandomizedPatterns, U"SoundSet & Table: Get randomized patterns", nullptr) {
+	SENTENCE (columnName, U"Column name", U"")
+	NATURAL (numberOfPatterns, U"Number of patterns", U"1000")
+	NATURAL (inputSize, U"Input size (number of samples)", U"8000")
+	NATURAL (outputSize, U"Output size (number of classes)", U"5")
+	OK
+DO
+	FIND_TWO (SoundSet, Table)
+		autoPatternList inputs, outputs;
+		SoundSet_Table_getRandomizedPatterns (me, you, columnName, numberOfPatterns, inputSize, outputSize,
+			& inputs, & outputs);
+		praat_new (inputs.move(), U"inputs");
+		praat_new (outputs.move(), U"outputs");
+	END
+}
+
 /***** STOP *****/
 
 DIRECT (PLAY_stopPlayingSound) {
@@ -2112,7 +2146,7 @@ static int publishPlayedProc () {
 /***** buttons *****/
 
 void praat_Sound_init () {
-	Thing_recognizeClassesByName (classSound, classLongSound, classSoundList, nullptr);
+	Thing_recognizeClassesByName (classSound, classLongSound, classSoundList, classSoundSet, nullptr);
 
 	Data_recognizeFileType (macSoundOrEmptyFileRecognizer);
 	Data_recognizeFileType (soundFileRecognizer);
@@ -2407,6 +2441,7 @@ void praat_Sound_init () {
 	praat_addAction1 (classSound, 0, U"Combine -", nullptr, 0, nullptr);
 		praat_addAction1 (classSound, 0, U"Combine to stereo", nullptr, 1, NEW1_Sounds_combineToStereo);
 		praat_addAction1 (classSound, 0, U"Combine into SoundList", nullptr, 1, NEW1_Sounds_combineIntoSoundList);
+		praat_addAction1 (classSound, 0, U"Combine into SoundSet", nullptr, 1, NEW1_Sounds_combineIntoSoundSet);
 		praat_addAction1 (classSound, 0, U"Concatenate", nullptr, 1, NEW1_Sounds_concatenate);
 		praat_addAction1 (classSound, 0, U"Concatenate recoverably", nullptr, 1, NEW2_Sounds_concatenateRecoverably);
 		praat_addAction1 (classSound, 0, U"Concatenate with overlap...", nullptr, 1, NEW1_Sounds_concatenateWithOverlap);
@@ -2428,7 +2463,10 @@ void praat_Sound_init () {
 	praat_addAction2 (classLongSound, 0, classSound, 0, U"Save as FLAC file...", nullptr, 0, SAVE_LongSound_Sound_saveAsFlacFile);
 	praat_addAction2 (classLongSound, 0, classSound, 0,   U"Write to FLAC file...", U"*Save as FLAC file...", praat_DEPRECATED_2011, SAVE_LongSound_Sound_saveAsFlacFile);
 
-	praat_addAction1 (classSoundList, 1, U"Extract all Sounds", nullptr, 0, NEWMANY_Extract_all_Sounds);
+	praat_addAction1 (classSoundList, 1, U"Extract all Sounds", nullptr, 0, NEWMANY_SoundList_extractAllSounds);
+
+	praat_addAction1 (classSoundSet, 1, U"Extract all Sounds", nullptr, 0, NEWMANY_SoundSet_extractAllSounds);
+	praat_addAction2 (classSoundSet, 1, classTable, 1, U"Get randomized patterns...", nullptr, 0, NEW2_SoundSet_Table_getRandomizedPatterns);
 }
 
 /* End of file praat_Sound.cpp */


=====================================
main/praat.plist
=====================================
@@ -492,6 +492,18 @@
 			<key>LSTypeIsPackage</key>
 			<integer>0</integer>
 		</dict>
+		<dict>
+			<key>CFBundleTypeExtensions</key>
+			<array>
+				<string>SoundSet</string>
+			</array>
+			<key>CFBundleTypeName</key>
+			<string>Praat Sound set</string>
+			<key>CFBundleTypeRole</key>
+			<string>Editor</string>
+			<key>LSTypeIsPackage</key>
+			<integer>0</integer>
+		</dict>
 	</array>
 	<key>CFBundleExecutable</key>
 	<string>Praat</string>


=====================================
melder/MAT.h
=====================================
@@ -138,6 +138,44 @@ inline autoMAT newMATmultiply (constMATVU const& x, double number) {
 	return result;
 }
 
+struct TypeMATadd_MAT_VEC          { constMATVU const& x; constVECVU const& y; };
+inline TypeMATadd_MAT_VEC operator+ (constMATVU const& x, constVECVU const& y) { return { x, y }; }
+#define GENERATE_ONE_TENSOR_FUNCTION(operator, op)  \
+	inline void operator (MATVU const& target, TypeMATadd_MAT_VEC const& expr) noexcept { \
+		Melder_assert (expr.x.nrow == target.nrow); \
+		Melder_assert (expr.x.ncol == target.ncol); \
+		Melder_assert (expr.x.ncol == expr.y.size); \
+		for (integer irow = 1; irow <= expr.x.nrow; irow ++) \
+			for (integer icol = 1; icol <= expr.x.ncol; icol ++) \
+				target [irow] [icol] op expr.x [irow] [icol] + expr.y [icol]; \
+	}
+GENERATE_FIVE_TENSOR_FUNCTIONS
+#undef GENERATE_ONE_TENSOR_FUNCTION
+inline autoMAT newMATadd (constMATVU const& x, constVECVU const& y) {
+	autoMAT result = newMATraw (x.nrow, x.ncol);
+	result.all() <<= x  +  y;
+	return result;
+}
+
+struct TypeMATmultiply_MAT_VEC          { constMATVU const& x; constVECVU const& y; };
+inline TypeMATmultiply_MAT_VEC operator* (constMATVU const& x, constVECVU const& y) { return { x, y }; }
+#define GENERATE_ONE_TENSOR_FUNCTION(operator, op)  \
+	inline void operator (MATVU const& target, TypeMATmultiply_MAT_VEC const& expr) noexcept { \
+		Melder_assert (expr.x.nrow == target.nrow); \
+		Melder_assert (expr.x.ncol == target.ncol); \
+		Melder_assert (expr.x.ncol == expr.y.size); \
+		for (integer irow = 1; irow <= expr.x.nrow; irow ++) \
+			for (integer icol = 1; icol <= expr.x.ncol; icol ++) \
+				target [irow] [icol] op expr.x [irow] [icol] * expr.y [icol]; \
+	}
+GENERATE_FIVE_TENSOR_FUNCTIONS
+#undef GENERATE_ONE_TENSOR_FUNCTION
+inline autoMAT newMATmultiply (constMATVU const& x, constVECVU const& y) {
+	autoMAT result = newMATraw (x.nrow, x.ncol);
+	result.all() <<= x  *  y;
+	return result;
+}
+
 struct TypeMATadd_MAT_MAT          { constMATVU const& x; constMATVU const& y; };
 inline TypeMATadd_MAT_MAT operator+ (constMATVU const& x, constMATVU const& y) { return { x, y }; }
 #define GENERATE_ONE_TENSOR_FUNCTION(operator, op)  \
@@ -178,6 +216,26 @@ inline autoMAT newMATsubtract (constMATVU const& x, constMATVU const& y) {
 	return result;
 }
 
+struct TypeMATmultiply_MAT_MAT          { constMATVU const& x; constMATVU const& y; };
+inline TypeMATmultiply_MAT_MAT operator* (constMATVU const& x, constMATVU const& y) { return { x, y }; }
+#define GENERATE_ONE_TENSOR_FUNCTION(operator, op)  \
+	inline void operator (MATVU const& target, TypeMATmultiply_MAT_MAT const& expr) noexcept { \
+		Melder_assert (expr.x.nrow == target.nrow); \
+		Melder_assert (expr.x.ncol == target.ncol); \
+		Melder_assert (expr.x.nrow == expr.y.nrow); \
+		Melder_assert (expr.x.ncol == expr.y.ncol); \
+		for (integer irow = 1; irow <= expr.x.nrow; irow ++) \
+			for (integer icol = 1; icol <= expr.x.ncol; icol ++) \
+				target [irow] [icol] op expr.x [irow] [icol] * expr.y [irow] [icol]; \
+	}
+GENERATE_FIVE_TENSOR_FUNCTIONS
+#undef GENERATE_ONE_TENSOR_FUNCTION
+inline autoMAT newMATmultiply (constMATVU const& x, constMATVU const& y) {
+	autoMAT result = newMATraw (x.nrow, x.ncol);
+	result.all() <<= x  *  y;
+	return result;
+}
+
 /*
 	Make the average of each column zero.
 		a[i][j] -= a[.][j]


=====================================
melder/MelderString.h
=====================================
@@ -2,7 +2,7 @@
 #define _melder_string_h_
 /* MelderString.h
  *
- * Copyright (C) 1992-2018 Paul Boersma
+ * Copyright (C) 1992-2019 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
@@ -18,9 +18,14 @@
  * along with this work. If not, see <http://www.gnu.org/licenses/>.
  */
 
-/********** STRINGS **********/
-
-/* These are functions for never having to check string boundaries again. */
+/*
+	Strings that:
+		- are null-terminated
+		- have O(1) access to their length
+		- grow as needed
+		- can be appended to without scanning for the final null character
+		- automatically convert numbers, objects, file names, vectors, and matrices to strings
+*/
 
 typedef struct {
 	int64 length;
@@ -30,7 +35,7 @@ typedef struct {
 typedef struct {
 	int64 length;
 	int64 bufferSize;
-	char32 *string;   // a growing buffer, rarely shrunk (can only be freed by MelderString32_free)
+	char32 *string;   // a growing buffer, rarely shrunk (can only be freed by MelderString_free)
 } MelderString;
 
 void MelderString16_free (MelderString16 *me);   // frees the buffer (and sets other attributes to zero)
@@ -42,8 +47,14 @@ void MelderString_ncopy (MelderString *me, conststring32 source, int64 n);
 
 inline static void _recursiveTemplate_MelderString_append (MelderString *me, const MelderArg& arg) {
 	if (arg._arg) {
-		const char32 *newEndOfStringLocation = stp32cpy (& my string [my length], arg._arg);
+		const char32 *newEndOfStringLocation = stp32cpy (& my string [my length], arg._arg);   // this will append a null character
 		my length = newEndOfStringLocation - & my string [0];
+	} else {
+		/*
+			Append a null string: do nothing.
+			The result will be null-terminated if `me` was null-terminated to start with,
+			which is a required invariant.
+		*/
 	}
 }
 template <typename... Args>
@@ -70,6 +81,7 @@ void MelderString_copy (MelderString *me, const MelderArg& first, Args... rest)
 	if (sizeNeeded > my bufferSize)
 		MelderString_expand (me, sizeNeeded);
 	my length = 0;
+	my string [0] = U'\0';   // maintain invariant
 	_recursiveTemplate_MelderString_append (me, first, rest...);
 }
 


=====================================
melder/melder_strings.cpp
=====================================
@@ -111,7 +111,7 @@ void MelderString_empty (MelderString *me) {
 	}
 	int64 sizeNeeded = 1;
 	if (sizeNeeded > my bufferSize) MelderString_expand (me, sizeNeeded);
-	my string [0] = '\0';
+	my string [0] = U'\0';
 	my length = 0;
 }
 
@@ -124,7 +124,7 @@ void MelderString_ncopy (MelderString *me, conststring32 source, int64 n) {
 	int64 sizeNeeded = length + 1;
 	if (sizeNeeded > my bufferSize) MelderString_expand (me, sizeNeeded);
 	str32ncpy (my string, source, length);
-	my string [length] = '\0';
+	my string [length] = U'\0';
 	my length = length;
 }
 


=====================================
sys/Formula.cpp
=====================================
@@ -89,14 +89,15 @@ enum { NO_SYMBOL_,
 	/* Functions of 1 variable; if you add, update the #defines. */
 	#define LOW_FUNCTION_1  ABS_
 		ABS_, ROUND_, FLOOR_, CEILING_,
-		RECTIFY_, VEC_RECTIFY_,
+		RECTIFY_, RECTIFY_H_, RECTIFY_HH_,
 		SQRT_, SIN_, COS_, TAN_, ARCSIN_, ARCCOS_, ARCTAN_, SINC_, SINCPI_,
 		EXP_, VEC_EXP_, MAT_EXP_,
 		SINH_, COSH_, TANH_, ARCSINH_, ARCCOSH_, ARCTANH_,
-		SIGMOID_, VEC_SIGMOID_, VEC_SOFTMAX_,
+		SIGMOID_, VEC_SIGMOID_, SOFTMAX_H_, SOFTMAX_PER_ROW_HH_,
 		INV_SIGMOID_, ERF_, ERFC_, GAUSS_P_, GAUSS_Q_, INV_GAUSS_Q_,
 		RANDOM_BERNOULLI_, VEC_RANDOM_BERNOULLI_,
 		RANDOM_POISSON_, MAT_TRANSPOSE_,
+		SUM_PER_ROW_H_, SUM_PER_COLUMN_H_,
 		LOG2_, LN_, LOG10_, LN_GAMMA_,
 		HERTZ_TO_BARK_, BARK_TO_HERTZ_, PHON_TO_DIFFERENCE_LIMENS_, DIFFERENCE_LIMENS_TO_PHON_,
 		HERTZ_TO_MEL_, MEL_TO_HERTZ_, HERTZ_TO_SEMITONES_, SEMITONES_TO_HERTZ_,
@@ -218,14 +219,15 @@ static const conststring32 Formula_instructionNames [1 + highestSymbol] = { U"",
 	U"self", U"self$", U"object", U"object$", U"_matrix", U"_matrix$",
 	U"stopwatch",
 	U"abs", U"round", U"floor", U"ceiling",
-	U"rectify", U"rectify#",
+	U"rectify", U"rectify#", U"rectify##",
 	U"sqrt", U"sin", U"cos", U"tan", U"arcsin", U"arccos", U"arctan", U"sinc", U"sincpi",
 	U"exp", U"exp#", U"exp##",
 	U"sinh", U"cosh", U"tanh", U"arcsinh", U"arccosh", U"arctanh",
-	U"sigmoid", U"sigmoid#", U"softmax#",
+	U"sigmoid", U"sigmoid#", U"softmax#", U"softmaxPerRow##",
 	U"invSigmoid", U"erf", U"erfc", U"gaussP", U"gaussQ", U"invGaussQ",
 	U"randomBernoulli", U"randomBernoulli#",
 	U"randomPoisson", U"transpose##",
+	U"sumPerRow#", U"sumPerColumn#",
 	U"log2", U"ln", U"log10", U"lnGamma",
 	U"hertzToBark", U"barkToHertz", U"phonToDifferenceLimens", U"differenceLimensToPhon",
 	U"hertzToMel", U"melToHertz", U"hertzToSemitones", U"semitonesToHertz",
@@ -2638,6 +2640,26 @@ static void do_add () {
 			//x->which = Stackel_NUMERIC_MATRIX;
 			return;
 		}
+		if (y->which == Stackel_NUMERIC_VECTOR) {
+			/*
+				result## = x## + y#
+				i.e.
+				result## [i, j] = x## [i, j] + y# [j]
+			*/
+			integer xnrow = x->numericMatrix.nrow, xncol = x->numericMatrix.ncol;
+			integer ysize = y->numericVector.size;
+			if (xncol != ysize)
+				Melder_throw (U"When adding a vector to a matrix, its size should be equal to the number of columns, instead of ", ysize, U" and ", xncol, U".");
+			if (x->owned) {
+				x->numericMatrix  +=  y->numericVector;
+			} else {
+				// x does not have to be cleaned up, because it was not owned
+				x->numericMatrix = newMATadd (x->numericMatrix, y->numericVector). releaseToAmbiguousOwner();
+				x->owned = true;
+			}
+			//x->which = Stackel_NUMERIC_MATRIX;
+			return;
+		}
 		if (y->which == Stackel_NUMBER) {
 			/*
 				result## = x## + y
@@ -2800,15 +2822,20 @@ static void do_mul () {
 	/*
 		result.. = x.. * y..
 	*/
-	Stackel y = pop, x = pop;
+	Stackel y = pop, x = topOfStack;
 	if (x->which == Stackel_NUMBER) {
-		double xvalue = x->number;
 		if (y->which == Stackel_NUMBER) {
-			/*
+			/*@praat
+				#
+				# result = x * y
+				#
+				x = 5
+				y = 6
 				result = x * y
-			*/
-			double yvalue = y->number;
-			pushNumber (xvalue * yvalue);
+				assert result = 30
+			@*/
+			x->number *= y->number;
+			//x->which = Stackel_NUMBER;   // superfluous, as is cleaning up
 			return;
 		}
 		if (y->which == Stackel_NUMERIC_VECTOR) {
@@ -2816,19 +2843,30 @@ static void do_mul () {
 				result# = x * y#
 			*/
 			if (y->owned) {
-				y->numericVector  *=  xvalue;
-				x->which = Stackel_NUMERIC_VECTOR;
+				/*@praat
+					#
+					# result# = x * owned y#
+					#
+					result# = 5 * { 11, 13, 31 }   ; numeric vector literals are owned
+					assert result# = { 55, 65, 155 }
+				@*/
+				y->numericVector  *=  x->number;
+				// x does not have to be cleaned up, because it was a number
 				moveNumericVector (y, x);
-				w ++;
 			} else {
-				integer ny = y->numericVector.size;
-				autoVEC result { ny, kTensorInitializationType::RAW };
-				for (integer i = 1; i <= ny; i ++) {
-					double yvalue = y->numericVector [i];
-					result [i] = xvalue * yvalue;
-				}
-				pushNumericVector (result.move());
+				/*@praat
+					#
+					# result# = x * unowned y#
+					#
+					y# = { 17, -11, 29 }
+					result# = 30 * y#   ; numeric vector variables are not owned
+					assert result# = { 510, -330, 870 }
+				@*/
+				// x does not have to be cleaned up, because it was a number
+				x->numericVector = newVECmultiply (y->numericVector, x->number). releaseToAmbiguousOwner();
+				x->owned = true;
 			}
+			x->which = Stackel_NUMERIC_VECTOR;
 			return;
 		}
 		if (y->which == Stackel_NUMERIC_MATRIX) {
@@ -2836,39 +2874,159 @@ static void do_mul () {
 				result## = x * y##
 			*/
 			if (y->owned) {
-				y->numericMatrix  *=  xvalue;
-				x->which = Stackel_NUMERIC_MATRIX;
+				y->numericMatrix  *=  x->number;
+				// x does not have to be cleaned up, because it was a number
 				moveNumericMatrix (y, x);
-				w ++;
 			} else {
-				integer nrow = y->numericMatrix.nrow, ncol = y->numericMatrix.ncol;
-				autoMAT result (nrow, ncol, kTensorInitializationType::RAW);
-				for (integer irow = 1; irow <= nrow; irow ++) {
-					for (integer icol = 1; icol <= ncol; icol ++) {
-						double yvalue = y->numericMatrix [irow] [icol];
-						result [irow] [icol] = xvalue * yvalue;
-					}
-				}
-				pushNumericMatrix (result.move());
+				// x does not have to be cleaned up, because it was a number
+				x->numericMatrix = newMATmultiply (y->numericMatrix, x->number). releaseToAmbiguousOwner();
+				x->owned = true;
 			}
+			x->which = Stackel_NUMERIC_MATRIX;
 			return;
 		}
 	}
-	if (x->which == Stackel_NUMERIC_VECTOR && y->which == Stackel_NUMERIC_VECTOR) {
-		/*
-			result# = x# * y#
-		*/
-		integer nx = x->numericVector.size, ny = y->numericVector.size;
-		if (nx != ny)
-			Melder_throw (U"When multiplying vectors, their numbers of elements should be equal, instead of ", nx, U" and ", ny, U".");
-		autoVEC result { nx, kTensorInitializationType::RAW };
-		for (integer i = 1; i <= nx; i ++) {
-			double xvalue = x->numericVector [i];
-			double yvalue = y->numericVector [i];
-			result [i] = xvalue * yvalue;
+	if (x->which == Stackel_NUMERIC_VECTOR) {
+		if (y->which == Stackel_NUMERIC_VECTOR) {
+			/*
+				result# = x# * y#
+				i.e.
+				result# [i] = x# [i] * y# [i]
+			*/
+			integer nx = x->numericVector.size, ny = y->numericVector.size;
+			if (nx != ny) {
+				/*@praat
+					#
+					# Error: unequal sizes.
+					#
+					x# = { 11, 13, 17 }
+					y# = { 8, 90 }
+					asserterror When multiplying vectors, their numbers of elements should be equal, instead of 3 and 2.
+					result# = x# + y#
+				@*/
+				Melder_throw (U"When multiplying vectors, their numbers of elements should be equal, instead of ", nx, U" and ", ny, U".");
+			}
+			if (x -> owned) {
+				/*@praat
+					#
+					# result# = owned x# * y#
+					#
+					result# = { 11, 13, 17 } * { 44, 56, 67 }   ; owned + owned
+					assert result# = { 484, 728, 1139 }
+					y# = { 3, 2, 89.5 }
+					result# = { 11, 13, 17 } * y#   ; owned * unowned
+					assert result# = { 33, 26, 1521.5 }
+				@*/
+				x->numericVector  *=  y->numericVector;
+			} else if (y -> owned) {
+				/*@praat
+					#
+					# result# = unowned x# * owned y#
+					#
+					x# = { 14, -3, 6.25 }
+					result# = x# * { 55, 1, -89 }
+					assert result# = { 770, -3, -556.25 }
+				@*/
+				y->numericVector  *=  x->numericVector;
+				// x does not have to be cleaned up, because it was not owned
+				moveNumericVector (y, x);
+			} else {
+				/*@praat
+					#
+					# result# = unowned x# * unowned y#
+					#
+					x# = { 14, -33, 6.25 }
+					y# = { -33, 17, 9 }
+					result# = x# * y#
+					assert result# = { -462, -561, 56.25 }
+				@*/
+				// x does not have to be cleaned up, because it was not owned
+				x->numericVector = newVECmultiply (x->numericVector, y->numericVector). releaseToAmbiguousOwner();
+				x->owned = true;
+			}
+			//x->which = Stackel_NUMERIC_VECTOR;   // superfluous
+			return;
+		}
+		if (y->which == Stackel_NUMBER) {
+			/*
+				result# = x# * y
+				i.e.
+				result# [i] = x# [i] * y
+			*/
+			if (x->owned) {
+				x->numericVector  *=  y->number;
+			} else {
+				// x does not have to be cleaned up, because it was not owned
+				x->numericVector = newVECmultiply (x->numericVector, y->number). releaseToAmbiguousOwner();
+				x->owned = true;
+			}
+			//x->which = Stackel_NUMERIC_VECTOR;   // superfluous
+			return;
+		}
+	}
+	if (x->which == Stackel_NUMERIC_MATRIX) {
+		if (y->which == Stackel_NUMERIC_MATRIX) {
+			/*
+				result## = x## * y##
+				i.e.
+				result## [i, j] = x## [i, j] * y## [i, j]
+			*/
+			integer xnrow = x->numericMatrix.nrow, xncol = x->numericMatrix.ncol;
+			integer ynrow = y->numericMatrix.nrow, yncol = y->numericMatrix.ncol;
+			if (xnrow != ynrow)
+				Melder_throw (U"When multiplying matrices, their numbers of rows should be equal, instead of ", xnrow, U" and ", ynrow, U".");
+			if (xncol != yncol)
+				Melder_throw (U"When multiplying matrices, their numbers of columns should be equal, instead of ", xncol, U" and ", yncol, U".");
+			if (x->owned) {
+				x->numericMatrix  *=  y->numericMatrix;
+			} else if (y->owned) {
+				y->numericMatrix  *=  x->numericMatrix;
+				// x does not have to be cleaned up, because it was not owned
+				moveNumericMatrix (y, x);
+			} else {
+				// x does not have to be cleaned up, because it was not owned
+				x->numericMatrix = newMATmultiply (x->numericMatrix, y->numericMatrix). releaseToAmbiguousOwner();
+				x->owned = true;
+			}
+			//x->which = Stackel_NUMERIC_MATRIX;
+			return;
+		}
+		if (y->which == Stackel_NUMERIC_VECTOR) {
+			/*
+				result## = x## * y#
+				i.e.
+				result## [i, j] = x## [i, j] * y# [j]
+			*/
+			integer xnrow = x->numericMatrix.nrow, xncol = x->numericMatrix.ncol;
+			integer ysize = y->numericVector.size;
+			if (xncol != ysize)
+				Melder_throw (U"When multiplying a matrix with a vector, the vector’s size should be equal to the matrix’s number of columns, instead of ", ysize, U" and ", xncol, U".");
+			if (x->owned) {
+				x->numericMatrix  *=  y->numericVector;
+			} else {
+				// x does not have to be cleaned up, because it was not owned
+				x->numericMatrix = newMATmultiply (x->numericMatrix, y->numericVector). releaseToAmbiguousOwner();
+				x->owned = true;
+			}
+			//x->which = Stackel_NUMERIC_MATRIX;
+			return;
+		}
+		if (y->which == Stackel_NUMBER) {
+			/*
+				result## = x## * y
+				i.e.
+				result## [i, j] = x## [i, j] * y
+			*/
+			if (x->owned) {
+				x->numericMatrix  *=  y->number;
+			} else {
+				// x does not have to be cleaned up, because it was not owned
+				x->numericMatrix = newMATmultiply (x->numericMatrix, y->number). releaseToAmbiguousOwner();
+				x->owned = true;
+			}
+			//x->which = Stackel_NUMERIC_MATRIX;   // superfluous
+			return;
 		}
-		pushNumericVector (result.move());
-		return;
 	}
 	Melder_throw (U"Cannot multiply (*) ", x->whichText(), U" by ", y->whichText(), U".");
 }
@@ -2979,7 +3137,7 @@ static void do_functionvec_n_n (double (*f) (double)) {
 			U" requires a numeric vector argument, not ", x->whichText(), U".");
 	}
 }
-static void do_softmax () {
+static void do_softmaxH () {
 	Stackel x = topOfStack;
 	if (x->which == Stackel_NUMERIC_VECTOR) {
 		if (! x->owned) {
@@ -3006,6 +3164,35 @@ static void do_softmax () {
 			U" requires a numeric vector argument, not ", x->whichText(), U".");
 	}
 }
+static void do_softmaxPerRowHH () {
+	Stackel x = topOfStack;
+	if (x->which == Stackel_NUMERIC_MATRIX) {
+		if (! x->owned) {
+			x->numericMatrix = newMATcopy (x->numericMatrix). releaseToAmbiguousOwner();   // TODO: no need to copy
+			x->owned = true;
+		}
+		integer nrow = x->numericMatrix.nrow, ncol = x->numericMatrix.ncol;
+		for (integer irow = 1; irow <= nrow; irow ++) {
+			double maximum = -1e308;
+			for (integer icol = 1; icol <= ncol; icol ++) {
+				if (x->numericMatrix [irow] [icol] > maximum)
+					maximum = x->numericMatrix [irow] [icol];
+			}
+			for (integer icol = 1; icol <= ncol; icol ++)
+				x->numericMatrix [irow] [icol] -= maximum;
+			longdouble sum = 0.0;
+			for (integer icol = 1; icol <= ncol; icol ++) {
+				x->numericMatrix [irow] [icol] = exp (x->numericMatrix [irow] [icol]);
+				sum += x->numericMatrix [irow] [icol];
+			}
+			for (integer icol = 1; icol <= ncol; icol ++)
+				x->numericMatrix [irow] [icol] /= (double) sum;
+		}
+	} else {
+		Melder_throw (U"The function ", Formula_instructionNames [parse [programPointer]. symbol],
+			U" requires a numeric matrix argument, not ", x->whichText(), U".");
+	}
+}
 static void do_abs () {
 	Stackel x = pop;
 	if (x->which == Stackel_NUMBER) {
@@ -3046,7 +3233,7 @@ static void do_rectify () {
 		Melder_throw (U"Cannot rectify ", x->whichText(), U".");
 	}
 }
-static void do_VECrectify () {
+static void do_rectifyH () {
 	Stackel x = pop;
 	if (x->which == Stackel_NUMERIC_VECTOR) {
 		integer nelm = x->numericVector.size;
@@ -3060,6 +3247,33 @@ static void do_VECrectify () {
 		Melder_throw (U"Cannot rectify ", x->whichText(), U".");
 	}
 }
+static void do_rectifyHH () {
+	Stackel x = topOfStack;
+	if (x->which == Stackel_NUMERIC_MATRIX) {
+		if (x->owned) {
+			integer nrow = x->numericMatrix.nrow, ncol = x->numericMatrix.ncol;
+			for (integer irow = 1; irow <= nrow; irow ++) {
+				for (integer icol = 1; icol <= ncol; icol ++) {
+					double xvalue = x->numericMatrix [irow] [icol];
+					x->numericMatrix [irow] [icol] = isundef (xvalue) ? undefined : xvalue > 0.0 ? xvalue : 0.0;
+				}
+			}
+		} else {
+			pop;
+			integer nrow = x->numericMatrix.nrow, ncol = x->numericMatrix.ncol;
+			autoMAT result = newMATraw (nrow, ncol);
+			for (integer irow = 1; irow <= nrow; irow ++) {
+				for (integer icol = 1; icol <= ncol; icol ++) {
+					double xvalue = x->numericMatrix [irow] [icol];
+					result [irow] [icol] = isundef (xvalue) ? undefined : xvalue > 0.0 ? xvalue : 0.0;
+				}
+			}
+			pushNumericMatrix (result.move());
+		}
+	} else {
+		Melder_throw (U"Cannot rectify ", x->whichText(), U".");
+	}
+}
 static void do_sqrt () {
 	Stackel x = pop;
 	if (x->which == Stackel_NUMBER) {
@@ -3210,6 +3424,8 @@ static void do_sum () {
 	Stackel x = pop;
 	if (x->which == Stackel_NUMERIC_VECTOR) {
 		pushNumber (NUMsum (x->numericVector));
+	} else if (x->which == Stackel_NUMERIC_MATRIX) {
+		pushNumber (NUMsum (x->numericMatrix));
 	} else {
 		Melder_throw (U"Cannot compute the sum of ", x->whichText(), U".");
 	}
@@ -3218,6 +3434,8 @@ static void do_mean () {
 	Stackel x = pop;
 	if (x->which == Stackel_NUMERIC_VECTOR) {
 		pushNumber (NUMmean (x->numericVector));
+	//} else if (x->which == Stackel_NUMERIC_MATRIX) {
+	//	pushNumber (NUMmean (x->numericMatrix));
 	} else {
 		Melder_throw (U"Cannot compute the mean of ", x->whichText(), U".");
 	}
@@ -5107,7 +5325,7 @@ static void do_MATmul_metal () {
 		MATVUmul_forceMetal_ (result.get(), x->numericMatrix, y->numericMatrix);
 		pushNumericMatrix (result.move());
 	} else {
-		Melder_throw (U"The function \"mul##\" requires two matrices, not ", x->whichText(), U" and ", y->whichText(), U".");
+		Melder_throw (U"The function \"mul_metal##\" requires two matrices, not ", x->whichText(), U" and ", y->whichText(), U".");
 	}
 }
 static void do_MATmul_fast () {
@@ -5200,6 +5418,24 @@ static void do_MATtranspose () {
 		Melder_throw (U"The function \"transpose##\" requires a matrix, not ", x->whichText(), U".");
 	}
 }
+static void do_sumPerRowH () {
+	Stackel x = pop;
+	if (x->which == Stackel_NUMERIC_MATRIX) {
+		autoVEC result = newVECsumPerRow (x->numericMatrix);
+		pushNumericVector (result.move());
+	} else {
+		Melder_throw (U"The function \"sumPerRow#\" requires a matrix, not ", x->whichText(), U".");
+	}
+}
+static void do_sumPerColumnH () {
+	Stackel x = pop;
+	if (x->which == Stackel_NUMERIC_MATRIX) {
+		autoVEC result = newVECsumPerColumn (x->numericMatrix);
+		pushNumericVector (result.move());
+	} else {
+		Melder_throw (U"The function \"sumPerColumn#\" requires a matrix, not ", x->whichText(), U".");
+	}
+}
 static void do_VECrepeat () {
 	Stackel n = pop, x = pop;
 	if (x->which == Stackel_NUMERIC_VECTOR && n->which == Stackel_NUMBER) {
@@ -6299,7 +6535,8 @@ case NUMBER_: { pushNumber (f [programPointer]. content.number);
 } break; case FLOOR_: { do_floor ();
 } break; case CEILING_: { do_ceiling ();
 } break; case RECTIFY_: { do_rectify ();
-} break; case VEC_RECTIFY_: { do_VECrectify ();
+} break; case RECTIFY_H_: { do_rectifyH ();
+} break; case RECTIFY_HH_: { do_rectifyHH ();
 } break; case SQRT_: { do_sqrt ();
 } break; case SIN_: { do_sin ();
 } break; case COS_: { do_cos ();
@@ -6320,7 +6557,8 @@ case NUMBER_: { pushNumber (f [programPointer]. content.number);
 } break; case ARCTANH_: { do_function_n_n (NUMarctanh);
 } break; case SIGMOID_: { do_function_n_n (NUMsigmoid);
 } break; case VEC_SIGMOID_: { do_functionvec_n_n (NUMsigmoid);
-} break; case VEC_SOFTMAX_: { do_softmax ();
+} break; case SOFTMAX_H_: { do_softmaxH ();
+} break; case SOFTMAX_PER_ROW_HH_: { do_softmaxPerRowHH ();
 } break; case INV_SIGMOID_: { do_function_n_n (NUMinvSigmoid);
 } break; case ERF_: { do_function_n_n (NUMerf);
 } break; case ERFC_: { do_function_n_n (NUMerfcc);
@@ -6331,6 +6569,8 @@ case NUMBER_: { pushNumber (f [programPointer]. content.number);
 } break; case VEC_RANDOM_BERNOULLI_: { do_functionvec_n_n (NUMrandomBernoulli_real);
 } break; case RANDOM_POISSON_: { do_function_n_n (NUMrandomPoisson);
 } break; case MAT_TRANSPOSE_: { do_MATtranspose ();
+} break; case SUM_PER_ROW_H_: { do_sumPerRowH ();
+} break; case SUM_PER_COLUMN_H_: { do_sumPerColumnH ();
 } break; case LOG2_: { do_log2 ();
 } break; case LN_: { do_ln ();
 } break; case LOG10_: { do_log10 ();


=====================================
sys/praat_version.h
=====================================
@@ -1,5 +1,5 @@
-#define PRAAT_VERSION_STR 6.0.48
-#define PRAAT_VERSION_NUM 6048
+#define PRAAT_VERSION_STR 6.0.49
+#define PRAAT_VERSION_NUM 6049
 #define PRAAT_YEAR 2019
-#define PRAAT_MONTH February
-#define PRAAT_DAY 17
+#define PRAAT_MONTH March
+#define PRAAT_DAY 2


=====================================
test/stat/Table_undefined.praat
=====================================
@@ -0,0 +1,6 @@
+Create Table with column names: "table", 1, "speaker"
+;Set string value: 1, "speaker", ""
+a$ = Get value: 1, "speaker"
+assert a$ = ""   ; <<'a$'>>
+a = Get value: 1, "speaker"
+assert a = undefined   ; 'a'



View it on GitLab: https://salsa.debian.org/med-team/praat/compare/f1500bb1a4da8e593215adea7e3c758a1c8893bd...ca3058bee548e61b0e25fe6fd205305cf0316bb9

-- 
View it on GitLab: https://salsa.debian.org/med-team/praat/compare/f1500bb1a4da8e593215adea7e3c758a1c8893bd...ca3058bee548e61b0e25fe6fd205305cf0316bb9
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20190311/7d868dc1/attachment-0001.html>


More information about the debian-med-commit mailing list