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

/*
 * pb 2002/12/16 corrected bug for cases in which format chunk follows data chunk in WAV file
 * pb 2003/09/12 Sound Designer II files
 * pb 2004/05/14 support for reading 24-bit and 32-bit audio files
 * pb 2004/11/12 writeShortToAudio can write single channels of stereo signal
 * pb 2004/11/15 fast reading of 16-bit audio files
 * pb 2006/12/13 32-bit IEEE float audio files
 * pb 2007/01/24 better error message for DVI ADPCM
 */

#include "melder.h"
#include "abcio.h"
#include "math.h"
#if defined (macintosh)
	#include <Resources.h>
#endif

/***** WRITING *****/

#define BYTES_PER_SAMPLE_PER_CHANNEL  2
#define BITS_PER_SAMPLE_PER_CHANNEL  16
#ifndef WAVE_FORMAT_PCM
	#define WAVE_FORMAT_PCM  0x0001
#endif
#define WAVE_FORMAT_IEEE_FLOAT  0x0003
#define WAVE_FORMAT_ALAW  0x0006
#define WAVE_FORMAT_MULAW  0x0007
#define WAVE_FORMAT_DVI_ADPCM 0x0011

int Melder_writeAudioFileHeader16 (FILE *f, int audioFileType, long sampleRate, long numberOfSamples, int numberOfChannels) {
	switch (audioFileType) {
		case Melder_AIFF: {
			long dataSize = numberOfSamples * BYTES_PER_SAMPLE_PER_CHANNEL * numberOfChannels;

			/* Form Chunk: contains all other chunks. */
			fwrite ("FORM", 1, 4, f);
			binputi4 (4 + (8 + 4) + (8 + 18) + (8 + 8 + dataSize), f);   /* Size of Form Chunk. */
			fwrite ("AIFF", 1, 4, f);   /* File type. */

			/* Format Version Chunk: 8 + 4 bytes. */
			fwrite ("FVER", 1, 4, f); binputi4 (4, f);
			binputi4 (0xA2805140, f); /* Time of version. */

			/* Common Chunk: 8 + 18 bytes. */
			fwrite ("COMM", 1, 4, f); binputi4 (18, f);
			binputi2 (numberOfChannels, f);
			binputi4 (numberOfSamples, f);
			binputi2 (BITS_PER_SAMPLE_PER_CHANNEL, f);
			binputr10 (sampleRate, f);

			/* Sound Data Chunk: 8 + 8 bytes + samples. */
			fwrite ("SSND", 1, 4, f); binputi4 (8 + dataSize, f);
			binputi4 (0, f);   /* Offset. */
			binputi4 (0, f);   /* Block size. */
		} break;
		case Melder_AIFC: {
			long dataSize = numberOfSamples * BYTES_PER_SAMPLE_PER_CHANNEL * numberOfChannels;

			/* Form Chunk: contains all other chunks. */
			fwrite ("FORM", 1, 4, f);
			binputi4 (4 + (8 + 4) + (8 + 24) + (8 + 8 + dataSize), f);   /* Size of Form Chunk. */
			fwrite ("AIFC", 1, 4, f);   /* File type. */

			/* Format Version Chunk: 8 + 4 bytes. */
			fwrite ("FVER", 1, 4, f); binputi4 (4, f);
			binputi4 (0xA2805140, f); /* Time of version. */

			/* Common Chunk: 8 + 24 bytes. */
			fwrite ("COMM", 1, 4, f); binputi4 (24, f);
			binputi2 (numberOfChannels, f);
			binputi4 (numberOfSamples, f);
			binputi2 (BITS_PER_SAMPLE_PER_CHANNEL, f);
			binputr10 (sampleRate, f);
			fwrite ("NONE", 1, 4, f);   /* Type of compression. */
			binputi2 (0, f);   /* Name of compression. */

			/* Sound Data Chunk: 8 + 8 bytes + samples. */
			fwrite ("SSND", 1, 4, f); binputi4 (8 + dataSize, f);
			binputi4 (0, f);   /* Offset. */
			binputi4 (0, f);   /* Block size. */
		} break;
		case Melder_WAV: {
			long dataSize = numberOfSamples * BYTES_PER_SAMPLE_PER_CHANNEL * numberOfChannels;

			/* RIFF Chunk: contains all other chunks. */
			fwrite ("RIFF", 1, 4, f);
			binputi4LE (4 + (12 + 16) + (4 + dataSize), f);
			fwrite ("WAVE", 1, 4, f);   /* File type. */

			/* Format Chunk: 8 + 16 bytes. */
			fwrite ("fmt ", 1, 4, f); binputi4LE (16, f);
			binputi2LE (WAVE_FORMAT_PCM, f);
			binputi2LE (numberOfChannels, f);
			binputi4LE (sampleRate, f);   /* Number of samples per second. */
			binputi4LE (sampleRate * BYTES_PER_SAMPLE_PER_CHANNEL * numberOfChannels, f);   /* Average number of bytes per second. */
			binputi2LE (BYTES_PER_SAMPLE_PER_CHANNEL * numberOfChannels, f);   /* Block alignment. */
			binputi2LE (BITS_PER_SAMPLE_PER_CHANNEL, f);   /* Bits per sample point. */

			/* Data Chunk: 8 bytes + samples. */
			fwrite ("data", 1, 4, f); binputi4LE (dataSize, f);
		} break;
		case Melder_NEXT_SUN: {
			fwrite (".snd", 1, 4, f);   /* Tag. */
			binputi4 (32, f);   /* Length of header. */
			binputi4 (numberOfSamples * 2 * numberOfChannels, f);   /* Length of data. */
			binputi4 (3, f);   /* 16-bits linear, not mu-law or A-law. */
			binputi4 (sampleRate, f);
			binputi4 (numberOfChannels, f);
			binputi4 (0, f);
			binputi4 (0, f);
		} break;
		case Melder_NIST: {
			char header [1024];
			memset (header, 0, 1024);
			sprintf (header, "NIST_1A\n   1024\n"
				"channel_count -i %d\n"
				"sample_count -i %ld\n"
				"sample_n_bytes -i 2\n"
				"sample_byte_format -s2 01\n" /* 01=LE 10=BE */
				"sample_coding -s3 pcm\n"
				"sample_rate -i %ld\n"
				"sample_min -i -32768\n"
				"sample_max -i 32767\n"
				"end_head\n", numberOfChannels, numberOfSamples, sampleRate);
			fwrite (header, 1, 1024, f);
		} break;
		case Melder_SOUND_DESIGNER_TWO: {
			return Melder_error ("Cannot yet write Sound Designer II files.");
		} break;
		default: return Melder_error ("Unknown audio file type %d.", audioFileType);
	}
	return 1;
}

static char *audioFileTypeString [] = { "none", "AIFF", "AIFC", "WAV", "NeXT/Sun", "NIST", "Sound Designer II" };
char * Melder_audioFileTypeString (int audioFileType) { return audioFileType > Melder_NUMBER_OF_AUDIO_FILE_TYPES ? "unknown" : audioFileTypeString [audioFileType]; }
static char *macAudioFileType [1+Melder_NUMBER_OF_AUDIO_FILE_TYPES]
	= { "", "AIFF", "AIFC", "WAVE", "ULAW", "NIST", "Sd2f" };
char * Melder_macAudioFileType (int audioFileType) { return macAudioFileType [audioFileType]; }
static char *winAudioFileExtension [1+Melder_NUMBER_OF_AUDIO_FILE_TYPES]
	= { "", ".aiff", ".aifc", ".wav", ".au", ".nist", ".sd2" };
char * Melder_winAudioFileExtension (int audioFileType) { return winAudioFileExtension [audioFileType]; }
static int defaultAudioFileEncoding16 [1+Melder_NUMBER_OF_AUDIO_FILE_TYPES]
	= { 0, Melder_LINEAR_16_BIG_ENDIAN, Melder_LINEAR_16_BIG_ENDIAN, Melder_LINEAR_16_LITTLE_ENDIAN,
	     Melder_LINEAR_16_BIG_ENDIAN, Melder_LINEAR_16_LITTLE_ENDIAN, Melder_LINEAR_16_BIG_ENDIAN };
int Melder_defaultAudioFileEncoding16 (int audioFileType) { return defaultAudioFileEncoding16 [audioFileType]; }

int MelderFile_writeAudioFile16 (MelderFile file, int audioFileType, const short *buffer, long sampleRate, long numberOfSamples, int numberOfChannels) {
	MelderFile_create (file, macAudioFileType [audioFileType], "PpgB", winAudioFileExtension [audioFileType]);
	if (file -> filePointer) {
		Melder_writeAudioFileHeader16 (file -> filePointer, audioFileType, sampleRate, numberOfSamples, numberOfChannels);
		Melder_writeShortToAudio (file -> filePointer, numberOfChannels, defaultAudioFileEncoding16 [audioFileType], buffer, numberOfSamples);
	}
	MelderFile_close (file);
	iferror return Melder_error ("(Melder_writeAudioFile16:) File not written.");
	return 1;
}

/***** READING *****/

int Melder_bytesPerSamplePoint (int encoding) {
	return
		encoding == Melder_LINEAR_16_BIG_ENDIAN || encoding == Melder_LINEAR_16_LITTLE_ENDIAN ? 2 :
		encoding == Melder_LINEAR_24_BIG_ENDIAN || encoding == Melder_LINEAR_24_LITTLE_ENDIAN ? 3 :
		encoding == Melder_LINEAR_32_BIG_ENDIAN || encoding == Melder_LINEAR_32_LITTLE_ENDIAN ||
		encoding == Melder_IEEE_FLOAT_32_BIG_ENDIAN || encoding == Melder_IEEE_FLOAT_32_LITTLE_ENDIAN ? 4 :
		1;
}

static int ulaw2linear [] = 
      { -32124, -31100, -30076, -29052, -28028, -27004, -25980, -24956,
        -23932, -22908, -21884, -20860, -19836, -18812, -17788, -16764,
        -15996, -15484, -14972, -14460, -13948, -13436, -12924, -12412,
        -11900, -11388, -10876, -10364,  -9852,  -9340,  -8828,  -8316,
         -7932,  -7676,  -7420,  -7164,  -6908,  -6652,  -6396,  -6140,
         -5884,  -5628,  -5372,  -5116,  -4860,  -4604,  -4348,  -4092,
         -3900,  -3772,  -3644,  -3516,  -3388,  -3260,  -3132,  -3004,
         -2876,  -2748,  -2620,  -2492,  -2364,  -2236,  -2108,  -1980,
         -1884,  -1820,  -1756,  -1692,  -1628,  -1564,  -1500,  -1436,
         -1372,  -1308,  -1244,  -1180,  -1116,  -1052,   -988,   -924,
          -876,   -844,   -812,   -780,   -748,   -716,   -684,   -652,
          -620,   -588,   -556,   -524,   -492,   -460,   -428,   -396,
          -372,   -356,   -340,   -324,   -308,   -292,   -276,   -260,
          -244,   -228,   -212,   -196,   -180,   -164,   -148,   -132,
          -120,   -112,   -104,    -96,    -88,    -80,    -72,    -64,
           -56,    -48,    -40,    -32,    -24,    -16,     -8,      0,
         32124,  31100,  30076,  29052,  28028,  27004,  25980,  24956,
         23932,  22908,  21884,  20860,  19836,  18812,  17788,  16764,
         15996,  15484,  14972,  14460,  13948,  13436,  12924,  12412,
         11900,  11388,  10876,  10364,   9852,   9340,   8828,   8316,
          7932,   7676,   7420,   7164,   6908,   6652,   6396,   6140,
          5884,   5628,   5372,   5116,   4860,   4604,   4348,   4092,
          3900,   3772,   3644,   3516,   3388,   3260,   3132,   3004,
          2876,   2748,   2620,   2492,   2364,   2236,   2108,   1980,
          1884,   1820,   1756,   1692,   1628,   1564,   1500,   1436,
          1372,   1308,   1244,   1180,   1116,   1052,    988,    924,
           876,    844,    812,    780,    748,    716,    684,    652,
           620,    588,    556,    524,    492,    460,    428,    396,
           372,    356,    340,    324,    308,    292,    276,    260,
           244,    228,    212,    196,    180,    164,    148,    132,
           120,    112,    104,     96,     88,     80,     72,     64,
            56,     48,     40,     32,     24,     16,      8,      0
       };

static short alaw2linear[] = 
{
   -5504,  -5248,  -6016,  -5760,  -4480,  -4224,  -4992,  -4736,
   -7552,  -7296,  -8064,  -7808,  -6528,  -6272,  -7040,  -6784,
   -2752,  -2624,  -3008,  -2880,  -2240,  -2112,  -2496,  -2368,
   -3776,  -3648,  -4032,  -3904,  -3264,  -3136,  -3520,  -3392,
  -22016, -20992, -24064, -23040, -17920, -16896, -19968, -18944,
  -30208, -29184, -32256, -31232, -26112, -25088, -28160, -27136,
  -11008, -10496, -12032, -11520,  -8960,  -8448,  -9984,  -9472,
  -15104, -14592, -16128, -15616, -13056, -12544, -14080, -13568,
    -344,   -328,   -376,   -360,   -280,   -264,   -312,   -296,
    -472,   -456,   -504,   -488,   -408,   -392,   -440,   -424,
     -88,    -72,   -120,   -104,    -24,     -8,    -56,    -40,
    -216,   -200,   -248,   -232,   -152,   -136,   -184,   -168,
   -1376,  -1312,  -1504,  -1440,  -1120,  -1056,  -1248,  -1184,
   -1888,  -1824,  -2016,  -1952,  -1632,  -1568,  -1760,  -1696,
    -688,   -656,   -752,   -720,   -560,   -528,   -624,   -592,
    -944,   -912,  -1008,   -976,   -816,   -784,   -880,   -848,
    5504,   5248,   6016,   5760,   4480,   4224,   4992,   4736,
    7552,   7296,   8064,   7808,   6528,   6272,   7040,   6784,
    2752,   2624,   3008,   2880,   2240,   2112,   2496,   2368,
    3776,   3648,   4032,   3904,   3264,   3136,   3520,   3392,
   22016,  20992,  24064,  23040,  17920,  16896,  19968,  18944,
   30208,  29184,  32256,  31232,  26112,  25088,  28160,  27136,
   11008,  10496,  12032,  11520,   8960,   8448,   9984,   9472,
   15104,  14592,  16128,  15616,  13056,  12544,  14080,  13568,
     344,    328,    376,    360,    280,    264,    312,    296,
     472,    456,    504,    488,    408,    392,    440,    424,
      88,     72,    120,    104,     24,      8,     56,     40,
     216,    200,    248,    232,    152,    136,    184,    168,
    1376,   1312,   1504,   1440,   1120,   1056,   1248,   1184,
    1888,   1824,   2016,   1952,   1632,   1568,   1760,   1696,
     688,    656,    752,    720,    560,    528,    624,    592,
     944,    912,   1008,    976,    816,    784,    880,    848
};

static int Melder_checkAiffFile (FILE *f, int *numberOfChannels, int *encoding,
	double *sampleRate, long *startOfData, long *numberOfSamples)
{
	char data [8], chunkID [4];
	int commonChunkPresent = FALSE, dataChunkPresent = FALSE, isAifc = TRUE, numberOfBitsPerSamplePoint;
	long chunkSize, i;

	/* Read header of AIFF(-C) file: 12 bytes. */

	if (fread (data, 1, 4, f) < 4) return Melder_error ("File too small: no FORM statement.");
	if (! strnequ (data, "FORM", 4)) return Melder_error ("Not an AIFF or AIFC file (FORM statement expected).");
	if (fread (data, 1, 4, f) < 4) return Melder_error ("File too small: no size of FORM chunk.");
	if (fread (data, 1, 4, f) < 4) return Melder_error ("File too small: no file type info (expected AIFF or AIFC).");
	if (! strnequ (data, "AIFF", 4) && ! strnequ (data, "AIFC", 4)) return Melder_error ("Not an AIFF or AIFC file (wrong file type info).");
	if (strnequ (data, "AIFF", 4)) isAifc = FALSE;

	/* Search for Common Chunk and Data Chunk. */

	while (fread (chunkID, 1, 4, f) == 4) {
		chunkSize = bingeti4 (f);
		if (chunkSize & 1) ++ chunkSize;   /* Round up to nearest even number. */
		/* IN SOUND FILES PRODUCED BY THE SGI'S soundeditor PROGRAM, */
		/* THE COMMON CHUNK HAS A chunkSize OF 18 INSTEAD OF 38, */
		/* ALTHOUGH THE COMMON CHUNK CONTAINS */
		/* THE 20-BYTE SEQUENCE "\016not compressed\0". */
		/* START FIX OF FOREIGN BUG */
		if(strnequ(chunkID,"NONE",4)&&
			(chunkSize==(14<<24)+('n'<<16)+('o'<<8)+'t'||chunkSize==('t'<<24)+('o'<<16)+('n'<<8)+14))
		{Melder_casual("Ha! a buggy SGI \"soundeditor\" file...");for(i=1;i<=20/*diff*/-8/*header*/;i++)fread(data,1,1,f);continue;}
		/* FINISH FIX OF FOREIGN BUG */
		if (strnequ (chunkID, "COMM", 4)) {
			/*
			 * Found a Common Chunk.
			 */
			commonChunkPresent = TRUE;
			*numberOfChannels = bingeti2 (f);
			if (*numberOfChannels < 1) return Melder_error ("%d sound channels is too few.", *numberOfChannels);
			if (*numberOfChannels > 2) return Melder_error ("%d sound channels is too many.", *numberOfChannels);
			*numberOfSamples = bingeti4 (f);
			if (*numberOfSamples <= 0) return Melder_error ("Too few samples (%ld).", *numberOfSamples);
			if (*numberOfSamples >= 1000000000) return Melder_error ("Too many samples (%ld).", *numberOfSamples);
			numberOfBitsPerSamplePoint = bingeti2 (f);
			if (numberOfBitsPerSamplePoint > 32) return Melder_error ("%d bits per sample is too many (maximum is 32).", numberOfBitsPerSamplePoint);
			*encoding =
				numberOfBitsPerSamplePoint > 24 ? Melder_LINEAR_32_BIG_ENDIAN :
				numberOfBitsPerSamplePoint > 16 ? Melder_LINEAR_24_BIG_ENDIAN :
				numberOfBitsPerSamplePoint > 8 ? Melder_LINEAR_16_BIG_ENDIAN :
				Melder_LINEAR_8_SIGNED;
			*sampleRate = bingetr10 (f);
			if (*sampleRate <= 0.0) return Melder_error ("Wrong sampling frequency (%.17g Hz).", *sampleRate);
			if (isAifc) {
				/*
				 * Read compression data; should be "NONE" or "sowt".
				 */
				if (fread (data, 1, 4, f) < 4) return Melder_error ("File too small: no compression info.");
				if (! strnequ (data, "NONE", 4) && ! strnequ (data, "sowt", 4)) return Melder_error ("Cannot read compressed AIFC files (compression type %c%c%c%c).",
					data [0], data [1], data [2], data [3]);
				if (strnequ (data, "sowt", 4))
					*encoding =
						numberOfBitsPerSamplePoint > 24 ? Melder_LINEAR_32_LITTLE_ENDIAN :
						numberOfBitsPerSamplePoint > 16 ? Melder_LINEAR_24_LITTLE_ENDIAN :
						numberOfBitsPerSamplePoint > 8 ? Melder_LINEAR_16_LITTLE_ENDIAN :
						Melder_LINEAR_8_SIGNED;
				/*
				 * Read rest of compression info.
				 */
				for (i = 23; i <= chunkSize; i ++)
					if (fread (data, 1, 1, f) < 1) return Melder_error ("File too small: expected chunk of %ld bytes, but found %ld.", chunkSize, i + 22);
			}
		} else if (strnequ (chunkID, "SSND", 4)) {
			/*
			 * Found a Data Chunk.
			 */
			dataChunkPresent = TRUE;
			*startOfData = ftell (f) + 8;   /* Ignore "offset" (4 bytes) and "blocksize" (4 bytes). */
			if (commonChunkPresent) break;   /* Optimization: do not read whole data chunk if we have already read the common chunk. */
		} else /* Ignore Version Chunk and unknown chunks. */
			for (i = 1; i <= chunkSize; i ++)
				if (fread (data, 1, 1, f) < 1) return Melder_error ("File too small: expected %ld bytes, but found %ld.", chunkSize, i);
	}

	if (! commonChunkPresent) return Melder_error ("Found no Common Chunk.");
	if (! dataChunkPresent) return Melder_error ("Found no Data Chunk.");

	return 1;
}

static int Melder_checkWavFile (FILE *f, int *numberOfChannels, int *encoding,
	double *sampleRate, long *startOfData, long *numberOfSamples)
{
	char data [8], chunkID [4];
	int formatChunkPresent = FALSE, dataChunkPresent = FALSE, numberOfBitsPerSamplePoint = -1;
	long chunkSize, dataChunkSize = -1, i;

	if (fread (data, 1, 4, f) < 4) return Melder_error ("File too small: no RIFF statement.");
	if (! strnequ (data, "RIFF", 4)) return Melder_error ("Not a WAV file (RIFF statement expected).");
	if (fread (data, 1, 4, f) < 4) return Melder_error ("File too small: no size of RIFF chunk.");
	if (fread (data, 1, 4, f) < 4) return Melder_error ("File too small: no file type info (expected WAVE statement).");
	if (! strnequ (data, "WAVE", 4) && ! strnequ (data, "CDDA", 4)) return Melder_error ("Not a WAVE or CD audio file (wrong file type info).");

	/* Search for Format Chunk and Data Chunk. */

	while (fread (chunkID, 1, 4, f) == 4) {
		chunkSize = bingeti4LE (f);
		if (strnequ (chunkID, "fmt ", 4)) {
			/*
			 * Found a Format Chunk.
			 */
			int winEncoding = bingeti2LE (f);
			formatChunkPresent = TRUE;			
			*numberOfChannels = bingeti2LE (f);
			if (*numberOfChannels < 1) return Melder_error ("%d sound channels is too few.", *numberOfChannels);
			if (*numberOfChannels > 2) return Melder_error ("%d sound channels is too many.", *numberOfChannels);
			*sampleRate = (double) bingeti4LE (f);
			if (*sampleRate <= 0.0) return Melder_error ("Wrong sampling frequency (%.17g Hz).", *sampleRate);
			(void) bingeti4LE (f);   /* avgBytesPerSec */
			(void) bingeti2LE (f);   /* blockAlign */
			numberOfBitsPerSamplePoint = bingeti2LE (f);
			if (numberOfBitsPerSamplePoint == 0) numberOfBitsPerSamplePoint = 16;   /* Default. */
			else if (numberOfBitsPerSamplePoint < 4) return Melder_error ("%d bits per sample is too few (minimum is 4).", numberOfBitsPerSamplePoint);
			else if (numberOfBitsPerSamplePoint > 32) return Melder_error ("%d bits per sample is too many (maximum is 32).", numberOfBitsPerSamplePoint);
			switch (winEncoding) {
				case WAVE_FORMAT_PCM:
					*encoding =
						numberOfBitsPerSamplePoint > 24 ? Melder_LINEAR_32_LITTLE_ENDIAN :
						numberOfBitsPerSamplePoint > 16 ? Melder_LINEAR_24_LITTLE_ENDIAN :
						numberOfBitsPerSamplePoint > 8 ? Melder_LINEAR_16_LITTLE_ENDIAN :
						Melder_LINEAR_8_UNSIGNED;
					break;
				case WAVE_FORMAT_IEEE_FLOAT:
					*encoding = Melder_IEEE_FLOAT_32_LITTLE_ENDIAN;
					break;
				case WAVE_FORMAT_ALAW:
					*encoding = Melder_ALAW;
					break;
				case WAVE_FORMAT_MULAW:
					*encoding = Melder_MULAW;
					break;
				case WAVE_FORMAT_DVI_ADPCM:
					return Melder_error ("Cannot read lossy compressed audio files (this one is DVI ADPCM).\n"
						"Please use uncompressed audio files. If you must open this file,\n"
						"please use an audio converter program to convert it first to normal (PCM) WAV format\n"
						"(Praat may have difficulty analysing the poor recording, though).");
				default:
					return Melder_error ("Unsupported Windows audio encoding %d.", winEncoding);
			}
			for (i = 17; i <= chunkSize; i ++)
				if (fread (data, 1, 1, f) < 1) return Melder_error ("File too small: expected %ld bytes in fmt chunk, but found %ld.", chunkSize, i);
		} else if (strnequ (chunkID, "data", 4)) {
			/*
			 * Found a Data Chunk.
			 */
			dataChunkPresent = TRUE;
			dataChunkSize = chunkSize;
			*startOfData = ftell (f);
			if (formatChunkPresent) break;   /* Optimization: do not read whole data chunk if we have already read the format chunk. */
		} else {   /* Ignore other chunks. */
		    /*Melder_warning ("chunk %c%c%c%c",chunkID[0],chunkID[1],chunkID[2],chunkID[3]);*/
			for (i = 1; i <= chunkSize; i ++)
				if (fread (data, 1, 1, f) < 1) return Melder_error ("File too small: expected %ld bytes, but found %ld.", chunkSize, i);
		}
	}

	if (! formatChunkPresent) return Melder_error ("Found no Format Chunk.");
	if (! dataChunkPresent) return Melder_error ("Found no Data Chunk.");
	Melder_assert (numberOfBitsPerSamplePoint != -1 && dataChunkSize != -1);
	*numberOfSamples = dataChunkSize / *numberOfChannels / ((numberOfBitsPerSamplePoint + 7) / 8);

	return 1;
}

static int Melder_checkNextSunFile (FILE *f, int *numberOfChannels, int *encoding,
	double *sampleRate, long *startOfData, long *numberOfSamples)
{
	long dataSize, sunEncoding, skip;
	char tag [4];
	fread (tag, 1, 4, f);
	if (strncmp (tag, ".snd", 4)) return Melder_error ("Not a Sun audio file.");
	*startOfData = bingeti4 (f);
	if (*startOfData < 24 || *startOfData > 320)
		return Melder_error ("Sorry, cannot read header of audio file. Length %ld.", *startOfData);
	dataSize = bingeti4 (f);
	if (dataSize <= 0) {
		/*
		 * Incorrect information. Get it from file length.
		 */
		long save = ftell (f);
		fseek (f, 0, SEEK_END);
		dataSize = ftell (f) - *startOfData;
		fseek (f, save, SEEK_SET);
	}
	sunEncoding = bingeti4 (f);
	switch (sunEncoding) {
		case 1: *encoding = Melder_MULAW; break;
		case 2: *encoding = Melder_LINEAR_8_SIGNED; break;
		case 3: *encoding = Melder_LINEAR_16_BIG_ENDIAN; break;
		case 27: *encoding = Melder_ALAW; break;
		default: return Melder_error ("Sorry, cannot translate audio file encoding %ld.", sunEncoding);
	}
	*sampleRate = bingeti4 (f);
	if (*sampleRate <= 0) return Melder_error ("Impossible sampling frequency %ld Hertz.", *sampleRate);
	*numberOfChannels = bingeti4 (f);
	if (*numberOfChannels < 1 || *numberOfChannels > 2)
		return Melder_error ("Wrong number of channels in audio file (%d).", *numberOfChannels);
	*numberOfSamples = dataSize / Melder_bytesPerSamplePoint (*encoding) / *numberOfChannels;
	skip = *startOfData - 24;
	while (skip -- > 0) (void) fgetc (f);
	return 1;
}

static int nistGetValue (const char *header, const char *object, double *rval, char *sval) {
	char obj [30], type [10], *match = strstr (header, object);
	if (! match) return 0;
	if (sscanf (match, "%s%s%s", obj, type, sval) != 3) return 0;
	if (strequ (type, "-i") || strequ (type, "-r")) *rval = atof (sval);
	else if (strncmp (type, "-s", 2)) return 0;
	return 1;
}
static int Melder_checkNistFile (FILE *f, int *numberOfChannels, int *encoding,
	double *sampleRate, long *startOfData, long *numberOfSamples)
{
	char header [1024], sval [100];
 	double rval;
	int numberOfBytesPerSamplePoint;
	if (fread (header, 1, 1024, f) != 1024)
		return Melder_error ("Cannot read NISTheader.");
	if (! strnequ (header, "NIST_1A", 7))
		return Melder_error ("Not a NIST sound file.");
	*startOfData = atol (header + 9);
	if (! nistGetValue (header, "sample_count", & rval, sval) || rval < 1)
		return Melder_error ("Incorrect number of samples in NIST file.");
	*numberOfSamples = rval;
	if (! nistGetValue (header, "sample_n_bytes", & rval, sval) || rval < 1 || rval > 2)
		return Melder_error ("Incorrect number of bytes per sample (should be 1 or 2).");
	numberOfBytesPerSamplePoint = rval;
	if (! nistGetValue (header, "channel_count", & rval, sval) || rval < 1)
		return Melder_error ("Incorrect number of channels.");
	*numberOfChannels = rval;
	if (*numberOfChannels > 2)
		return Melder_error ("Too many channels in NIST file.");
	if (! nistGetValue (header, "sample_rate", sampleRate, sval) || *sampleRate < 1)
		return Melder_error ("Incorrect sampling frequency %f Hz.", *sampleRate);
	*encoding = Melder_LINEAR_16_BIG_ENDIAN;
	if (nistGetValue (header, "sample_byte_format", & rval, sval) && strequ (sval, "01"))
		*encoding = Melder_LINEAR_16_LITTLE_ENDIAN;
	if (numberOfBytesPerSamplePoint == 1)
		*encoding = Melder_LINEAR_8_SIGNED;
	if (nistGetValue (header, "sample_coding", & rval, sval)) {
		if (strnequ (sval, "ulaw", 4))
			*encoding = Melder_MULAW;
		else if (strstr (sval, "embedded-shorten-v"))
			if (nistGetValue (header, "database_id", & rval, sval) && strequ (sval, "POLYPHONE-NL"))
				*encoding = Melder_POLYPHONE;
			else
				*encoding = Melder_SHORTEN;
		else if (strnequ (sval, "alaw", 4))   /* Must be after previous, because some files have "alaw,embedded..." */
			*encoding = Melder_ALAW;
	}
	return 1;
}
#ifdef macintosh
static double Melder_getNumberFromStrResource (int resourceID) {
	char *pstring;
	char string [256];
	int length;
	Handle han = Get1Resource ('STR ', resourceID);
	if (! han) { Melder_error ("Resource %d not found.", resourceID); return 0.0; }
	pstring = (char *) (*han);
	length = (unsigned char) pstring [0];
	strncpy (string, & pstring [1], length);
	string [length] = 0;
	ReleaseResource (han);
	return atof (string);
}
static int MelderFile_checkSoundDesignerTwoFile (MelderFile file, int *numberOfChannels, int *encoding,
	double *sampleRate, long *startOfData, long *numberOfSamples)
{
	FSSpec fspec;
	int path = -1, sampleSize;
	Melder_fileToMac (file, & fspec);
	path = FSpOpenResFile (& fspec, fsRdPerm);   /* Open resource fork; there is the header info. */
	if (path == -1) { Melder_error ("Cannot open resource fork."); goto end; }
	sampleSize = Melder_getNumberFromStrResource (1000);
	iferror { Melder_error ("No sample size information."); goto end; }
	if (sampleSize < 1 || sampleSize > 4) {
		Melder_error ("Wrong sample size: %d (should be between 1 and 4).", sampleSize);
		goto end;
	}
	*encoding =
		sampleSize == 1 ? Melder_LINEAR_8_SIGNED :
		sampleSize == 2 ? Melder_LINEAR_16_BIG_ENDIAN :
		sampleSize == 3 ? Melder_LINEAR_24_BIG_ENDIAN :
		Melder_LINEAR_32_BIG_ENDIAN;
	*sampleRate = Melder_getNumberFromStrResource (1001);
	iferror { Melder_error ("No sampling frequency information."); goto end; }
	if (*sampleRate <= 0.0) {
		Melder_error ("Wrong sampling frequency: %s", Melder_double (*sampleRate));
		goto end;
	}
	*numberOfChannels = Melder_getNumberFromStrResource (1002);
	iferror { Melder_error ("No channel number information."); goto end; }
	if (*numberOfChannels != 1 && *numberOfChannels != 2) {
		Melder_error ("Wrong number of channels: %s", Melder_integer (*numberOfChannels));
		goto end;
	}
	*numberOfSamples = MelderFile_length (file) / sampleSize / *numberOfChannels;
	if (*numberOfSamples <= 0) {
		Melder_error ("No samples in file.");
		goto end;
	}
	*startOfData = 0;
end:
	if (path != -1) CloseResFile (path);
	iferror return Melder_error ("Sound Designer II file not read.");
	return 1;
}
#endif

int MelderFile_checkSoundFile (MelderFile file, int *numberOfChannels, int *encoding,
	double *sampleRate, long *startOfData, long *numberOfSamples)
{
	char data [16];
	FILE *f = file -> filePointer;
	if (f == NULL || fread (data, 1, 16, f) < 16) return 0;
	rewind (f);
	if (strnequ (data, "FORM", 4) && strnequ (data + 8, "AIFF", 4)) {
		Melder_checkAiffFile (f, numberOfChannels, encoding, sampleRate, startOfData, numberOfSamples);
		iferror return 0;
		return Melder_AIFF;
	}
	if (strnequ (data, "FORM", 4) && strnequ (data + 8, "AIFC", 4)) {
		Melder_checkAiffFile (f, numberOfChannels, encoding, sampleRate, startOfData, numberOfSamples);
		iferror return 0;
		return Melder_AIFC;
	}
	if (strnequ (data, "RIFF", 4) && (strnequ (data + 8, "WAVE", 4) || strnequ (data + 8, "CDDA", 4))) {
		Melder_checkWavFile (f, numberOfChannels, encoding, sampleRate, startOfData, numberOfSamples);
		iferror return 0;
		return Melder_WAV;
	}
	if (strnequ (data, ".snd", 4)) {
		Melder_checkNextSunFile (f, numberOfChannels, encoding, sampleRate, startOfData, numberOfSamples);
		iferror return 0;
		return Melder_NEXT_SUN;
	}
	if (strnequ (data, "NIST_1A", 7)) {
		Melder_checkNistFile (f, numberOfChannels, encoding, sampleRate, startOfData, numberOfSamples);
		iferror return 0;
		return Melder_NIST;
	}
	#ifdef macintosh
		if (MelderFile_getMacType (file) == 'Sd2f') {
			MelderFile_checkSoundDesignerTwoFile (file, numberOfChannels, encoding, sampleRate, startOfData, numberOfSamples);
			iferror return 0;
			return Melder_SOUND_DESIGNER_TWO;
		}
	#endif
	return 0;   /* Not a recognized sound file. */
}

int Melder_readAudioToFloat (FILE *f, int numberOfChannels, int encoding, float *leftBuffer, float *rightBuffer, long numberOfSamples) {
	long i;
	Melder_assert (sizeof (char) == 1 && sizeof (short) == 2 && sizeof (float) == 4);
	switch (encoding) {
		case Melder_LINEAR_8_SIGNED:
			if (numberOfChannels == 1)
				for (i = 1; i <= numberOfSamples; i ++) {
					signed char value;
					if (! fread (& value, 1, 1, f)) { Melder_error ("File too small (mono 8-bit)."); goto end; }
					leftBuffer [i] = value * (1.0f / 128);
				}
			else if (rightBuffer)
				for (i = 1; i <= numberOfSamples; i ++) {
					signed char left, right;
					if (! fread (& left, 1, 1, f) || ! fread (& right, 1, 1, f)) { Melder_error ("File too small (stereo 8-bit)."); goto end; }
					leftBuffer [i] = left * (1.0f / 128);
					rightBuffer [i] = right * (1.0f / 128);
				}
			else
				for (i = 1; i <= numberOfSamples; i ++) {
					signed char left, right;
					if (! fread (& left, 1, 1, f) || ! fread (& right, 1, 1, f)) { Melder_error ("File too small (stereo 8-bit)."); goto end; }
					leftBuffer [i] = (left + right) * (1.0f / 256);
				}
			break;
		case Melder_LINEAR_8_UNSIGNED:
			if (numberOfChannels == 1)
				for (i = 1; i <= numberOfSamples; i ++)
					leftBuffer [i] = bingetu1 (f) * (1.0f / 128) - 1.0f;
			else if (rightBuffer)
				for (i = 1; i <= numberOfSamples; i ++) {
					leftBuffer [i] = bingetu1 (f) * (1.0f / 128) - 1.0f;
					rightBuffer [i] = bingetu1 (f) * (1.0f / 128) - 1.0f;
				}
			else
				for (i = 1; i <= numberOfSamples; i ++) {
					int left = bingetu1 (f), right = bingetu1 (f);
					leftBuffer [i] = (left + right) * (1.0f / 256) - 0.5f;
				}
			break;
		case Melder_LINEAR_16_BIG_ENDIAN:
			if (numberOfChannels == 1) {
				unsigned char *bytes = (unsigned char *) & leftBuffer [1] + numberOfSamples * 2;   /* in top half */
				fread (bytes, 1, numberOfSamples * 2, f);
				for (i = 1; i <= numberOfSamples; i ++) {
					unsigned char byte1 = * bytes ++, byte2 = * bytes ++;
					long value = (signed short) (((unsigned short) byte1 << 8) | (unsigned short) byte2);
					leftBuffer [i] = value * (1.0f / 32768);
				}
			} else if (rightBuffer) {
				unsigned char *bytes = (unsigned char *) & leftBuffer [1];
				fread (bytes, 1, numberOfSamples * 4, f);
				for (i = 1; i <= numberOfSamples; i ++) {
					unsigned char byte1 = * bytes ++, byte2 = * bytes ++, byte3 = * bytes ++, byte4 = * bytes ++;
					long left = (signed short) (((unsigned short) byte1 << 8) | (unsigned short) byte2);
					long right = (signed short) (((unsigned short) byte3 << 8) | (unsigned short) byte4);
					leftBuffer [i] = left * (1.0f / 32768);
					rightBuffer [i] = right * (1.0f / 32768);
				}
			} else {
				unsigned char *bytes = (unsigned char *) & leftBuffer [1];
				fread (bytes, 1, numberOfSamples * 4, f);
				for (i = 1; i <= numberOfSamples; i ++) {
					unsigned char byte1 = * bytes ++, byte2 = * bytes ++, byte3 = * bytes ++, byte4 = * bytes ++;
					long left = (signed short) (((unsigned short) byte1 << 8) | (unsigned short) byte2);
					long right = (signed short) (((unsigned short) byte3 << 8) | (unsigned short) byte4);
					leftBuffer [i] = (left + right) * (1.0f / 65536);
				}
			}
			break;
		case Melder_LINEAR_16_LITTLE_ENDIAN:
			if (numberOfChannels == 1) {
				unsigned char *bytes = (unsigned char *) & leftBuffer [1] + numberOfSamples * 2;   /* in top half */
				fread (bytes, 1, numberOfSamples * 2, f);
				for (i = 1; i <= numberOfSamples; i ++) {
					unsigned char byte1 = * bytes ++, byte2 = * bytes ++;
					long value = (signed short) (((unsigned short) byte2 << 8) | (unsigned short) byte1);
					leftBuffer [i] = value * (1.0f / 32768);
				}
			} else if (rightBuffer) {
				unsigned char *bytes = (unsigned char *) & leftBuffer [1];
				fread (bytes, 1, numberOfSamples * 4, f);
				for (i = 1; i <= numberOfSamples; i ++) {
					unsigned char byte1 = * bytes ++, byte2 = * bytes ++, byte3 = * bytes ++, byte4 = * bytes ++;
					long left = (signed short) (((unsigned short) byte2 << 8) | (unsigned short) byte1);
					long right = (signed short) (((unsigned short) byte4 << 8) | (unsigned short) byte3);
					leftBuffer [i] = left * (1.0f / 32768);
					rightBuffer [i] = right * (1.0f / 32768);
				}
			} else {
				unsigned char *bytes = (unsigned char *) & leftBuffer [1];
				fread (bytes, 1, numberOfSamples * 4, f);
				for (i = 1; i <= numberOfSamples; i ++) {
					unsigned char byte1 = * bytes ++, byte2 = * bytes ++, byte3 = * bytes ++, byte4 = * bytes ++;
					long left = (signed short) (((unsigned short) byte2 << 8) | (unsigned short) byte1);
					long right = (signed short) (((unsigned short) byte4 << 8) | (unsigned short) byte3);
					leftBuffer [i] = (left + right) * (1.0f / 65536);
				}
			}
			break;
		case Melder_LINEAR_24_BIG_ENDIAN:
			if (numberOfChannels == 1)
				for (i = 1; i <= numberOfSamples; i ++)
					leftBuffer [i] = bingeti3 (f) * (1.0f / 8388608);
			else if (rightBuffer)
				for (i = 1; i <= numberOfSamples; i ++) {
					leftBuffer [i] = bingeti3 (f) * (1.0f / 8388608);
					rightBuffer [i] = bingeti3 (f) * (1.0f / 8388608);
				}
			else
				for (i = 1; i <= numberOfSamples; i ++) {
					long left = bingeti3 (f), right = bingeti3 (f);
					leftBuffer [i] = (left + right) * (1.0f / 16777216);
				}
			break;
		case Melder_LINEAR_24_LITTLE_ENDIAN:
			if (numberOfChannels == 1)
				for (i = 1; i <= numberOfSamples; i ++)
					leftBuffer [i] = bingeti3LE (f) * (1.0f / 8388608);
			else if (rightBuffer)
				for (i = 1; i <= numberOfSamples; i ++) {
					leftBuffer [i] = bingeti3LE (f) * (1.0f / 8388608);
					rightBuffer [i] = bingeti3LE (f) * (1.0f / 8388608);
				}
			else
				for (i = 1; i <= numberOfSamples; i ++) {
					long left = bingeti3LE (f), right = bingeti3LE (f);
					leftBuffer [i] = (left + right) * (1.0f / 16777216);
				}
			break;
		case Melder_LINEAR_32_BIG_ENDIAN:
			if (numberOfChannels == 1)
				for (i = 1; i <= numberOfSamples; i ++)
					leftBuffer [i] = bingeti4 (f) * (1.0f / 32768 / 65536);
			else if (rightBuffer)
				for (i = 1; i <= numberOfSamples; i ++) {
					leftBuffer [i] = bingeti4 (f) * (1.0f / 32768 / 65536);
					rightBuffer [i] = bingeti4 (f) * (1.0f / 32768 / 65536);
				}
			else
				for (i = 1; i <= numberOfSamples; i ++) {
					long left = bingeti4 (f), right = bingeti4 (f);
					leftBuffer [i] = (left + right) * (1.0f / 65536 / 65536);
				}
			break;
		case Melder_LINEAR_32_LITTLE_ENDIAN:
			if (numberOfChannels == 1)
				for (i = 1; i <= numberOfSamples; i ++)
					leftBuffer [i] = bingeti4LE (f) * (1.0f / 32768 / 65536);
			else if (rightBuffer)
				for (i = 1; i <= numberOfSamples; i ++) {
					leftBuffer [i] = bingeti4LE (f) * (1.0f / 32768 / 65536);
					rightBuffer [i] = bingeti4LE (f) * (1.0f / 32768 / 65536);
				}
			else
				for (i = 1; i <= numberOfSamples; i ++) {
					long left = bingeti4LE (f), right = bingeti4LE (f);
					leftBuffer [i] = (left + right) * (1.0f / 65536 / 65536);
				}
			break;
		case Melder_IEEE_FLOAT_32_BIG_ENDIAN:
			if (numberOfChannels == 1)
				for (i = 1; i <= numberOfSamples; i ++)
					leftBuffer [i] = bingetr4 (f);
			else if (rightBuffer)
				for (i = 1; i <= numberOfSamples; i ++) {
					leftBuffer [i] = bingetr4 (f);
					rightBuffer [i] = bingetr4 (f);
				}
			else
				for (i = 1; i <= numberOfSamples; i ++) {
					double left = bingetr4 (f), right = bingetr4 (f);
					leftBuffer [i] = (left + right) * 0.5;
				}
			break;
		case Melder_IEEE_FLOAT_32_LITTLE_ENDIAN:
			if (numberOfChannels == 1)
				for (i = 1; i <= numberOfSamples; i ++)
					leftBuffer [i] = bingetr4LE (f);
			else if (rightBuffer)
				for (i = 1; i <= numberOfSamples; i ++) {
					leftBuffer [i] = bingetr4LE (f);
					rightBuffer [i] = bingetr4LE (f);
				}
			else
				for (i = 1; i <= numberOfSamples; i ++) {
					double left = bingetr4LE (f), right = bingetr4LE (f);
					leftBuffer [i] = (left + right) * 0.5;
				}
			break;
		case Melder_MULAW:
			if (numberOfChannels == 1)
				for (i = 1; i <= numberOfSamples; i ++)
					leftBuffer [i] = ulaw2linear [bingetu1 (f)] * (1.0f / 32768);
			else if (rightBuffer)
				for (i = 1; i <= numberOfSamples; i ++) {
					leftBuffer [i] = ulaw2linear [bingetu1 (f)] * (1.0f / 32768);
					rightBuffer [i] = ulaw2linear [bingetu1 (f)] * (1.0f / 32768);
				}
			else
				for (i = 1; i <= numberOfSamples; i ++) {
					long left = ulaw2linear [bingetu1 (f)], right = ulaw2linear [bingetu1 (f)];
					leftBuffer [i] = (left + right) * (1.0f / 65536);
				}
			break;
		case Melder_ALAW:
			if (numberOfChannels == 1)
				for (i = 1; i <= numberOfSamples; i ++)
					leftBuffer [i] = alaw2linear [bingetu1 (f)] * (1.0f / 32768);
			else if (rightBuffer)
				for (i = 1; i <= numberOfSamples; i ++) {
					leftBuffer [i] = alaw2linear [bingetu1 (f)] * (1.0f / 32768);
					rightBuffer [i] = alaw2linear [bingetu1 (f)] * (1.0f / 32768);
				}
			else
				for (i = 1; i <= numberOfSamples; i ++) {
					long left = alaw2linear [bingetu1 (f)], right = alaw2linear [bingetu1 (f)];
					leftBuffer [i] = (left + right) * (1.0f / 65536);
				}
			break;
		default:
			return Melder_error ("(Melder_readAudioToFloat:) Unknown encoding %d.", encoding);
	}
end:
	if (feof (f)) Melder_warning ("Audio file too short. Missing samples were set to zero.");
	if (ferror (f)) return Melder_error ("(Melder_readAudioToFloat:) Error reading audio samples from file.");
	return 1;
}

int Melder_readAudioToShort (FILE *f, int numberOfChannels, int encoding, short *buffer, long numberOfSamples) {
	long n = numberOfSamples * numberOfChannels, i;
	static const unsigned short byteSwapTest = 3 * 256 + 1;
	switch (encoding) {
		case Melder_LINEAR_8_SIGNED:
			for (i = 0; i < n; i ++) {
				signed char value;
				if (! fread (& value, 1, 1, f)) { Melder_error ("File too small (mono 8-bit)."); goto end; }
				buffer [i] = value * 256;
			}
			break;
		case Melder_LINEAR_8_UNSIGNED:
			for (i = 0; i < n; i ++)
				buffer [i] = bingetu1 (f) * 256L - 32768;
			break;
		case Melder_LINEAR_16_BIG_ENDIAN:
			fread (buffer, sizeof (short), n, f);
			if (* (unsigned char *) & byteSwapTest == 1) {
				for (i = 0; i < n; i ++) {
					unsigned short value = buffer [i];
					buffer [i] = (value >> 8) + (value << 8);
				}
			}
			break;
		case Melder_LINEAR_16_LITTLE_ENDIAN:
			fread (buffer, sizeof (short), n, f);
			if (* (unsigned char *) & byteSwapTest == 3) {
				for (i = 0; i < n; i ++) {
					unsigned short value = buffer [i];
					buffer [i] = (value >> 8) + (value << 8);
				}
			}
			break;
		case Melder_LINEAR_24_BIG_ENDIAN:
			for (i = 0; i < n; i ++)
				buffer [i] = bingeti3 (f) / 256;   /* BUG: truncation; not ideal. */
			break;
		case Melder_LINEAR_24_LITTLE_ENDIAN:
			for (i = 0; i < n; i ++)
				buffer [i] = bingeti3LE (f) / 256;   /* BUG: truncation; not ideal. */
			break;
		case Melder_LINEAR_32_BIG_ENDIAN:
			for (i = 0; i < n; i ++)
				buffer [i] = bingeti4 (f) / 65536;   /* BUG: truncation; not ideal. */
			break;
		case Melder_LINEAR_32_LITTLE_ENDIAN:
			for (i = 0; i < n; i ++)
				buffer [i] = bingeti4LE (f) / 65536;   /* BUG: truncation; not ideal. */
			break;
		case Melder_IEEE_FLOAT_32_BIG_ENDIAN:
			for (i = 0; i < n; i ++)
				buffer [i] = bingetr4 (f) * 32768;   /* BUG: truncation; not ideal. */
			break;
		case Melder_IEEE_FLOAT_32_LITTLE_ENDIAN:
			for (i = 0; i < n; i ++)
				buffer [i] = bingetr4LE (f) * 32768;   /* BUG: truncation; not ideal. */
			break;
		case Melder_MULAW:
			for (i = 0; i < n; i ++)
				buffer [i] = ulaw2linear [bingetu1 (f)];
			break;
		case Melder_ALAW:
			for (i = 0; i < n; i ++)
				buffer [i] = alaw2linear [bingetu1 (f)];
			break;
		default:
			return Melder_error ("(Melder_readAudioToShort:) Unknown encoding %d.", encoding);
	}
end:
	if (feof (f)) Melder_warning ("Audio file too short. Missing samples were set to zero.");
	if (ferror (f)) return Melder_error ("(Melder_readAudioToShort:) Error reading audio samples from file.");
	return 1;
}

int Melder_writeShortToAudio (FILE *f, int numberOfChannels, int encoding, const short *buffer, long numberOfSamples) {
	long n = numberOfSamples * numberOfChannels, start = 0, step = 1, i;
	if (numberOfChannels < 0) {
		n = numberOfSamples * 2;   /* stereo */
		step = 2;   /* Only one channel will be  written. */
		if (numberOfChannels == -2) {
			start = 1;   /* Right channel. */
		}
	}
	switch (encoding) {
		case Melder_LINEAR_8_SIGNED:
			for (i = start; i < n; i += step)
				binputi1 (buffer [i] >> 8, f);
		break; case Melder_LINEAR_8_UNSIGNED:
			for (i = start; i < n; i += step)
				binputu1 ((buffer [i] >> 8) + 128, f);
		break; case Melder_LINEAR_16_BIG_ENDIAN:
			for (i = start; i < n; i += step)
				binputi2 (buffer [i], f);
		break; case Melder_LINEAR_16_LITTLE_ENDIAN:
			for (i = start; i < n; i += step)
				binputi2LE (buffer [i], f);
		break; case Melder_LINEAR_24_BIG_ENDIAN:
			for (i = start; i < n; i += step)
				binputi3 (buffer [i] << 8, f);
		break; case Melder_LINEAR_24_LITTLE_ENDIAN:
			for (i = start; i < n; i += step)
				binputi3LE (buffer [i] << 8, f);
		break; case Melder_LINEAR_32_BIG_ENDIAN:
			for (i = start; i < n; i += step)
				binputi4 (buffer [i] << 16, f);
		break; case Melder_LINEAR_32_LITTLE_ENDIAN:
			for (i = start; i < n; i += step)
				binputi4LE (buffer [i] << 16, f);
		break; case Melder_IEEE_FLOAT_32_BIG_ENDIAN:
			for (i = start; i < n; i += step)
				binputr4 (buffer [i] / 32768.0, f);
		break; case Melder_IEEE_FLOAT_32_LITTLE_ENDIAN:
			for (i = start; i < n; i += step)
				binputr4LE (buffer [i] / 32768.0, f);
		break; case Melder_MULAW: case Melder_ALAW: default:
			return Melder_error ("(Melder_writeShortToAudio:) Unknown encoding %d.", encoding);
	}
	if (feof (f)) return Melder_error ("(Melder_writeShortToAudio:) Early end of audio file.");
	if (ferror (f)) return Melder_error ("(Melder_writeShortToAudio:) Error writing audio samples to file.");
	return 1;
}

int Melder_writeFloatToAudio (FILE *f, int encoding, const float *left, long nleft, const float *right, long nright, int warnIfClipped) {
	long n = nleft > nright ? nleft : nright, i, nclipped = 0;
	switch (encoding) {
		case Melder_LINEAR_8_SIGNED:
			for (i = 0; i < n; i ++) {
				if (i < nleft) {
					long value = floor (left [i] * 128 + 0.5);
					if (value < -128) { value = -128; nclipped ++; }
					if (value > 127) { value = 127; nclipped ++; }
					binputi1 (value, f);
				} else {
					binputi1 (0, f);
				}
				if (right) {
					if (i < nright) {
						long value = floor (right [i] * 128 + 0.5);
						if (value < -128) { value = -128; nclipped ++; }
						if (value > 127) { value = 127; nclipped ++; }
						binputi1 (value, f);
					} else {
						binputi1 (0, f);
					}
				}
			}
			break;
		case Melder_LINEAR_8_UNSIGNED:
			for (i = 0; i < n; i ++) {
				if (i < nleft) {
					long value = floor ((left [i] + 1.0) * 128);
					if (value < 0) { value = 0; nclipped ++; }
					if (value > 255) { value = 255; nclipped ++; }
					binputu1 (value, f);
				} else {
					binputu1 (128, f);
				}
				if (right) {
					if (i < nright) {
						long value = floor ((right [i] + 1.0) * 128);
						if (value < 0) { value = 0; nclipped ++; }
						if (value > 255) { value = 255; nclipped ++; }
						binputu1 (value, f);
					} else {
						binputu1 (128, f);
					}
				}
			}
			break;
		case Melder_LINEAR_16_BIG_ENDIAN:
			for (i = 0; i < n; i ++) {
				if (i < nleft) {
					long value = floor (left [i] * 32768 + 0.5);
					if (value < -32768) { value = -32768; nclipped ++; }
					if (value > 32767) { value = 32767; nclipped ++; }
					binputi2 (value, f);
				} else {
					binputi2 (0, f);
				}
				if (right) {
					if (i < nright) {
						long value = floor (right [i] * 32768 + 0.5);
						if (value < -32768) { value = -32768; nclipped ++; }
						if (value > 32767) { value = 32767; nclipped ++; }
						binputi2 (value, f);
					} else {
						binputi2 (0, f);
					}
				}
			}
			break;
		case Melder_LINEAR_16_LITTLE_ENDIAN:
			for (i = 0; i < n; i ++) {
				if (i < nleft) {
					long value = floor (left [i] * 32768 + 0.5);
					if (value < -32768) { value = -32768; nclipped ++; }
					if (value > 32767) { value = 32767; nclipped ++; }
					binputi2LE (value, f);
				} else {
					binputi2LE (0, f);
				}
				if (right) {
					if (i < nright) {
						long value = floor (right [i] * 32768 + 0.5);
						if (value < -32768) { value = -32768; nclipped ++; }
						if (value > 32767) { value = 32767; nclipped ++; }
						binputi2LE (value, f);
					} else {
						binputi2LE (0, f);
					}
				}
			}
			break;
		case Melder_LINEAR_24_BIG_ENDIAN:
			for (i = 0; i < n; i ++) {
				if (i < nleft) {
					long value = floor (left [i] * 8388608 + 0.5);
					if (value < -8388608) { value = -8388608; nclipped ++; }
					if (value > 8388607) { value = 8388607; nclipped ++; }
					binputi3 (value, f);
				} else {
					binputi3 (0, f);
				}
				if (right) {
					if (i < nright) {
						long value = floor (right [i] * 8388608 + 0.5);
						if (value < -8388608) { value = -8388608; nclipped ++; }
						if (value > 8388607) { value = 8388607; nclipped ++; }
						binputi3 (value, f);
					} else {
						binputi3 (0, f);
					}
				}
			}
			break;
		case Melder_LINEAR_24_LITTLE_ENDIAN:
			for (i = 0; i < n; i ++) {
				if (i < nleft) {
					long value = floor (left [i] * 8388608 + 0.5);
					if (value < -8388608) { value = -8388608; nclipped ++; }
					if (value > 8388607) { value = 8388607; nclipped ++; }
					binputi3LE (value, f);
				} else {
					binputi3LE (0, f);
				}
				if (right) {
					if (i < nright) {
						long value = floor (right [i] * 8388608 + 0.5);
						if (value < -8388608) { value = -8388608; nclipped ++; }
						if (value > 8388607) { value = 8388607; nclipped ++; }
						binputi3LE (value, f);
					} else {
						binputi3LE (0, f);
					}
				}
			}
			break;
		case Melder_LINEAR_32_BIG_ENDIAN:
			for (i = 0; i < n; i ++) {
				if (i < nleft) {
					double value = floor (left [i] * 2147483648.0 + 0.5);
					if (value < -2147483648.0) { value = -2147483648.0; nclipped ++; }
					if (value > 2147483647.0) { value = 2147483647.0; nclipped ++; }
					binputi4 (value, f);
				} else {
					binputi4 (0, f);
				}
				if (right) {
					if (i < nright) {
						double value = floor (right [i] * 32768 + 0.5);
						if (value < -2147483648.0) { value = -2147483648.0; nclipped ++; }
						if (value > 2147483647.0) { value = 2147483647.0; nclipped ++; }
						binputi4 (value, f);
					} else {
						binputi4 (0, f);
					}
				}
			}
			break;
		case Melder_LINEAR_32_LITTLE_ENDIAN:
			for (i = 0; i < n; i ++) {
				if (i < nleft) {
					double value = floor (left [i] * 2147483648.0 + 0.5);
					if (value < -2147483648.0) { value = -2147483648.0; nclipped ++; }
					if (value > 2147483647.0) { value = 2147483647.0; nclipped ++; }
					binputi4LE (value, f);
				} else {
					binputi4LE (0, f);
				}
				if (right) {
					if (i < nright) {
						double value = floor (right [i] * 2147483648.0 + 0.5);
						if (value < -2147483648.0) { value = -2147483648.0; nclipped ++; }
						if (value > 2147483647.0) { value = 2147483647.0; nclipped ++; }
						binputi4LE (value, f);
					} else {
						binputi4LE (0, f);
					}
				}
			}
			break;
		case Melder_IEEE_FLOAT_32_BIG_ENDIAN:
			for (i = 0; i < n; i ++) {
				if (i < nleft) {
					double value = left [i];
					binputr4 (value, f);
				} else {
					binputr4 (0, f);
				}
				if (right) {
					if (i < nright) {
						double value = right [i];
						binputr4 (value, f);
					} else {
						binputr4 (0, f);
					}
				}
			}
			break;
		case Melder_IEEE_FLOAT_32_LITTLE_ENDIAN:
			for (i = 0; i < n; i ++) {
				if (i < nleft) {
					double value = left [i];
					binputr4LE (value, f);
				} else {
					binputr4LE (0, f);
				}
				if (right) {
					if (i < nright) {
						double value = right [i];
						binputr4LE (value, f);
					} else {
						binputr4LE (0, f);
					}
				}
			}
			break;
		case Melder_MULAW:
		case Melder_ALAW:
		default:
			return Melder_error ("(Melder_writeFloatToAudio:) Unknown format.");
	}
	if (nclipped > 0 && warnIfClipped)
		Melder_warning ("(Melder_writeFloatToAudio:) %ld out of %ld samples have been clipped.\n"
			"Advice: you could scale the amplitudes or write to a binary file.", nclipped, n);
	return 1;
}

/* End of file melder_audiofiles.c */
