/* praat_Sound_init.c
 *
 * Copyright (C) 1992-2003 Paul Boersma
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 * pb 2003/03/03
 */

#include "praat.h"

#include "FunctionEditor_Sound.h"
#include "FunctionEditor_SoundAnalysis.h"
#include "LongSound.h"
#include "Manipulation.h"
#include "ParamCurve.h"
#include "Sound_and_Spectrogram.h"
#include "Sound_and_Spectrum.h"
#include "Sound_and_Wavelet.h"
#include "Sound_extensions.h"
#include "Sound_to_Cochleagram.h"
#include "Sound_to_Formant.h"
#include "Sound_to_Harmonicity.h"
#include "Sound_to_Intensity.h"
#include "Sound_to_Pitch.h"
#include "Sound_to_PointProcess.h"
#include "SoundEditor.h"
#include "SoundRecorder.h"
#include "SpectrumEditor.h"
#include "TextGrid.h"

int praat_Fon_formula (Any dia);
void praat_TimeFunction_query_init (void *klas);

static int pr_LongSound_concatenate (MelderFile file, int audioFileType) {
	int IOBJECT;
	Ordered me = Ordered_create ();
	if (! me) return 0;
	WHERE (SELECTED)
		if (! Collection_addItem (me, OBJECT)) { my size = 0; forget (me); return 0; }
	if (! LongSound_concatenate (me, file, audioFileType)) {
		my size = 0; forget (me); return 0;
	}
	my size = 0; forget (me);
	return 1;
}

/***** LONGSOUND *****/

FORM (LongSound_extractPart, "Extract part", 0)
	REAL ("left Time range (s)", "0.0")
	REAL ("right Time range (s)", "1.0")
	BOOLEAN ("Preserve times", 1)
	OK
DO
	EVERY_TO (LongSound_extractPart (OBJECT, GET_REAL ("left Time range"), GET_REAL ("right Time range"), GET_INTEGER ("Preserve times")))
END

FORM (LongSound_getIndexFromTime, "LongSound: Get sample index", "Sound: Get index from time...")
	REAL ("Time (s)", "0.5")
	OK
DO
	Melder_informationReal (Sampled_xToIndex (ONLY (classLongSound), GET_REAL ("Time")), NULL);
END

DIRECT (LongSound_getSamplePeriod)
	LongSound me = ONLY (classLongSound);
	Melder_informationReal (my dx, "seconds");
END

DIRECT (LongSound_getSampleRate)
	LongSound me = ONLY (classLongSound);
	Melder_informationReal (1 / my dx, "Hertz");
END

FORM (LongSound_getTimeFromIndex, "Get time from sample index", "Sound: Get time from index...")
	INTEGER ("Sample index", "100")
	OK
DO
	Melder_informationReal (Sampled_indexToX (ONLY (classLongSound), GET_INTEGER ("Sample index")), "seconds");
END

DIRECT (LongSound_getNumberOfSamples)
	LongSound me = ONLY (classLongSound);
	Melder_information ("%ld samples", my nx);
END

DIRECT (LongSound_help) Melder_help ("LongSound"); END

FORM_READ (LongSound_open, "Open long sound file", 0)
	if (! praat_new (LongSound_open (file), MelderFile_name (file))) return 0;
END

FORM (LongSound_playPart, "LongSound: Play part", 0)
	REAL ("left Time range (s)", "0.0")
	REAL ("right Time range (s)", "10.0")
	OK
DO
	int n = 0;
	EVERY (n ++)
	if (n == 1 || Melder_getMaximumAsynchronicity () < Melder_ASYNCHRONOUS) {
		EVERY (LongSound_playPart (OBJECT, GET_REAL ("left Time range"), GET_REAL ("right Time range"), NULL, NULL))
	} else {
		Melder_setMaximumAsynchronicity (Melder_INTERRUPTABLE);
		EVERY (LongSound_playPart (OBJECT, GET_REAL ("left Time range"), GET_REAL ("right Time range"), NULL, NULL))
		Melder_setMaximumAsynchronicity (Melder_ASYNCHRONOUS);
	}
END

FORM (LongSound_writePartToAudioFile, "LongSound: Write part to audio file", 0)
	LABEL ("", "Audio file:")
	TEXTFIELD ("Audio file", "")
	RADIO ("Type", 1)
	{ int i; for (i = 1; i <= Melder_NUMBER_OF_AUDIO_FILE_TYPES; i ++) {
		RADIOBUTTON (Melder_audioFileTypeString (i))
	}}
	REAL ("left Time range (s)", "0.0")
	REAL ("right Time range (s)", "10.0")
	OK
DO
	struct MelderFile file;
	if (! Melder_relativePathToFile (GET_STRING ("Audio file"), & file)) return 0;
	if (! LongSound_writePartToAudioFile16 (ONLY (classLongSound), GET_INTEGER ("Type"),
		GET_REAL ("left Time range"), GET_REAL ("right Time range"), & file)) return 0;
END
	
FORM (LongSound_to_TextGrid, "To TextGrid...", "LongSound: To TextGrid...")
	SENTENCE ("Tier names", "Mary John bell")
	SENTENCE ("Point tiers", "bell")
	OK
DO
	EVERY_TO (TextGrid_create (((LongSound) OBJECT) -> xmin, ((Pitch) OBJECT) -> xmax,
		GET_STRING ("Tier names"), GET_STRING ("Point tiers")))
END

DIRECT (LongSound_view)
	if (praat.batch)
		return Melder_error ("Cannot view a LongSound from batch.");
	else
		WHERE (SELECTED)
			if (! praat_installEditor (SoundEditor_create (praat.topShell, FULL_NAME, OBJECT), IOBJECT))
				return 0;
END

FORM_WRITE (LongSound_writeToAifcFile, "Write to AIFC file", 0, "aifc")
	if (! pr_LongSound_concatenate (file, Melder_AIFC)) return 0;
END

FORM_WRITE (LongSound_writeToAiffFile, "Write to AIFF file", 0, "aiff")
	if (! pr_LongSound_concatenate (file, Melder_AIFF)) return 0;
END

FORM_WRITE (LongSound_writeToNextSunFile, "Write to NeXT/Sun file", 0, "au")
	if (! pr_LongSound_concatenate (file, Melder_NEXT_SUN)) return 0;
END

FORM_WRITE (LongSound_writeToNistFile, "Write to NIST file", 0, "nist")
	if (! pr_LongSound_concatenate (file, Melder_NIST)) return 0;
END

FORM_WRITE (LongSound_writeToWavFile, "Write to WAV file", 0, "wav")
	if (! pr_LongSound_concatenate (file, Melder_WAV)) return 0;
END

/********** LONGSOUND & SOUND **********/

FORM_WRITE (LongSound_Sound_writeToAifcFile, "Write to AIFC file", 0, "aifc")
	if (! pr_LongSound_concatenate (file, Melder_AIFC)) return 0;
END

FORM_WRITE (LongSound_Sound_writeToAiffFile, "Write to AIFF file", 0, "aiff")
	if (! pr_LongSound_concatenate (file, Melder_AIFF)) return 0;
END

FORM_WRITE (LongSound_Sound_writeToNextSunFile, "Write to NeXT/Sun file", 0, "au")
	if (! pr_LongSound_concatenate (file, Melder_NEXT_SUN)) return 0;
END

FORM_WRITE (LongSound_Sound_writeToNistFile, "Write to NIST file", 0, "nist")
	if (! pr_LongSound_concatenate (file, Melder_NIST)) return 0;
END

FORM_WRITE (LongSound_Sound_writeToWavFile, "Write to WAV file", 0, "wav")
	if (! pr_LongSound_concatenate (file, Melder_WAV)) return 0;
END

/********** SOUND **********/

FORM (Sound_add, "Add number to Sound", 0)
	REAL ("Number", "0.1")
	OK
DO
	WHERE (SELECTED) {
		Vector_addScalar (OBJECT, GET_REAL ("Number"));
		praat_dataChanged (OBJECT);
	}
END

DIRECT (Sounds_concatenate)
	long nx = 0;
	double dx = 0.0;
	Sound thee;
	WHERE (SELECTED) {
		Sound me = OBJECT;
		if (dx == 0.0) dx = my dx;
		else if (my dx != dx) return Melder_error ("(Sounds_concatenate:) Sampling frequencies do not match.");
		nx += my nx;
	}
	thee = Sound_create (0.0, nx * dx, nx, dx, 0.5 * dx);
	if (! thee) return 0;
	nx = 0;
	WHERE (SELECTED) {
		Sound me = OBJECT;
		NUMfvector_copyElements (my z [1], thy z [1] + nx, 1, my nx);
		nx += my nx;
	}
	if (! praat_new (thee, "chain")) return 0;
END

DIRECT (Sounds_concatenateRecoverably)
	long nx = 0, iinterval = 0;
	double dx = 0.0, tmin = 0.0;
	Sound thee = NULL;
	TextGrid him = NULL;
	WHERE (SELECTED) {
		Sound me = OBJECT;
		if (dx == 0.0) dx = my dx;
		else if (my dx != dx) return Melder_error ("(Sounds_concatenate:) Sampling frequencies do not match.");
		nx += my nx;
	}
	thee = Sound_create (0.0, nx * dx, nx, dx, 0.5 * dx); cherror
	him = TextGrid_create (0.0, nx * dx, "labels", ""); cherror
	nx = 0;
	WHERE (SELECTED) {
		Sound me = OBJECT;
		double tmax = tmin + my nx * dx;
		NUMfvector_copyElements (my z [1], thy z [1] + nx, 1, my nx);
		iinterval ++;
		if (iinterval > 1) { TextGrid_insertBoundary (him, 1, tmin); cherror }
		TextGrid_setIntervalText (him, 1, iinterval, my name); cherror
		nx += my nx;
		tmin = tmax;
	}
	praat_new (thee, "chain"); cherror
	praat_new (him, "chain"); cherror
end:
	iferror { forget (thee); forget (him); return 0; }
END

DIRECT (Sounds_convolve)
	Sound sound1 = NULL, sound2 = NULL;
	int i1 = 0, i2 = 0;
	char name [200];
	WHERE (SELECTED)
		if (sound1)
			{ sound2 = OBJECT; i2 = IOBJECT; }
		else
			{ sound1 = OBJECT; i1 = IOBJECT; }
	Melder_assert (sound1 && sound2 && i1 && i2);
	sprintf (name, "%s_%s", strchr (praat.list [i1]. name, ' ') + 1, strchr (praat.list [i2]. name, ' ') + 1);
	if (! praat_new (Sounds_convolve (sound1, sound2), name)) return 0;
END

FORM (Sounds_crossCorrelate, "Cross-correlate", 0)
	REAL ("From lag (s)", "-0.1")
	REAL ("To lag (s)", "0.1")
	BOOLEAN ("Normalize", 1)
	OK
DO
	Sound s1 = NULL, s2 = NULL;
	WHERE (SELECTED) if (s1) s2 = OBJECT; else s1 = OBJECT;
	if (! praat_new (Sounds_crossCorrelate (s1, s2, GET_REAL ("From lag"), GET_REAL ("To lag"),
		GET_INTEGER ("Normalize")), "cc_%s_%s", s1 -> name, s2 -> name)) return 0;
END

FORM (Sound_create, "Create Sound", "Create Sound...")
	WORD ("Name", "sineWithNoise")
	REAL ("Starting time (s)", "0.0")
	REAL ("Finishing time (s)", "1.0")
	POSITIVE ("Sample rate (Hz)", "22050")
	LABEL ("", "Formula:")
	TEXTFIELD ("formula", "1/2 * sin(2*pi*377*x) + randomGauss(0,0.1)")
	OK
DO
	double startingTime = GET_REAL ("Starting time");
	double finishingTime = GET_REAL ("Finishing time");
	double sampleRate = GET_REAL ("Sample rate");
	long numberOfSamples = floor ((finishingTime - startingTime) * sampleRate + 0.5);
	REQUIRE (finishingTime > startingTime, "Maximum time must be greater than minimum time.")
	REQUIRE (numberOfSamples >= 1, "Sound too short to contain any samples.")
	if (! praat_new (Sound_create (startingTime, finishingTime, numberOfSamples, 1 / sampleRate,
		startingTime + 0.5 * (finishingTime - startingTime - (numberOfSamples - 1) / sampleRate)),
		GET_STRING ("Name"))) return 0;
	praat_updateSelection ();
	if (! praat_Fon_formula (dia)) return 0;
END

FORM (Sound_createFromToneComplex, "Create Sound from tone complex", "Create Sound from tone complex...")
	WORD ("Name", "toneComplex")
	REAL ("Starting time (s)", "0.0")
	REAL ("Finishing time (s)", "1.0")
	POSITIVE ("Sample rate (Hz)", "22050")
	RADIO ("Phase", 2)
		RADIOBUTTON ("Sine")
		RADIOBUTTON ("Cosine")
	POSITIVE ("Frequency step (Hz)", "100")
	REAL ("First frequency (Hz)", "0 (= frequency step)")
	REAL ("Ceiling (Hz)", "0 (= Nyquist)")
	INTEGER ("Number of components", "0 (= maximum)")
	OK
DO
	if (! praat_new (Sound_createFromToneComplex (GET_REAL ("Starting time"), GET_REAL ("Finishing time"),
		GET_REAL ("Sample rate"), GET_INTEGER ("Phase") - 1, GET_REAL ("Frequency step"),
		GET_REAL ("First frequency"), GET_REAL ("Ceiling"), GET_INTEGER ("Number of components")),
		GET_STRING ("Name"))) return 0;
END

FORM (Sound_deemphasizeInline, "Sound: De-emphasize (in-line)", "Sound: De-emphasize (in-line)...")
	REAL ("From frequency (Hz)", "50.0")
	OK
DO
	WHERE (SELECTED) {
		Sound_deEmphasis (OBJECT, GET_REAL ("From frequency"));
		Vector_scale (OBJECT, 0.99);
		praat_dataChanged (OBJECT);
	}
END

FORM (Sound_deepenBandModulation, "Deepen band modulation", "Sound: Deepen band modulation...")
	POSITIVE ("Enhancement (dB)", "20")
	POSITIVE ("From frequency (Hz)", "300")
	POSITIVE ("To frequency (Hz)", "8000")
	POSITIVE ("Slow modulation (Hz)", "3")
	POSITIVE ("Fast modulation (Hz)", "30")
	POSITIVE ("Band smoothing (Hz)", "100")
	OK
DO
	WHERE (SELECTED)
		if (! praat_new (Sound_deepenBandModulation (OBJECT, GET_REAL ("Enhancement"),
			GET_REAL ("From frequency"), GET_REAL ("To frequency"),
			GET_REAL ("Slow modulation"), GET_REAL ("Fast modulation"), GET_REAL ("Band smoothing")),
			"%s_%.0f", NAME, GET_REAL ("Enhancement"))) return 0;
END

FORM (Sound_draw, "Draw Sound", 0)
	REAL ("left Time range (seconds)", "0.0")
	REAL ("right Time range", "0.0 (= all)")
	REAL ("Minimum", "0.0")
	REAL ("Maximum", "0.0 (= auto)")
	BOOLEAN ("Garnish", 1)
	OK
DO
	EVERY_DRAW (Sound_draw (OBJECT, GRAPHICS, GET_REAL ("left Time range"), GET_REAL ("right Time range"),
		GET_REAL ("Minimum"), GET_REAL ("Maximum"), GET_INTEGER ("Garnish")))
END

FORM (Sound_drawClipped, "Draw Sound", 0)
	REAL ("left Time range (seconds)", "0.0")
	REAL ("right Time range (seconds)", "0.0 (= all)")
	REAL ("Minimum", "0.0")
	REAL ("Maximum", "0.0")
	BOOLEAN ("Garnish", 1)
	OK
DO
	EVERY_DRAW (Sound_drawClipped (OBJECT, GRAPHICS, GET_REAL ("left Time range"), GET_REAL ("right Time range"),
		GET_REAL ("Minimum"), GET_REAL ("Maximum"), GET_INTEGER ("Garnish")))
END

FORM (Sound_drawSamples, "Draw Sound samples", 0)
	REAL ("left Time range (seconds)", "0.0")
	REAL ("right Time range (seconds)", "0.0 (= all)")
	REAL ("Minimum", "0.0")
	REAL ("Maximum", "0.0 (= auto)")
	BOOLEAN ("Garnish", 1)
	OK
DO
	EVERY_DRAW (Sound_drawSamples (OBJECT, GRAPHICS, GET_REAL ("left Time range"),
		GET_REAL ("right Time range"), GET_REAL ("Minimum"), GET_REAL ("Maximum"), GET_INTEGER ("Garnish")))
END

static void cb_SoundEditor_publish (Any editor, void *closure, Any publish) {
	(void) editor;
	(void) closure;
	if (! praat_new (publish, NULL)) { Melder_flushError (NULL); return; }
	praat_updateSelection ();
	if (Thing_member (publish, classSpectrum) && strequ (Thing_getName (publish), "slice")) {
		int IOBJECT;
		WHERE (SELECTED) {
			SpectrumEditor editor2 = SpectrumEditor_create (praat.topShell, FULL_NAME, OBJECT);
			if (! editor2) return;
			if (! praat_installEditor (editor2, IOBJECT)) Melder_flushError (NULL);
		}
	}
}
DIRECT (Sound_edit)
	if (praat.batch) {
		return Melder_error ("Cannot edit a Sound from batch.");
	} else {
		WHERE (SELECTED) {
			SoundEditor editor = SoundEditor_create (praat.topShell, FULL_NAME, OBJECT);
			if (! editor) return 0;
			if (! praat_installEditor (editor, IOBJECT)) return 0;
			Editor_setPublishCallback (editor, cb_SoundEditor_publish, NULL);
		}
	}
END

FORM (Sound_extractPart, "Extract part of Sound", 0)
	REAL ("left Time range (s)", "0")
	REAL ("right Time range (s)", "0.1")
	ENUM ("Window", Sound_WINDOW, enumi (Sound_WINDOW, Hanning))
	POSITIVE ("Relative width", "1.0")
	BOOLEAN ("Preserve times", 0)
	OK
DO
	WHERE (SELECTED) {
		Sound me = OBJECT;
		if (! praat_new (Sound_extractPart (me,
			GET_REAL ("left Time range"), GET_REAL ("right Time range"),
			GET_INTEGER ("Window"), GET_REAL ("Relative width"),
			GET_INTEGER ("Preserve times")),
			"%s_part", my name)) return 0;
	}
END

FORM (Sound_filter_deemphasis, "Sound: Filter (de-emphasis)", "Sound: Filter (de-emphasis)...")
	REAL ("From frequency (Hz)", "50.0")
	OK
DO
	WHERE (SELECTED) {
		if (! praat_new (Sound_filter_deemphasis (OBJECT, GET_REAL ("From frequency")),
			"%s_deemp", NAME)) return 0;
	}
END

FORM (Sound_filter_formula, "Filter (formula)...", "Formula...")
	LABEL ("", "Frequency-domain filtering with a formula (uses Sound-to-Spectrum and Spectrum-to-Sound): x is frequency in Hertz")
	TEXTFIELD ("formula", "if x<500 or x>1000 then 0 else self fi; rectangular band filter")
	OK
DO
	WHERE (SELECTED)
		if (! praat_new (Sound_filter_formula (OBJECT, GET_STRING ("formula")),
			"%s_filt", NAME)) return 0;
END

FORM (Sound_filter_oneFormant, "Sound: Filter (one formant)", "Sound: Filter (one formant)...")
	REAL ("Frequency (Hz)", "1000")
	POSITIVE ("Bandwidth (Hz)", "100")
	OK
DO
	WHERE (SELECTED) {
		if (! praat_new (Sound_filter_oneFormant (OBJECT, GET_REAL ("Frequency"), GET_REAL ("Bandwidth")),
			"%s_filt", NAME)) return 0;
	}
END

FORM (Sound_filterWithOneFormantInline, "Sound: Filter with one formant (in-line)", "Sound: Filter with one formant (in-line)...")
	REAL ("Frequency (Hz)", "1000")
	POSITIVE ("Bandwidth (Hz)", "100")
	OK
DO
	WHERE (SELECTED) {
		Sound_filterWithOneFormantInline (OBJECT, GET_REAL ("Frequency"), GET_REAL ("Bandwidth"));
		praat_dataChanged (OBJECT);
	}
END

FORM (Sound_filter_passHannBand, "Sound: Filter (pass Hann band)", "Sound: Filter (pass Hann band)...")
	REAL ("From frequency (Hz)", "500")
	REAL ("To frequency (s)", "1000")
	POSITIVE ("Smoothing (Hz)", "100")
	OK
DO
	WHERE (SELECTED) {
		if (! praat_new (Sound_filter_passHannBand (OBJECT,
			GET_REAL ("From frequency"), GET_REAL ("To frequency"), GET_REAL ("Smoothing")),
			"%s_band", NAME)) return 0;
	}
END

FORM (Sound_filter_preemphasis, "Sound: Filter (pre-emphasis)", "Sound: Filter (pre-emphasis)...")
	REAL ("From frequency (Hz)", "50.0")
	OK
DO
	WHERE (SELECTED) {
		if (! praat_new (Sound_filter_preemphasis (OBJECT, GET_REAL ("From frequency")),
			"%s_preemp", NAME)) return 0;
	}
END

FORM (Sound_filter_stopHannBand, "Sound: Filter (stop Hann band)", "Sound: Filter (stop Hann band)...")
	REAL ("From frequency (Hz)", "500")
	REAL ("To frequency (s)", "1000")
	POSITIVE ("Smoothing (Hz)", "100")
	OK
DO
	WHERE (SELECTED) {
		if (! praat_new (Sound_filter_stopHannBand (OBJECT,
			GET_REAL ("From frequency"), GET_REAL ("To frequency"), GET_REAL ("Smoothing")),
			"%s_band", NAME)) return 0;
	}
END

FORM (Sound_formula, "Sound: Formula", "Sound: Formula...")
	LABEL ("label1", "! `x' is the time in seconds, `col' is the sample number.")
	LABEL ("label2", "x = x1   ! time associated with first sample")
	LABEL ("label3", "for col from 1 to ncol")
	LABEL ("label4", "   self [col] = ...")
	TEXTFIELD ("formula", "self")
	LABEL ("label5", "   x = x + dx")
	LABEL ("label6", "endfor")
	OK
DO
	if (! praat_Fon_formula (dia)) return 0;
END

FORM (Sound_getAbsoluteExtremum, "Sound: Get absolute extremum", "Sound: Get absolute extremum...")
	REAL ("left Time range (s)", "0.0")
	REAL ("right Time range (s)", "0.0 (= all)")
	RADIO ("Interpolation", 4)
	RADIOBUTTON ("None")
	RADIOBUTTON ("Parabolic")
	RADIOBUTTON ("Cubic")
	RADIOBUTTON ("Sinc70")
	RADIOBUTTON ("Sinc700")
	OK
DO
	Melder_informationReal (Vector_getAbsoluteExtremum (ONLY (classSound),
		GET_REAL ("left Time range"), GET_REAL ("right Time range"), GET_INTEGER ("Interpolation") - 1), "Pascal");
END

FORM (Sound_getEnergy, "Sound: Get energy", "Sound: Get energy...")
	REAL ("left Time range (s)", "0.0")
	REAL ("right Time range (s)", "0.0 (= all)")
	OK
DO
	Melder_informationReal (Vector_getEnergy (ONLY (classSound), GET_REAL ("left Time range"), GET_REAL ("right Time range")), "Pa2 sec");
END

DIRECT (Sound_getEnergyInAir)
	Melder_informationReal (Sound_getEnergyInAir (ONLY (classSound)), "Joule/m2");
END

FORM (Sound_getIndexFromTime, "Sound: Get sample index", "Sound: Get index from time...")
	REAL ("Time (s)", "0.5")
	OK
DO
	Melder_informationReal (Sampled_xToIndex (ONLY (classSound), GET_REAL ("Time")), NULL);
END

DIRECT (Sound_getIntensity_dB)
	Melder_informationReal (Sound_getIntensity_dB (ONLY (classSound)), "dB");
END

FORM (Sound_getMaximum, "Sound: Get maximum", "Sound: Get maximum...")
	REAL ("left Time range (s)", "0.0")
	REAL ("right Time range (s)", "0.0 (= all)")
	RADIO ("Interpolation", 4)
	RADIOBUTTON ("None")
	RADIOBUTTON ("Parabolic")
	RADIOBUTTON ("Cubic")
	RADIOBUTTON ("Sinc70")
	RADIOBUTTON ("Sinc700")
	OK
DO
	Melder_informationReal (Vector_getMaximum (ONLY (classSound),
		GET_REAL ("left Time range"), GET_REAL ("right Time range"), GET_INTEGER ("Interpolation") - 1), "Pascal");
END

FORM (Sound_getMean, "Sound: Get mean", "Sound: Get mean...")
	REAL ("left Time range (s)", "0.0")
	REAL ("right Time range (s)", "0.0 (= all)")
	OK
DO
	Melder_informationReal (Vector_getMean (ONLY (classSound), GET_REAL ("left Time range"), GET_REAL ("right Time range")), "Pascal");
END

FORM (Sound_getMinimum, "Sound: Get minimum", "Sound: Get minimum...")
	REAL ("left Time range (s)", "0.0")
	REAL ("right Time range (s)", "0.0 (= all)")
	RADIO ("Interpolation", 4)
	RADIOBUTTON ("None")
	RADIOBUTTON ("Parabolic")
	RADIOBUTTON ("Cubic")
	RADIOBUTTON ("Sinc70")
	RADIOBUTTON ("Sinc700")
	OK
DO
	Melder_informationReal (Vector_getMinimum (ONLY (classSound),
		GET_REAL ("left Time range"), GET_REAL ("right Time range"), GET_INTEGER ("Interpolation") - 1), "Pascal");
END

FORM (Sound_getNearestZeroCrossing, "Sound: Get nearest zero crossing", "Sound: Get nearest zero crossing...")
	REAL ("Time (s)", "0.5")
	OK
DO
	Melder_informationReal (Sound_getNearestZeroCrossing (ONLY (classSound), GET_REAL ("Time")), "seconds");
END

DIRECT (Sound_getNumberOfSamples)
	Sound me = ONLY (classSound);
	Melder_information ("%ld samples", my nx);
END

FORM (Sound_getPower, "Sound: Get power", "Sound: Get power...")
	REAL ("left Time range (s)", "0.0")
	REAL ("right Time range (s)", "0.0 (= all)")
	OK
DO
	Melder_informationReal (Vector_getPower (ONLY (classSound), GET_REAL ("left Time range"), GET_REAL ("right Time range")), "Pa2");
END

DIRECT (Sound_getPowerInAir)
	Melder_informationReal (Sound_getPowerInAir (ONLY (classSound)), "Watt/m2");
END

FORM (Sound_getRootMeanSquare, "Sound: Get root-mean-square", "Sound: Get root-mean-square...")
	REAL ("left Time range (s)", "0.0")
	REAL ("right Time range (s)", "0.0 (= all)")
	OK
DO
	Melder_informationReal (Vector_getRootMeanSquare (ONLY (classSound), GET_REAL ("left Time range"), GET_REAL ("right Time range")), "Pascal");
END

DIRECT (Sound_getSamplePeriod)
	Sound me = ONLY (classSound);
	Melder_informationReal (my dx, "seconds");
END

DIRECT (Sound_getSampleRate)
	Sound me = ONLY (classSound);
	Melder_informationReal (1 / my dx, "Hertz");
END

FORM (Sound_getStandardDeviation, "Sound: Get standard deviation", "Sound: Get standard deviation...")
	REAL ("left Time range (s)", "0.0")
	REAL ("right Time range (s)", "0.0 (= all)")
	OK
DO
	Melder_informationReal (Vector_getStandardDeviation (ONLY (classSound), GET_REAL ("left Time range"), GET_REAL ("right Time range")), "Pascal");
END

FORM (Sound_getTimeFromIndex, "Get time from sample index", "Sound: Get time from index...")
	INTEGER ("Sample index", "100")
	OK
DO
	Melder_informationReal (Sampled_indexToX (ONLY (classSound), GET_INTEGER ("Sample index")), "seconds");
END

FORM (Sound_getTimeOfMaximum, "Sound: Get time of maximum", "Sound: Get time of maximum...")
	REAL ("left Time range (s)", "0.0")
	REAL ("right Time range (s)", "0.0 (= all)")
	RADIO ("Interpolation", 4)
	RADIOBUTTON ("None")
	RADIOBUTTON ("Parabolic")
	RADIOBUTTON ("Cubic")
	RADIOBUTTON ("Sinc70")
	RADIOBUTTON ("Sinc700")
	OK
DO
	Melder_informationReal (Vector_getXOfMaximum (ONLY (classSound),
		GET_REAL ("left Time range"), GET_REAL ("right Time range"), GET_INTEGER ("Interpolation") - 1), "seconds");
END

FORM (Sound_getTimeOfMinimum, "Sound: Get time of minimum", "Sound: Get time of minimum...")
	REAL ("left Time range (s)", "0.0")
	REAL ("right Time range (s)", "0.0 (= all)")
	RADIO ("Interpolation", 4)
	RADIOBUTTON ("None")
	RADIOBUTTON ("Parabolic")
	RADIOBUTTON ("Cubic")
	RADIOBUTTON ("Sinc70")
	RADIOBUTTON ("Sinc700")
	OK
DO
	Melder_informationReal (Vector_getXOfMinimum (ONLY (classSound),
		GET_REAL ("left Time range"), GET_REAL ("right Time range"), GET_INTEGER ("Interpolation") - 1), "seconds");
END

FORM (Sound_getValueAtIndex, "Get value at index", "Sound: Get value at index...")
	INTEGER ("Sample index", "100")
	OK
DO
	Sound me = ONLY (classSound);
	long sampleIndex = GET_INTEGER ("Sample index");
	Melder_informationReal (sampleIndex < 1 || sampleIndex > my nx ? NUMundefined : my z [1] [sampleIndex], "Pascal");
END

FORM (Sound_getValueAtTime, "Get value at time", "Sound: Get value at time...")
	REAL ("Time (s)", "0.5")
	RADIO ("Interpolation", 4)
	RADIOBUTTON ("Nearest")
	RADIOBUTTON ("Linear")
	RADIOBUTTON ("Cubic")
	RADIOBUTTON ("Sinc70")
	RADIOBUTTON ("Sinc700")
	OK
DO
	Melder_informationReal (Vector_getValueAtX (ONLY (classSound), GET_REAL ("Time"), GET_INTEGER ("Interpolation") - 1), "Pascal");
END

DIRECT (Sound_help) Melder_help ("Sound"); END

FORM (Sound_lengthen_psola, "Lengthen (PSOLA)", "Sound: Lengthen (PSOLA)...")
	POSITIVE ("Minimum pitch (Hz)", "75")
	POSITIVE ("Maximum pitch (Hz)", "600")
	POSITIVE ("Factor", "1.5")
	OK
DO
	double minimumPitch = GET_REAL ("Minimum pitch"), maximumPitch = GET_REAL ("Maximum pitch");
	double factor = GET_REAL ("Factor");
	REQUIRE (minimumPitch < maximumPitch, "Maximum pitch should be greater than minimum pitch.")
	WHERE (SELECTED)
		if (! praat_new (Sound_lengthen_psola (OBJECT, minimumPitch, maximumPitch, factor),
			"%s_%.2f", NAME, factor)) return 0;
END

FORM (Sound_multiply, "Multiply Sound by number", 0)
	REAL ("Number", "1.5")
	OK
DO
	WHERE (SELECTED) {
		Vector_multiplyByScalar (OBJECT, GET_REAL ("Number"));
		praat_dataChanged (OBJECT);
	}
END

FORM (Sound_multiplyByWindow, "Multiply Sound by window", 0)
	ENUM ("Window", Sound_WINDOW, enumi (Sound_WINDOW, Hanning))
	OK
DO
	WHERE (SELECTED) {
		Sound_multiplyByWindow (OBJECT, GET_INTEGER ("Window"));
		praat_dataChanged (OBJECT);
	}
END

FORM (Sound_overrideSampleRate, "Override sample rate", 0)
	POSITIVE ("New sample rate", "16000.0")
	OK
DO
	WHERE (SELECTED) {
		Sound_overrideSampleRate (OBJECT, GET_REAL ("New sample rate"));
		praat_dataChanged (OBJECT);
	}
END

DIRECT (Sound_play)
	int n = 0;
	EVERY (n ++)
	if (n == 1 || Melder_getMaximumAsynchronicity () < Melder_ASYNCHRONOUS) {
		EVERY (Sound_play (OBJECT, NULL, NULL))
	} else {
		Melder_setMaximumAsynchronicity (Melder_INTERRUPTABLE);
		EVERY (Sound_play (OBJECT, NULL, NULL))
		Melder_setMaximumAsynchronicity (Melder_ASYNCHRONOUS);
	}
END

FORM (Sound_preemphasizeInline, "Sound: Pre-emphasize (in-line)", "Sound: Pre-emphasize (in-line)...")
	REAL ("From frequency (Hz)", "50.0")
	OK
DO
	WHERE (SELECTED) {
		Sound_preEmphasis (OBJECT, GET_REAL ("From frequency"));
		Vector_scale (OBJECT, 0.99);
		praat_dataChanged (OBJECT);
	}
END

FORM_READ (Sound_read2FromStereoFile, "Read two Sounds from stereo file", 0)
	Sound left, right;
	char name_left [300], name_right [300];
	sprintf (name_left, "%.200s_left", MelderFile_name (file));
	sprintf (name_right, "%.200s_right", MelderFile_name (file));
	if (! Sound_read2FromSoundFile (file, & left, & right)) return 0;
	if (! praat_new (left, "left")) return 0;
	if (right) { if (! praat_new (right, "right")) return 0; }
END

FORM_READ (Sound_readFromRawAlawFile, "Read Sound from raw Alaw file", 0)
	if (! praat_new (Sound_readFromRawAlawFile (file), MelderFile_name (file))) return 0;
END

static SoundRecorder soundRecorder;   /* Only one at a time. */
static void cb_SoundRecorder_destroy (Any editor, void *closure) {
	(void) editor;
	(void) closure;
	soundRecorder = NULL;
}
static int previousNumberOfChannels;
static void cb_SoundRecorder_publish (Any editor, void *closure, Any publish) {
	(void) editor;
	(void) closure;
	if (! praat_new (publish, NULL)) Melder_flushError (NULL);
	praat_updateSelection ();
}
DIRECT (Sound_record_mono)
	if (praat.batch) return Melder_error ("Cannot record a Sound from batch.");
	if (soundRecorder) {
		if (previousNumberOfChannels == 1) {
			Editor_raise (soundRecorder);
		} else {
			forget (soundRecorder);
		}
	}
	if (! soundRecorder) {
		soundRecorder = SoundRecorder_create (praat.topShell, 1, praat.context);
		if (soundRecorder == NULL) return 0;
		Editor_setDestroyCallback (soundRecorder, cb_SoundRecorder_destroy, NULL);
		Editor_setPublishCallback (soundRecorder, cb_SoundRecorder_publish, NULL);
	}
	previousNumberOfChannels = 1;
END
static void cb_SoundRecorder_publish2 (Any editor, Any closure, Any publish1, Any publish2) {
	(void) editor;
	(void) closure;
	if (! praat_new (publish1, "left") || ! praat_new (publish2, "right")) Melder_flushError (NULL);
	praat_updateSelection ();
}
DIRECT (Sound_record_stereo)
	if (praat.batch) return Melder_error ("Cannot record a Sound from batch.");
	if (soundRecorder) {
		if (previousNumberOfChannels == 2) {
			Editor_raise (soundRecorder);
		} else {
			forget (soundRecorder);
		}
	}
	if (! soundRecorder) {
		soundRecorder = SoundRecorder_create (praat.topShell, 2, praat.context);
		if (soundRecorder == NULL) return 0;
		Editor_setDestroyCallback (soundRecorder, cb_SoundRecorder_destroy, NULL);
		Editor_setPublishCallback (soundRecorder, cb_SoundRecorder_publish, NULL);
		Editor_setPublish2Callback (soundRecorder, cb_SoundRecorder_publish2, NULL);
	}
	previousNumberOfChannels = 2;
END

FORM (Sound_recordFixedTime, "Record Sound", 0)
	RADIO ("Input source", 1)
		RADIOBUTTON ("Microphone")
		RADIOBUTTON ("Line")
		RADIOBUTTON ("Digital")
	#if defined (sgi)
		REAL ("Gain (0-1)", "0.5")
	#else
		REAL ("Gain (0-1)", "0.1")
	#endif
	REAL ("Balance (0-1)", "0.5")
	RADIO ("Sampling frequency", 1)
		#if defined (hpux)
			RADIOBUTTON ("5512")
		#endif
		#ifdef UNIX
		RADIOBUTTON ("8000")
		#endif
		#ifdef sgi
		RADIOBUTTON ("9800")
		#endif
		RADIOBUTTON ("11025")
		#ifdef UNIX
		RADIOBUTTON ("16000")
		#endif
		RADIOBUTTON ("22050")
		#ifdef UNIX
		RADIOBUTTON ("32000")
		#endif
		RADIOBUTTON ("44100")
		#ifdef UNIX
		RADIOBUTTON ("48000")
		#endif
	POSITIVE ("Duration (seconds)", "1.0")
	OK
DO
	NEW (Sound_recordFixedTime (GET_INTEGER ("Input source"),
		GET_REAL ("Gain"), GET_REAL ("Balance"),
		atol (GET_STRING ("Sampling frequency")), GET_REAL ("Duration")));
END

FORM (Sound_resample, "Resample sound", "Sound: Resample...")
	POSITIVE ("New sample rate (Hz)", "10000")
	NATURAL ("Precision (samples)", "50")
	OK
DO
	double samplingFrequency = GET_REAL ("New sample rate");
	WHERE (SELECTED)
		if (! praat_new (Sound_resample (OBJECT, samplingFrequency, GET_INTEGER ("Precision")),
			"%s %ld", NAME, (long) floor (samplingFrequency + 0.5))) return 0;
END

DIRECT (Sound_reverse)
	WHERE (SELECTED) {
		Sound_reverse (OBJECT, 0, 0);
		praat_dataChanged (OBJECT);
	}
END

FORM (Sound_scale, "Scale Sound", 0)
	POSITIVE ("Maximum amplitude", "0.99996948")
	LABEL ("", "(default for 16-bit audio is 32767 / 32768)")  
	OK
DO
	WHERE (SELECTED) {
		Vector_scale (OBJECT, GET_REAL ("Maximum amplitude"));
		praat_dataChanged (OBJECT);
	}
END

FORM (Sound_setValueAtIndex, "Set value at index", "Sound: Set value at index...")
	NATURAL ("Sample index", "100")
	REAL ("New value", "0")
	OK
DO
	WHERE (SELECTED) {
		Sound me = OBJECT;
		long index = GET_INTEGER ("Sample index");
		if (index > my nx)
			return Melder_error ("The sample index should not exceed the number of samples, which is %ld.", my nx);
		my z [1] [index] = GET_REAL ("New value");
		praat_dataChanged (me);
	}
END

DIRECT (Sound_subtractMean)
	WHERE (SELECTED) {
		Vector_subtractMean (OBJECT);
		praat_dataChanged (OBJECT);
	}
END

FORM (Sound_to_Manipulation, "From Sound to Manipulation", "Manipulation")
	POSITIVE ("Time step (s)", "0.01")
	POSITIVE ("Minimum pitch (Hz)", "75")
	POSITIVE ("Maximum pitch (Hz)", "600")
	OK
DO
	double fmin = GET_REAL ("Minimum pitch"), fmax = GET_REAL ("Maximum pitch");
	REQUIRE (fmax > fmin, "Maximum pitch must be greater than minimum pitch.");
	EVERY_TO (Sound_to_Manipulation (OBJECT, GET_REAL ("Time step"), fmin, fmax))
END

FORM (Sound_to_Cochleagram, "From Sound to Cochleagram", 0)
	POSITIVE ("Time step (s)", "0.01")
	POSITIVE ("Frequency resolution (Bark)", "0.1")
	POSITIVE ("Window length (s)", "0.03")
	REAL ("Forward-masking time (s)", "0.03")
	OK
DO
	EVERY_TO (Sound_to_Cochleagram (OBJECT, GET_REAL ("Time step"),
		GET_REAL ("Frequency resolution"), GET_REAL ("Window length"), GET_REAL ("Forward-masking time")))
END

FORM (Sound_to_Cochleagram_edb, "From Sound to Cochleagram (De Boer, Meddis & Hewitt)", 0)
	POSITIVE ("Time step (s)", "0.01")
	POSITIVE ("Frequency resolution (Bark)", "0.1")
	BOOLEAN ("Has synapse", 1)
	LABEL ("", "Meddis synapse properties")
	POSITIVE ("   replenishment rate (/sec)", "5.05")
	POSITIVE ("   loss rate (/sec)", "2500")
	POSITIVE ("   return rate (/sec)", "6580")
	POSITIVE ("   reprocessing rate (/sec)", "66.31")
	OK
DO
	EVERY_TO (Sound_to_Cochleagram_edb (OBJECT, GET_REAL ("Time step"),
		GET_REAL ("Frequency resolution"), GET_INTEGER ("Has synapse"),
		GET_REAL ("   replenishment rate"), GET_REAL ("   loss rate"),
		GET_REAL ("   return rate"), GET_REAL ("   reprocessing rate")))
END

FORM (Sound_to_Formant_burg, "From Sound to Formant (Burg)", "Sound: To Formant (burg)...")
	POSITIVE ("Time step (s)", "0.01")
	NATURAL ("Max. number of formants", "5")
	REAL ("Maximum formant (Hz)", "5500 (= adult female)")
	POSITIVE ("Window length (s)", "0.025")
	POSITIVE ("Pre-emphasis from (Hz)", "50")
	OK
DO
	EVERY_TO (Sound_to_Formant_burg (OBJECT, GET_REAL ("Time step"),
		GET_INTEGER ("Max. number of formants"), GET_REAL ("Maximum formant"),
		GET_REAL ("Window length"), GET_REAL ("Pre-emphasis from")))
END

FORM (Sound_to_Formant_keepAll, "From Sound to Formant (keep all)", "Sound: To Formant (keep all)...")
	POSITIVE ("Time step (s)", "0.01")
	NATURAL ("Max. number of formants", "5")
	REAL ("Maximum formant (Hz)", "5500 (= adult female)")
	POSITIVE ("Window length (s)", "0.025")
	POSITIVE ("Pre-emphasis from (Hz)", "50")
	OK
DO
	EVERY_TO (Sound_to_Formant_keepAll (OBJECT, GET_REAL ("Time step"),
		GET_INTEGER ("Max. number of formants"), GET_REAL ("Maximum formant"),
		GET_REAL ("Window length"), GET_REAL ("Pre-emphasis from")))
END

FORM (Sound_to_Formant_willems, "From Sound to Formant (split Levinson (Willems))", "Sound: To Formant (sl)...")
	POSITIVE ("Time step (s)", "0.01")
	NATURAL ("Number of formants", "5")
	REAL ("Maximum formant (Hz)", "5500 (= adult female)")
	POSITIVE ("Window length (s)", "0.025")
	POSITIVE ("Pre-emphasis from (Hz)", "50")
	OK
DO
	EVERY_TO (Sound_to_Formant_willems (OBJECT, GET_REAL ("Time step"),
		GET_INTEGER ("Number of formants"), GET_REAL ("Maximum formant"),
		GET_REAL ("Window length"), GET_REAL ("Pre-emphasis from")))
END

FORM (Sound_to_Harmonicity_ac, "From Sound to Harmonicity", "Sound: To Harmonicity (ac)...")
	POSITIVE ("Time step (s)", "0.01")
	POSITIVE ("Minimum pitch (Hz)", "75")
	REAL ("Silence threshold", "0.1")
	POSITIVE ("Periods per window", "4.5")
	OK
DO
	double periodsPerWindow = GET_REAL ("Periods per window");
	REQUIRE (periodsPerWindow >= 3.0, "Number of periods per window must be >= 3.")
	EVERY_TO (Sound_to_Harmonicity_ac (OBJECT, GET_REAL ("Time step"),
		GET_REAL ("Minimum pitch"), GET_REAL ("Silence threshold"), periodsPerWindow))
END

FORM (Sound_to_Harmonicity_cc, "From Sound to Harmonicity", "Sound: To Harmonicity (cc)...")
	POSITIVE ("Time step (s)", "0.01")
	POSITIVE ("Minimum pitch (Hz)", "75")
	REAL ("Silence threshold", "0.1")
	POSITIVE ("Periods per window", "1.0")
	OK
DO
	EVERY_TO (Sound_to_Harmonicity_cc (OBJECT, GET_REAL ("Time step"),
		GET_REAL ("Minimum pitch"), GET_REAL ("Silence threshold"),
		GET_REAL ("Periods per window")))
END

FORM (Sound_to_Harmonicity_gne, "From Sound to Cross-correlation", 0)
	POSITIVE ("Minimum frequency (Hz)", "500")
	POSITIVE ("Maximum frequency (Hz)", "4500")
	POSITIVE ("Bandwidth (Hz)", "1000")
	POSITIVE ("Step (Hz)", "80")
	OK
DO
	EVERY_TO (Sound_to_Harmonicity_GNE (OBJECT, GET_REAL ("Minimum frequency"),
		GET_REAL ("Maximum frequency"), GET_REAL ("Bandwidth"),
		GET_REAL ("Step")))
END

FORM (Sound_to_Intensity, "From Sound to Intensity", "Sound: To Intensity...")
	POSITIVE ("Minimum pitch (Hz)", "100")
	REAL ("Time step (s)", "0 (= auto)")
	OK
DO
	EVERY_TO (Sound_to_Intensity ((Sound) OBJECT,
		GET_REAL ("Minimum pitch"), GET_REAL ("Time step")))
END

DIRECT (Sound_to_IntervalTier)
	EVERY_TO (IntervalTier_create (((Sound) OBJECT) -> xmin, ((Sound) OBJECT) -> xmax))
END

DIRECT (Sound_to_Matrix)
	EVERY_TO (Sound_to_Matrix (OBJECT))
END

DIRECT (Sounds_to_ParamCurve)
	Sound s1 = NULL, s2 = NULL;
	WHERE (SELECTED) if (s1) s2 = OBJECT; else s1 = OBJECT;
	if (! praat_new (ParamCurve_create (s1, s2), "%s_%s", s1 -> name, s2 -> name)) return 0;
END

FORM (Sound_to_Pitch, "From Sound to Pitch (simple)", "Sound: To Pitch...")
	REAL ("Time step (s)", "0.0 (= auto)")
	POSITIVE ("Pitch floor (Hz)", "75.0")
	POSITIVE ("Pitch ceiling (Hz)", "600.0")
	OK
DO
	EVERY_TO (Sound_to_Pitch (OBJECT, GET_REAL ("Time step"),
		GET_REAL ("Pitch floor"), GET_REAL ("Pitch ceiling")))
END

FORM (Sound_to_Pitch_ac, "From Sound to Pitch (ac)", "Sound: To Pitch (ac)...")
	LABEL ("", "Finding the candidates")
	REAL ("Time step (s)", "0.0 (= auto)")
	POSITIVE ("Pitch floor (Hz)", "75.0")
	NATURAL ("Max. number of candidates", "15")
	BOOLEAN ("Very accurate", 0)
	LABEL ("", "Finding a path")
	REAL ("Silence threshold", "0.03")
	REAL ("Voicing threshold", "0.45")
	REAL ("Octave cost", "0.01")
	REAL ("Octave-jump cost", "0.35")
	REAL ("Voiced / unvoiced cost", "0.14")
	POSITIVE ("Pitch ceiling (Hz)", "600.0")
	OK
DO
	long maxnCandidates = GET_INTEGER ("Max. number of candidates");
	REQUIRE (maxnCandidates >= 2, "Maximum number of candidates must be greater than 1.")
	EVERY_TO (Sound_to_Pitch_ac (OBJECT, GET_REAL ("Time step"),
		GET_REAL ("Pitch floor"), 3.0, maxnCandidates, GET_INTEGER ("Very accurate"),
		GET_REAL ("Silence threshold"), GET_REAL ("Voicing threshold"),
		GET_REAL ("Octave cost"), GET_REAL ("Octave-jump cost"),
		GET_REAL ("Voiced / unvoiced cost"), GET_REAL ("Pitch ceiling")))
END

FORM (Sound_to_Pitch_cc, "From Sound to Pitch (cc)", "Sound: To Pitch (cc)...")
	LABEL ("", "Finding the candidates")
	REAL ("Time step (s)", "0.0 (= auto)")
	POSITIVE ("Pitch floor (Hz)", "75")
	NATURAL ("Max. number of candidates", "15")
	BOOLEAN ("Very accurate", 0)
	LABEL ("", "Finding a path")
	REAL ("Silence threshold", "0.03")
	REAL ("Voicing threshold", "0.45")
	REAL ("Octave cost", "0.01")
	REAL ("Octave-jump cost", "0.35")
	REAL ("Voiced / unvoiced cost", "0.14")
	POSITIVE ("Pitch ceiling (Hz)", "600")
	OK
DO
	long maxnCandidates = GET_INTEGER ("Max. number of candidates");
	REQUIRE (maxnCandidates >= 2, "Maximum number of candidates must be greater than 1.")
	EVERY_TO (Sound_to_Pitch_cc (OBJECT, GET_REAL ("Time step"),
		GET_REAL ("Pitch floor"), 1.0, maxnCandidates, GET_INTEGER ("Very accurate"),
		GET_REAL ("Silence threshold"), GET_REAL ("Voicing threshold"),
		GET_REAL ("Octave cost"), GET_REAL ("Octave-jump cost"),
		GET_REAL ("Voiced / unvoiced cost"), GET_REAL ("Pitch ceiling")))
END

FORM (Sound_to_PointProcess_extrema, "Get extrema", 0)
	BOOLEAN ("Include maxima", 1)
	BOOLEAN ("Include minima", 0)
	RADIO ("Interpolation", 4)
	RADIOBUTTON ("None")
	RADIOBUTTON ("Parabolic")
	RADIOBUTTON ("Cubic")
	RADIOBUTTON ("Sinc70")
	RADIOBUTTON ("Sinc700")
	OK
DO
	EVERY_TO (Sound_to_PointProcess_extrema (OBJECT, GET_INTEGER ("Interpolation") - 1,
		GET_INTEGER ("Include maxima"), GET_INTEGER ("Include minima")))
END

FORM (Sound_to_PointProcess_periodic_cc, "From Sound to PointProcess (periodic, cc)", "Sound: To PointProcess (periodic, cc)...")
	POSITIVE ("Minimum pitch (Hz)", "75")
	POSITIVE ("Maximum pitch (Hz)", "600")
	OK
DO
	double fmin = GET_REAL ("Minimum pitch"), fmax = GET_REAL ("Maximum pitch");
	REQUIRE (fmax > fmin, "Maximum pitch must be greater than minimum pitch.");
	EVERY_TO (Sound_to_PointProcess_periodic_cc (OBJECT, fmin, fmax))
END

FORM (Sound_to_PointProcess_periodic_peaks, "From Sound to PointProcess (periodic, peaks)", "Sound: To PointProcess (periodic, peaks)...")
	POSITIVE ("Minimum pitch (Hz)", "75")
	POSITIVE ("Maximum pitch (Hz)", "600")
	BOOLEAN ("Include maxima", 1)
	BOOLEAN ("Include minima", 0)
	OK
DO
	double fmin = GET_REAL ("Minimum pitch"), fmax = GET_REAL ("Maximum pitch");
	REQUIRE (fmax > fmin, "Maximum pitch must be greater than minimum pitch.");
	EVERY_TO (Sound_to_PointProcess_periodic_peaks (OBJECT, fmin, fmax, GET_INTEGER ("Include maxima"), GET_INTEGER ("Include minima")))
END

FORM (Sound_to_PointProcess_zeroes, "Get zeroes", 0)
	BOOLEAN ("Include raisers", 1)
	BOOLEAN ("Include fallers", 0)
	OK
DO
	EVERY_TO (Sound_to_PointProcess_zeroes (OBJECT,
		GET_INTEGER ("Include raisers"), GET_INTEGER ("Include fallers")))
END

FORM (Sound_to_Spectrogram, "From Sound to Spectrogram", "Sound: To Spectrogram...")
	POSITIVE ("Window length (s)", "0.005")
	POSITIVE ("Maximum frequency (Hz)", "5000")
	POSITIVE ("Time step (s)", "0.002")
	POSITIVE ("Frequency step (Hz)", "20")
	RADIO ("Window shape", 6)
		RADIOBUTTON ("Square (rectangular)")
		RADIOBUTTON ("Hamming (raised sine-squared)")
		RADIOBUTTON ("Bartlett (triangular)")
		RADIOBUTTON ("Welch (parabolic)")
		RADIOBUTTON ("Hanning (sine-squared)")
		RADIOBUTTON ("Gaussian")
	OK
DO
	EVERY_TO (Sound_to_Spectrogram (OBJECT, GET_REAL ("Window length"),
		GET_REAL ("Maximum frequency"), GET_REAL ("Time step"),
		GET_REAL ("Frequency step"), GET_INTEGER ("Window shape") - 1, 8.0, 8.0))
END

DIRECT (Sound_to_Spectrum)
	return Melder_error ("Replace \"To Spectrum\" with \"To Spectrum (fft)\".\n"
		"The sign of the imaginary part of the resulting spectrum has changed.");
END

DIRECT (Sound_to_Spectrum_fft)
	EVERY_TO (Sound_to_Spectrum_fft (OBJECT))
END

FORM (Sound_to_TextGrid, "To TextGrid...", "Sound: To TextGrid...")
	SENTENCE ("Tier names", "Mary John bell")
	SENTENCE ("Point tiers", "bell")
	OK
DO
	EVERY_TO (TextGrid_create (((Sound) OBJECT) -> xmin, ((Sound) OBJECT) -> xmax,
		GET_STRING ("Tier names"), GET_STRING ("Point tiers")))
END

DIRECT (Sound_to_TextTier)
	EVERY_TO (TextTier_create (((Sound) OBJECT) -> xmin, ((Sound) OBJECT) -> xmax))
END

FORM (Sound_to_Wavelet, "From Sound to Wavelet", 0)
	RADIO ("Number of coefficients", 1)
		RADIOBUTTON ("4")
		RADIOBUTTON ("6")
		RADIOBUTTON ("8")
		RADIOBUTTON ("10")
		RADIOBUTTON ("12")
		RADIOBUTTON ("14")
		RADIOBUTTON ("16")
		RADIOBUTTON ("18")
		RADIOBUTTON ("20")
	OK
DO
	EVERY_TO (Sound_to_Wavelet (OBJECT, atol (GET_STRING ("Number of coefficients"))))
END

FORM (SoundInputPrefs, "Sound input preferences", "SoundRecorder")
	NATURAL ("Buffer size (MB)", "20")
	OK
SET_INTEGER ("Buffer size", SoundRecorder_getBufferSizePref_MB ())
DO
	long size = GET_INTEGER ("Buffer size");
	REQUIRE (size <= 1000, "Buffer size cannot exceed 1000 megabytes.")
	SoundRecorder_setBufferSizePref_MB (size);
END

FORM (SoundOutputPrefs, "Sound output preferences", 0)
	#if defined (sun) || defined (HPUX)
		RADIO ("Internal speaker", 1)
		RADIOBUTTON ("On")
		RADIOBUTTON ("Off")
	#endif
	#if defined (pietjepuk)
		REAL ("Output gain (0..1)", "0.3")
	#endif
	LABEL ("", "The following determines how sounds are played.")
	LABEL ("", "Between parentheses, you find what you can do simultaneously.")
	LABEL ("", "Decrease asynchronicity if sound plays with discontinuities.")
	OPTIONMENU ("Maximum asynchronicity", 4)
	OPTION ("Synchronous (nothing)")
	OPTION ("Calling back (view running cursor)")
	OPTION ("Interruptable (Escape key stops playing)")
	OPTION ("Asynchronous (anything)")
	REAL ("Silence before and after (s)", "0.0")
	OK
#if defined (sun) || defined (HPUX)
	SET_INTEGER ("Internal speaker", 2 - Melder_getUseInternalSpeaker ())
#endif
#if defined (pietjepuk)
	SET_REAL ("Output gain", Melder_getOutputGain ())
#endif
SET_INTEGER ("Maximum asynchronicity", Melder_getMaximumAsynchronicity () + 1);
SET_REAL ("Silence before and after", Melder_getZeroPadding ());
DO
	#if defined (sun) || defined (HPUX)
		Melder_setUseInternalSpeaker (2 - GET_INTEGER ("Internal speaker"));
	#endif
	#if defined (pietjepuk)
		Melder_setOutputGain (GET_REAL ("Gain"));
	#endif
	Melder_setMaximumAsynchronicity (GET_INTEGER ("Maximum asynchronicity") - 1);
	Melder_setZeroPadding (GET_REAL ("Silence before and after"));
END

FORM_WRITE (Sound_writeToAifcFile, "Write to AIFC file", 0, "aifc")
	if (! pr_LongSound_concatenate (file, Melder_AIFC)) return 0;
END

FORM_WRITE (Sound_writeToAiffFile, "Write to AIFF file", 0, "aiff")
	if (! pr_LongSound_concatenate (file, Melder_AIFF)) return 0;
END

FORM_WRITE (Sound_writeToRaw8bitUnsignedFile, "Write to raw 8-bit unsigned sound file", 0, "8uns")
	if (! Sound_writeToRaw8bitUnsignedFile (ONLY_OBJECT, file)) return 0;
END

FORM_WRITE (Sound_writeToRaw8bitSignedFile, "Write to raw 8-bit signed sound file", 0, "8sig")
	if (! Sound_writeToRaw8bitSignedFile (ONLY_OBJECT, file)) return 0;
END

FORM (Sound_writeToRawSoundFile, "Write to raw sound file", 0)
	LABEL ("", "Raw binary file:")
	TEXTFIELD ("Raw binary file", "")
	RADIO ("Encoding", 3)
		RADIOBUTTON ("Linear 8-bit signed")
		RADIOBUTTON ("Linear 8-bit unsigned")
		RADIOBUTTON ("Linear 16-bit big-endian")
		RADIOBUTTON ("Linear 16-bit little-endian")
	OK
DO
	struct MelderFile file;
	Melder_relativePathToFile (GET_STRING ("Raw binary file"), & file);
	if (! Sound_writeToRawSoundFile (ONLY_OBJECT, NULL, & file, GET_INTEGER ("Encoding"))) return 0;
END

FORM_WRITE (Sound_writeToKayFile, "Write to Kay sound file", 0, "kay")
	if (! Sound_writeToKayFile (ONLY_OBJECT, file)) return 0;
END

#ifdef macintosh
FORM_WRITE (Sound_writeToMacSoundFile, "Write to Macintosh sound file", 0, "macsound")
	if (! Sound_writeToMacSoundFile (ONLY_OBJECT, file)) return 0;
END
#endif

FORM_WRITE (Sound_writeToNextSunFile, "Write to NeXT/Sun file", 0, "au")
	if (! pr_LongSound_concatenate (file, Melder_NEXT_SUN)) return 0;
END

FORM_WRITE (Sound_writeToNistFile, "Write to NIST file", 0, "nist")
	if (! pr_LongSound_concatenate (file, Melder_NIST)) return 0;
END

FORM_WRITE (Sound_writeToSesamFile, "Write to Sesam file", 0, "sdf")
	if (! Sound_writeToSesamFile (ONLY_OBJECT, file)) return 0;
END

FORM_WRITE (Sound_writeToStereoAifcFile, "Write to stereo AIFC file", 0, "aifc")
	Sound s1 = NULL, s2 = NULL;
	WHERE (SELECTED) if (s1) s2 = OBJECT; else s1 = OBJECT;
	Melder_assert (s1 && s2);
	if (! Sound_writeToAudioFile16 (s1, s2, file, Melder_AIFC)) return 0;
END

FORM_WRITE (Sound_writeToStereoAiffFile, "Write to stereo AIFF file", 0, "aiff")
	Sound s1 = NULL, s2 = NULL;
	WHERE (SELECTED) if (s1) s2 = OBJECT; else s1 = OBJECT;
	Melder_assert (s1 && s2);
	if (! Sound_writeToAudioFile16 (s1, s2, file, Melder_AIFF)) return 0;
END

FORM_WRITE (Sound_writeToStereoNextSunFile, "Write to stereo NeXT/Sun file", 0, "au")
	Sound s1 = NULL, s2 = NULL;
	WHERE (SELECTED) if (s1) s2 = OBJECT; else s1 = OBJECT;
	Melder_assert (s1 && s2);
	if (! Sound_writeToAudioFile16 (s1, s2, file, Melder_NEXT_SUN)) return 0;
END

FORM_WRITE (Sound_writeToStereoNistFile, "Write to stereo NIST file", 0, "nist")
	Sound s1 = NULL, s2 = NULL;
	WHERE (SELECTED) if (s1) s2 = OBJECT; else s1 = OBJECT;
	Melder_assert (s1 && s2);
	if (! Sound_writeToAudioFile16 (s1, s2, file, Melder_NIST)) return 0;
END

FORM_WRITE (Sound_writeToStereoWavFile, "Write to stereo WAV file", 0, "wav")
	Sound s1 = NULL, s2 = NULL;
	WHERE (SELECTED) if (s1) s2 = OBJECT; else s1 = OBJECT;
	Melder_assert (s1 && s2);
	if (! Sound_writeToAudioFile16 (s1, s2, file, Melder_WAV)) return 0;
END

FORM_WRITE (Sound_writeToSunAudioFile, "Write to NeXT/Sun file", 0, "au")
	if (! pr_LongSound_concatenate (file, Melder_NEXT_SUN)) return 0;
END

FORM_WRITE (Sound_writeToWavFile, "Write to WAV file", 0, "wav")
	if (! pr_LongSound_concatenate (file, Melder_WAV)) return 0;
END

/***** STOP *****/

DIRECT (stopPlayingSound)
	Melder_stopPlaying (Melder_IMPLICIT);
END

/***** Help menus *****/

DIRECT (FilteringTutorial) Melder_help ("Filtering"); END

/***** file recognizers *****/

static Any macSoundOrEmptyFileRecognizer (int nread, const char *header, MelderFile file) {
	/***** No data in file? This may be a Macintosh sound file with only a resource fork. *****/
	(void) header;
	if (nread > 0) return NULL;
	#ifdef macintosh
		return Sound_readFromMacSoundFile (file);
	#else
		return Melder_errorp ("File %.200s is empty", MelderFile_messageName (file));   /* !!! */
	#endif
}

static Any soundFileRecognizer (int nread, const char *header, MelderFile file) {
	if (nread < 16) return NULL;
	if (strnequ (header, "FORM", 4) && strnequ (header + 8, "AIF", 3) ||
	    strnequ (header, "RIFF", 4) && (strnequ (header + 8, "WAVE", 4) || strnequ (header + 8, "CDDA", 4)) ||
	    strnequ (header, ".snd", 4) ||
	    strnequ (header, "NIST_1A", 7) && strnequ (header + 8, "   1024", 7))
		return Sound_readFromSoundFile (file);
	return NULL;
}

static Any sesamFileRecognizer (int nread, const char *header, MelderFile file) {
	char *fileName = MelderFile_name (file);
	(void) header;
	if (nread < 512 || ! strstr (fileName, ".sdf") && ! strstr (fileName, ".SDF")) return NULL;
	return Sound_readFromSesamFile (file);
}

static Any bellLabsFileRecognizer (int nread, const char *header, MelderFile file) {
	if (nread < 16 || ! strnequ (& header [0], "SIG\n", 4)) return NULL;
	return Sound_readFromBellLabsFile (file);
}

static Any kayFileRecognizer (int nread, const char *header, MelderFile file) {
	if (nread <= 12 || ! strnequ (& header [0], "FORMDS16", 8)) return NULL;
	return Sound_readFromKayFile (file);
}

/***** override play and records buttons in manuals *****/

static Sound melderSound, melderSoundFromFile, last;
static int recordProc (double duration) {
	if (last == melderSound) last = NULL;
	forget (melderSound);
	melderSound = Sound_recordFixedTime (1, 1.0, 0.5, 22050, duration);
	if (! melderSound) return 0;
	last = melderSound;
	return 1;
}
static int recordFromFileProc (MelderFile file) {
	if (last == melderSoundFromFile) last = NULL;
	forget (melderSoundFromFile);
	melderSoundFromFile = Data_readFromFile (file);
	if (! melderSoundFromFile) return 0;
	if (! Thing_member (melderSoundFromFile, classSound)) { forget (melderSoundFromFile); return 0; }
	last = melderSoundFromFile;
	Sound_play (melderSoundFromFile, NULL, NULL);
	return 1;
}
static void playProc (void) {
	if (melderSound) {
		Sound_play (melderSound, NULL, NULL);
		last = melderSound;
	}
}
static void playReverseProc (void) {
	/*if (melderSound) Sound_playReverse (melderSound);*/
}
static int publishPlayedProc (void) {
	if (! last) return 0;
	return Melder_publish (Data_copy (last));
}

/***** buttons *****/

void praat_uvafon_Sound_init (void);
void praat_uvafon_Sound_init (void) {

	Data_recognizeFileType (macSoundOrEmptyFileRecognizer);
	Data_recognizeFileType (soundFileRecognizer);
	Data_recognizeFileType (sesamFileRecognizer);
	Data_recognizeFileType (bellLabsFileRecognizer);
	Data_recognizeFileType (kayFileRecognizer);

	SoundEditor_prefs ();
	SoundRecorder_prefs ();
	FunctionEditor_prefs ();
	FunctionEditor_Sound_prefs ();
	FunctionEditor_SoundAnalysis_prefs ();

	Melder_setRecordProc (recordProc);
	Melder_setRecordFromFileProc (recordFromFileProc);
	Melder_setPlayProc (playProc);
	Melder_setPlayReverseProc (playReverseProc);
	Melder_setPublishPlayedProc (publishPlayedProc);

	praat_addMenuCommand ("Objects", "New", "Record mono Sound...", 0, 0, DO_Sound_record_mono);
	praat_addMenuCommand ("Objects", "New", "Record stereo Sound...", 0, 0, DO_Sound_record_stereo);
	praat_addMenuCommand ("Objects", "New", "Record Sound (fixed time)...", 0, praat_HIDDEN, DO_Sound_recordFixedTime);
	praat_addMenuCommand ("Objects", "New", "Sound", 0, 0, 0);
		praat_addMenuCommand ("Objects", "New", "Create Sound...", 0, 1, DO_Sound_create);
		praat_addMenuCommand ("Objects", "New", "Create Sound from tone complex...", 0, 1, DO_Sound_createFromToneComplex);

	praat_addMenuCommand ("Objects", "Read", "-- read sound --", 0, 0, 0);
	praat_addMenuCommand ("Objects", "Read", "Open long sound file...", 0, 'L', DO_LongSound_open);
	praat_addMenuCommand ("Objects", "Read", "Read two Sounds from stereo file...", 0, 0, DO_Sound_read2FromStereoFile);
	praat_addMenuCommand ("Objects", "Read", "Read from special sound file", 0, 0, 0);
		praat_addMenuCommand ("Objects", "Read", "Read Sound from raw Alaw file...", 0, 1, DO_Sound_readFromRawAlawFile);

	praat_addMenuCommand ("Objects", "Goodies", "Stop playing sound", 0, motif_ESCAPE, DO_stopPlayingSound);
	praat_addMenuCommand ("Objects", "Preferences", "-- sound prefs --", 0, 0, 0);
	praat_addMenuCommand ("Objects", "Preferences", "Sound input prefs...", 0, 0, DO_SoundInputPrefs);
	praat_addMenuCommand ("Objects", "Preferences", "Sound output prefs...", 0, 0, DO_SoundOutputPrefs);

	praat_addAction1 (classLongSound, 0, "LongSound help", 0, 0, DO_LongSound_help);
	praat_addAction1 (classLongSound, 1, "View", 0, 0, DO_LongSound_view);
	praat_addAction1 (classLongSound, 0, "Play part...", 0, 0, DO_LongSound_playPart);
	praat_addAction1 (classLongSound, 1, "Query -          ", 0, 0, 0);
		praat_TimeFunction_query_init (classLongSound);
		praat_addAction1 (classLongSound, 1, "Get number of samples", 0, 1, DO_LongSound_getNumberOfSamples);
		praat_addAction1 (classLongSound, 1, "Get sample period", 0, 1, DO_LongSound_getSamplePeriod);
		praat_addAction1 (classLongSound, 1, "Get sample rate", 0, 1, DO_LongSound_getSampleRate);
		praat_addAction1 (classLongSound, 1, "Get time from index...", 0, 1, DO_LongSound_getTimeFromIndex);
		praat_addAction1 (classLongSound, 1, "Get index from time...", 0, 1, DO_LongSound_getIndexFromTime);
	praat_addAction1 (classLongSound, 0, "Label & segment", 0, 0, 0);
	praat_addAction1 (classLongSound, 0, "To TextGrid...", 0, 0, DO_LongSound_to_TextGrid);
	praat_addAction1 (classLongSound, 0, "Convert to Sound", 0, 0, 0);
	praat_addAction1 (classLongSound, 0, "Extract part...", 0, 0, DO_LongSound_extractPart);
	praat_addAction1 (classLongSound, 0, "Write to AIFF file...", 0, 0, DO_LongSound_writeToAiffFile);
	praat_addAction1 (classLongSound, 0, "Write to AIFC file...", 0, 0, DO_LongSound_writeToAifcFile);
	praat_addAction1 (classLongSound, 0, "Write to WAV file...", 0, 0, DO_LongSound_writeToWavFile);
	praat_addAction1 (classLongSound, 0, "Write to Next/Sun file...", 0, 0, DO_LongSound_writeToNextSunFile);
	praat_addAction1 (classLongSound, 0, "Write to NIST file...", 0, 0, DO_LongSound_writeToNistFile);
	praat_addAction1 (classLongSound, 0, "Write part to audio file...", 0, 0, DO_LongSound_writePartToAudioFile);

	praat_addAction1 (classSound, 0, "Write to AIFF file...", 0, 0, DO_Sound_writeToAiffFile);
	praat_addAction1 (classSound, 0, "Write to AIFC file...", 0, 0, DO_Sound_writeToAifcFile);
	praat_addAction1 (classSound, 0, "Write to WAV file...", 0, 0, DO_Sound_writeToWavFile);
	praat_addAction1 (classSound, 0, "Write to Next/Sun file...", 0, 0, DO_Sound_writeToNextSunFile);
	praat_addAction1 (classSound, 0, "Write to Sun audio file...", 0, praat_HIDDEN, DO_Sound_writeToSunAudioFile);
	praat_addAction1 (classSound, 0, "Write to NIST file...", 0, 0, DO_Sound_writeToNistFile);
	#ifdef macintosh
	praat_addAction1 (classSound, 1, "Write to Mac sound file...", 0, praat_HIDDEN, DO_Sound_writeToMacSoundFile);
	#endif
	praat_addAction1 (classSound, 1, "Write to Kay sound file...", 0, 0, DO_Sound_writeToKayFile);
	praat_addAction1 (classSound, 1, "Write to Sesam file...", 0, praat_HIDDEN, DO_Sound_writeToSesamFile);
	#ifndef _WIN32
	praat_addAction1 (classSound, 1, "Write to raw sound file...", 0, 0, DO_Sound_writeToRawSoundFile);
	#endif
	praat_addAction1 (classSound, 1, "Write to raw 8-bit signed file...", 0, praat_HIDDEN, DO_Sound_writeToRaw8bitSignedFile);
	praat_addAction1 (classSound, 1, "Write to raw 8-bit unsigned file...", 0, praat_HIDDEN, DO_Sound_writeToRaw8bitUnsignedFile);
	praat_addAction1 (classSound, 2, "Write to stereo AIFF file...", 0, 0, DO_Sound_writeToStereoAiffFile);
	praat_addAction1 (classSound, 2, "Write to stereo AIFC file...", 0, 0, DO_Sound_writeToStereoAifcFile);
	praat_addAction1 (classSound, 2, "Write to stereo WAV file...", 0, 0, DO_Sound_writeToStereoWavFile);
	praat_addAction1 (classSound, 2, "Write to stereo Next/Sun file...", 0, 0, DO_Sound_writeToStereoNextSunFile);
	praat_addAction1 (classSound, 2, "Write to stereo NIST file...", 0, 0, DO_Sound_writeToStereoNistFile);
	praat_addAction1 (classSound, 0, "Sound help", 0, 0, DO_Sound_help);
	praat_addAction1 (classSound, 1, "Edit", 0, 0, DO_Sound_edit);
	praat_addAction1 (classSound, 0, "Play", 0, 0, DO_Sound_play);
	praat_addAction1 (classSound, 0, "Draw -               ", 0, 0, 0);
		praat_addAction1 (classSound, 0, "Draw...", 0, 1, DO_Sound_draw);
		praat_addAction1 (classSound, 0, "Draw clipped...", 0, 1, DO_Sound_drawClipped);
		praat_addAction1 (classSound, 0, "Draw samples...", 0, 1, DO_Sound_drawSamples);
	praat_addAction1 (classSound, 1, "Query -          ", 0, 0, 0);
		praat_TimeFunction_query_init (classSound);
		praat_addAction1 (classSound, 1, "Get number of samples", 0, 1, DO_Sound_getNumberOfSamples);
		praat_addAction1 (classSound, 1, "Get sample period", 0, 1, DO_Sound_getSamplePeriod);
		praat_addAction1 (classSound, 1, "Get sample rate", 0, 1, DO_Sound_getSampleRate);
		praat_addAction1 (classSound, 1, "Get time from index...", 0, 1, DO_Sound_getTimeFromIndex);
		praat_addAction1 (classSound, 1, "Get index from time...", 0, 1, DO_Sound_getIndexFromTime);
		praat_addAction1 (classSound, 1, "-- get content --", 0, 1, 0);
		praat_addAction1 (classSound, 1, "Get value at time...", 0, 1, DO_Sound_getValueAtTime);
		praat_addAction1 (classSound, 1, "Get value at index...", 0, 1, DO_Sound_getValueAtIndex);
		praat_addAction1 (classSound, 1, "-- get shape --", 0, 1, 0);
		praat_addAction1 (classSound, 1, "Get minimum...", 0, 1, DO_Sound_getMinimum);
		praat_addAction1 (classSound, 1, "Get time of minimum...", 0, 1, DO_Sound_getTimeOfMinimum);
		praat_addAction1 (classSound, 1, "Get maximum...", 0, 1, DO_Sound_getMaximum);
		praat_addAction1 (classSound, 1, "Get time of maximum...", 0, 1, DO_Sound_getTimeOfMaximum);
		praat_addAction1 (classSound, 1, "Get absolute extremum...", 0, 1, DO_Sound_getAbsoluteExtremum);
		praat_addAction1 (classSound, 1, "Get nearest zero crossing...", 0, 1, DO_Sound_getNearestZeroCrossing);
		praat_addAction1 (classSound, 1, "-- get statistics --", 0, 1, 0);
		praat_addAction1 (classSound, 1, "Get mean...", 0, 1, DO_Sound_getMean);
		praat_addAction1 (classSound, 1, "Get root-mean-square...", 0, 1, DO_Sound_getRootMeanSquare);
		praat_addAction1 (classSound, 1, "Get standard deviation...", 0, 1, DO_Sound_getStandardDeviation);
		praat_addAction1 (classSound, 1, "-- get energy --", 0, 1, 0);
		praat_addAction1 (classSound, 1, "Get energy...", 0, 1, DO_Sound_getEnergy);
		praat_addAction1 (classSound, 1, "Get power...", 0, 1, DO_Sound_getPower);
		praat_addAction1 (classSound, 1, "-- get energy in air --", 0, 1, 0);
		praat_addAction1 (classSound, 1, "Get energy in air", 0, 1, DO_Sound_getEnergyInAir);
		praat_addAction1 (classSound, 1, "Get power in air", 0, 1, DO_Sound_getPowerInAir);
		praat_addAction1 (classSound, 1, "Get intensity (dB)", 0, 1, DO_Sound_getIntensity_dB);
	praat_addAction1 (classSound, 0, "Modify -          ", 0, 0, 0);
		praat_addAction1 (classSound, 0, "Reverse", 0, 1, DO_Sound_reverse);
		praat_addAction1 (classSound, 0, "Formula...", 0, 1, DO_Sound_formula);
		praat_addAction1 (classSound, 0, "-- add & mul --", 0, 1, 0);
		praat_addAction1 (classSound, 0, "Add...", 0, 1, DO_Sound_add);
		praat_addAction1 (classSound, 0, "Subtract mean", 0, 1, DO_Sound_subtractMean);
		praat_addAction1 (classSound, 0, "Multiply...", 0, 1, DO_Sound_multiply);
		praat_addAction1 (classSound, 0, "Multiply by window...", 0, 1, DO_Sound_multiplyByWindow);
		praat_addAction1 (classSound, 0, "Scale...", 0, 1, DO_Sound_scale);
		praat_addAction1 (classSound, 0, "-- set --", 0, 1, 0);
		praat_addAction1 (classSound, 0, "Set value at index...", 0, 1, DO_Sound_setValueAtIndex);
		praat_addAction1 (classSound, 0, "-- modify hack --", 0, 1, 0);
		praat_addAction1 (classSound, 0, "Override sample rate...", 0, 1, DO_Sound_overrideSampleRate);
		praat_addAction1 (classSound, 0, "-- in-line filters --", 0, 1, 0);
		praat_addAction1 (classSound, -1, "In-line filters:", 0, 1, DO_Sound_overrideSampleRate);   /* BUG */
		praat_addAction1 (classSound, 0, "Filter with one formant (in-line)...", 0, 1, DO_Sound_filterWithOneFormantInline);
		praat_addAction1 (classSound, 0, "Pre-emphasize (in-line)...", 0, 1, DO_Sound_preemphasizeInline);
		praat_addAction1 (classSound, 0, "De-emphasize (in-line)...", 0, 1, DO_Sound_deemphasizeInline);
	praat_addAction1 (classSound, 0, "Label & segment -   ", 0, 0, 0);
		praat_addAction1 (classSound, 0, "To TextGrid...", 0, 1, DO_Sound_to_TextGrid);
		praat_addAction1 (classSound, 0, "-- to single tier --", 0, 1, 0);
		praat_addAction1 (classSound, 0, "To TextTier", 0, 1, DO_Sound_to_TextTier);
		praat_addAction1 (classSound, 0, "To IntervalTier", 0, 1, DO_Sound_to_IntervalTier);
	praat_addAction1 (classSound, 0, "Analyse", 0, 0, 0);
	praat_addAction1 (classSound, 0, "Periodicity -        ", 0, 0, 0);
		praat_addAction1 (classSound, 0, "To Pitch...", 0, 1, DO_Sound_to_Pitch);
		praat_addAction1 (classSound, 0, "To Pitch (ac)...", 0, 1, DO_Sound_to_Pitch_ac);
		praat_addAction1 (classSound, 0, "To Pitch (cc)...", 0, 1, DO_Sound_to_Pitch_cc);
		praat_addAction1 (classSound, 0, "To PointProcess (periodic, cc)...", 0, 1, DO_Sound_to_PointProcess_periodic_cc);
		praat_addAction1 (classSound, 0, "To PointProcess (periodic, peaks)...", 0, 1, DO_Sound_to_PointProcess_periodic_peaks);
		praat_addAction1 (classSound, 0, "-- hnr --", 0, 1, 0);
		praat_addAction1 (classSound, 0, "To Harmonicity (cc)...", 0, 1, DO_Sound_to_Harmonicity_cc);
		praat_addAction1 (classSound, 0, "To Harmonicity (ac)...", 0, 1, DO_Sound_to_Harmonicity_ac);
		praat_addAction1 (classSound, 0, "To Harmonicity (gne)...", 0, 1, DO_Sound_to_Harmonicity_gne);
	praat_addAction1 (classSound, 0, "Spectrum -", 0, 0, 0);
		praat_addAction1 (classSound, 0, "To Spectrum (fft)", 0, 1, DO_Sound_to_Spectrum_fft);
		praat_addAction1 (classSound, 0, "To Spectrum", 0, praat_DEPTH_1 + praat_HIDDEN, DO_Sound_to_Spectrum);
		praat_addAction1 (classSound, 0, "-- spectrotemporal --", 0, 1, 0);
		praat_addAction1 (classSound, 0, "To Spectrogram...", 0, 1, DO_Sound_to_Spectrogram);
		praat_addAction1 (classSound, 0, "To Wavelet...", 0, 1, DO_Sound_to_Wavelet);
		praat_addAction1 (classSound, 0, "To Cochleagram...", 0, 1, DO_Sound_to_Cochleagram);
		praat_addAction1 (classSound, 0, "To Cochleagram (edb)...", 0, praat_DEPTH_1 + praat_HIDDEN, DO_Sound_to_Cochleagram_edb);
	praat_addAction1 (classSound, 0, "Formants & LPC -     ", 0, 0, 0);
		praat_addAction1 (classSound, 0, "To Formant (burg)...", 0, 1, DO_Sound_to_Formant_burg);
		praat_addAction1 (classSound, 0, "To Formant (keep all)...", 0, 1, DO_Sound_to_Formant_keepAll);
		praat_addAction1 (classSound, 0, "To Formant (sl)...", 0, 1, DO_Sound_to_Formant_willems);
	praat_addAction1 (classSound, 0, "Points -          ", 0, 0, 0);
		praat_addAction1 (classSound, 0, "To PointProcess (extrema)...", 0, 1, DO_Sound_to_PointProcess_extrema);
		praat_addAction1 (classSound, 0, "To PointProcess (zeroes)...", 0, 1, DO_Sound_to_PointProcess_zeroes);
	praat_addAction1 (classSound, 0, "To Intensity...", 0, 0, DO_Sound_to_Intensity);
	praat_addAction1 (classSound, 0, "Manipulate", 0, 0, 0);
	praat_addAction1 (classSound, 0, "To Manipulation...", 0, 0, DO_Sound_to_Manipulation);
	praat_addAction1 (classSound, 0, "Synthesize", 0, 0, 0);
	praat_addAction1 (classSound, 0, "Convert -       ", 0, 0, 0);
		praat_addAction1 (classSound, 0, "Extract part...", 0, 1, DO_Sound_extractPart);
		praat_addAction1 (classSound, 0, "Resample...", 0, 1, DO_Sound_resample);
		praat_addAction1 (classSound, 0, "-- cast --", 0, 1, 0);
		praat_addAction1 (classSound, 0, "Down to Matrix", 0, 1, DO_Sound_to_Matrix);
	praat_addAction1 (classSound, 0, "Filter -       ", 0, 0, 0);
		praat_addAction1 (classSound, 0, "Filtering tutorial", 0, 1, DO_FilteringTutorial);
		praat_addAction1 (classSound, 0, "-- frequency-domain filter --", 0, 1, 0);
		praat_addAction1 (classSound, 0, "Filter (pass Hann band)...", 0, 1, DO_Sound_filter_passHannBand);
		praat_addAction1 (classSound, 0, "Filter (stop Hann band)...", 0, 1, DO_Sound_filter_stopHannBand);
		praat_addAction1 (classSound, 0, "Filter (formula)...", 0, 1, DO_Sound_filter_formula);
		praat_addAction1 (classSound, 0, "-- time-domain filter --", 0, 1, 0);
		praat_addAction1 (classSound, 0, "Filter (one formant)...", 0, 1, DO_Sound_filter_oneFormant);
		praat_addAction1 (classSound, 0, "Filter (pre-emphasis)...", 0, 1, DO_Sound_filter_preemphasis);
		praat_addAction1 (classSound, 0, "Filter (de-emphasis)...", 0, 1, DO_Sound_filter_deemphasis);
	praat_addAction1 (classSound, 0, "Enhance -       ", 0, 0, 0);
		praat_addAction1 (classSound, 0, "Lengthen (PSOLA)...", 0, 1, DO_Sound_lengthen_psola);
		praat_addAction1 (classSound, 0, "Deepen band modulation...", 0, 1, DO_Sound_deepenBandModulation);
	praat_addAction1 (classSound, 0, "Combine sounds -", 0, 0, 0);
		praat_addAction1 (classSound, 2, "Convolve", 0, 1, DO_Sounds_convolve);
		praat_addAction1 (classSound, 0, "Concatenate", 0, 1, DO_Sounds_concatenate);
		praat_addAction1 (classSound, 0, "Concatenate recoverably", 0, 1, DO_Sounds_concatenateRecoverably);
		praat_addAction1 (classSound, 2, "To ParamCurve", 0, 1, DO_Sounds_to_ParamCurve);
		praat_addAction1 (classSound, 2, "Cross-correlate...", 0, 1, DO_Sounds_crossCorrelate);

	praat_addAction2 (classLongSound, 0, classSound, 0, "Write to AIFF file...", 0, 0, DO_LongSound_Sound_writeToAiffFile);
	praat_addAction2 (classLongSound, 0, classSound, 0, "Write to AIFC file...", 0, 0, DO_LongSound_Sound_writeToAifcFile);
	praat_addAction2 (classLongSound, 0, classSound, 0, "Write to WAV file...", 0, 0, DO_LongSound_Sound_writeToWavFile);
	praat_addAction2 (classLongSound, 0, classSound, 0, "Write to NeXT/Sun file...", 0, 0, DO_LongSound_Sound_writeToNextSunFile);
	praat_addAction2 (classLongSound, 0, classSound, 0, "Write to NIST file...", 0, 0, DO_LongSound_Sound_writeToNistFile);
}

/* End of file praat_Sound.c */
