/*
	Audio File Library
	Copyright (C) 1998-2000, Michael Pruett <michael@68k.org>
	Copyright (C) 2000-2001, Silicon Graphics, Inc.

	This library is free software; you can redistribute it and/or
	modify it under the terms of the GNU Library General Public
	License as published by the Free Software Foundation; either
	version 2 of the License, or (at your option) any later version.

	This library 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
	Library General Public License for more details.

	You should have received a copy of the GNU Library General Public
	License along with this library; if not, write to the
	Free Software Foundation, Inc., 59 Temple Place - Suite 330,
	Boston, MA  02111-1307  USA.
*/

/*
	wave.c

	This file contains code for parsing RIFF WAVE format sound files.
*/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <assert.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif

#include "audiofile.h"
#include "util.h"
#include "afinternal.h"
#include "byteorder.h"
#include "wave.h"
#include "track.h"
#include "setup.h"
#include "marker.h"

int _af_wave_compression_types[_AF_WAVE_NUM_COMPTYPES] =
{
	AF_COMPRESSION_G711_ULAW,
	AF_COMPRESSION_G711_ALAW
};

_InstParamInfo _af_wave_inst_params[_AF_WAVE_NUM_INSTPARAMS] =
{
	{ AF_INST_MIDI_BASENOTE, AU_PVTYPE_LONG, "MIDI base note", {60} },
	{ AF_INST_NUMCENTS_DETUNE, AU_PVTYPE_LONG, "Detune in cents", {0} },
	{ AF_INST_MIDI_LOVELOCITY, AU_PVTYPE_LONG, "Low velocity", {1} },
	{ AF_INST_MIDI_HIVELOCITY, AU_PVTYPE_LONG, "High velocity", {127} },
	{ AF_INST_MIDI_LONOTE, AU_PVTYPE_LONG, "Low note", {0} },
	{ AF_INST_MIDI_HINOTE, AU_PVTYPE_LONG, "High note", {127} },
	{ AF_INST_NUMDBS_GAIN, AU_PVTYPE_LONG, "Gain in dB", {0} }
};

_AFfilesetup _af_wave_default_filesetup =
{
	_AF_VALID_FILESETUP,	/* valid */
	AF_FILE_WAVE,		/* fileFormat */
	AF_TRUE,		/* trackSet */
	AF_TRUE,		/* instrumentSet */
	AF_TRUE,		/* miscellaneousSet  */
	1,			/* trackCount */
	NULL,			/* tracks */
	0,			/* instrumentCount */
	NULL,			/* instruments */
	0,			/* miscellaneousCount */
	NULL			/* miscellaneous */
};

static status ParseFrameCount (AFfilehandle filehandle, AFvirtualfile *fp,
	u_int32_t id, size_t size)
{
	u_int32_t	totalFrames;
	_Track		*track;

	track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK);

	af_fread(&totalFrames, 1, 4, fp);

	track->totalfframes = LENDIAN_TO_HOST_INT32(totalFrames);

	return AF_SUCCEED;
}

static status ParseFormat (AFfilehandle filehandle, AFvirtualfile *fp,
	u_int32_t id, size_t size)
{
	_Track		*track;
	u_int16_t	formatTag, channelCount;
	u_int32_t	sampleRate, averageBytesPerSecond;
	u_int16_t	blockAlign;
	_WAVEInfo	*wave;

	assert(filehandle != NULL);
	assert(fp != NULL);
	assert(!memcmp(&id, "fmt ", 4));

	track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK);

	assert(filehandle->formatSpecific != NULL);
	wave = (_WAVEInfo *) filehandle->formatSpecific;

	af_fread(&formatTag, 1, 2, fp);
	formatTag = LENDIAN_TO_HOST_INT16(formatTag);

	af_fread(&channelCount, 1, 2, fp);
	channelCount = LENDIAN_TO_HOST_INT16(channelCount);
	track->f.channelCount = channelCount;

	af_fread(&sampleRate, 1, 4, fp);
	sampleRate = LENDIAN_TO_HOST_INT32(sampleRate);
	track->f.sampleRate = sampleRate;

	af_fread(&averageBytesPerSecond, 1, 4, fp);
	averageBytesPerSecond = LENDIAN_TO_HOST_INT32(averageBytesPerSecond);

	af_fread(&blockAlign, 1, 2, fp);
	blockAlign = LENDIAN_TO_HOST_INT16(blockAlign);

	track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN;

	/* Default to uncompressed audio data. */
	track->f.compressionType = AF_COMPRESSION_NONE;

	switch (formatTag)
	{
		case WAVE_FORMAT_PCM:
		{
			u_int16_t	bitsPerSample;

			af_fread(&bitsPerSample, 1, 2, fp);
			bitsPerSample = LENDIAN_TO_HOST_INT16(bitsPerSample);

			track->f.sampleWidth = bitsPerSample;

			if (bitsPerSample == 0 || bitsPerSample > 32)
			{
				_af_error(AF_BAD_WIDTH,
					"bad sample width of %d bits",
					bitsPerSample);
				return AF_FAIL;
			}

			if (bitsPerSample <= 8)
				track->f.sampleFormat = AF_SAMPFMT_UNSIGNED;
			else
				track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
		}
		break;

		case WAVE_FORMAT_MULAW:
		case IBM_FORMAT_MULAW:
			track->f.sampleWidth = 16;
			track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
			track->f.compressionType = AF_COMPRESSION_G711_ULAW;
			break;

		case WAVE_FORMAT_ALAW:
		case IBM_FORMAT_ALAW:
			track->f.sampleWidth = 16;
			track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
			track->f.compressionType = AF_COMPRESSION_G711_ALAW;
			break;

		case WAVE_FORMAT_IEEE_FLOAT:
		{
			u_int16_t	bitsPerSample;

			af_fread(&bitsPerSample, 1, 2, fp);
			bitsPerSample = LENDIAN_TO_HOST_INT16(bitsPerSample);

			if (bitsPerSample == 64)
			{
				track->f.sampleWidth = 64;
				track->f.sampleFormat = AF_SAMPFMT_DOUBLE;
			}
			else
			{
				track->f.sampleWidth = 32;
				track->f.sampleFormat = AF_SAMPFMT_FLOAT;
			}
		}
		break;

		case WAVE_FORMAT_ADPCM:
		{
			u_int16_t	bitsPerSample, extraByteCount,
					samplesPerBlock, numCoefficients;
			int		i;
			AUpvlist	pv;
			long		l;
			void		*v;

			if (track->f.channelCount != 1 &&
				track->f.channelCount != 2)
			{
				_af_error(AF_BAD_CHANNELS,
					"WAVE file with MS ADPCM compression "
					"must have 1 or 2 channels");
			}

			af_fread(&bitsPerSample, 1, 2, fp);
			bitsPerSample = LENDIAN_TO_HOST_INT16(bitsPerSample);

			af_fread(&extraByteCount, 1, 2, fp);
			extraByteCount = LENDIAN_TO_HOST_INT16(extraByteCount);

			af_fread(&samplesPerBlock, 1, 2, fp);
			samplesPerBlock = LENDIAN_TO_HOST_INT16(samplesPerBlock);

			af_fread(&numCoefficients, 1, 2, fp);
			numCoefficients = LENDIAN_TO_HOST_INT16(numCoefficients);

			/* numCoefficients should be at least 7. */
			assert(numCoefficients >= 7 && numCoefficients <= 255);

			for (i=0; i<numCoefficients; i++)
			{
				int16_t	a0, a1;

				af_fread(&a0, 1, 2, fp);
				af_fread(&a1, 1, 2, fp);

				a0 = LENDIAN_TO_HOST_INT16(a0);
				a1 = LENDIAN_TO_HOST_INT16(a1);

				wave->msadpcmCoefficients[i][0] = a0;
				wave->msadpcmCoefficients[i][1] = a1;
			}

			track->f.sampleWidth = 16;
			track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
			track->f.compressionType = AF_COMPRESSION_MS_ADPCM;
			track->f.byteOrder = _AF_BYTEORDER_NATIVE;

			/* Create the parameter list. */
			pv = AUpvnew(4);
			AUpvsetparam(pv, 0, _AF_MS_ADPCM_NUM_COEFFICIENTS);
			AUpvsetvaltype(pv, 0, AU_PVTYPE_LONG);
			l = numCoefficients;
			AUpvsetval(pv, 0, &l);

			AUpvsetparam(pv, 1, _AF_MS_ADPCM_COEFFICIENTS);
			AUpvsetvaltype(pv, 1, AU_PVTYPE_PTR);
			v = wave->msadpcmCoefficients;
			AUpvsetval(pv, 1, &v);

			AUpvsetparam(pv, 2, _AF_SAMPLES_PER_BLOCK);
			AUpvsetvaltype(pv, 2, AU_PVTYPE_LONG);
			l = samplesPerBlock;
			AUpvsetval(pv, 2, &l);

			AUpvsetparam(pv, 3, _AF_BLOCK_SIZE);
			AUpvsetvaltype(pv, 3, AU_PVTYPE_LONG);
			l = blockAlign;
			AUpvsetval(pv, 3, &l);

			track->f.compressionParams = pv;
		}
		break;

		case WAVE_FORMAT_DVI_ADPCM:
		{
			AUpvlist	pv;
			long		l;

			u_int16_t	bitsPerSample, extraByteCount,
					samplesPerBlock;

			af_fread(&bitsPerSample, 1, 2, fp);
			bitsPerSample = LENDIAN_TO_HOST_INT16(bitsPerSample);

			af_fread(&extraByteCount, 1, 2, fp);
			extraByteCount = LENDIAN_TO_HOST_INT16(extraByteCount);

			af_fread(&samplesPerBlock, 1, 2, fp);
			samplesPerBlock = LENDIAN_TO_HOST_INT16(samplesPerBlock);

			track->f.sampleWidth = 16;
			track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
			track->f.compressionType = AF_COMPRESSION_IMA;
			track->f.byteOrder = _AF_BYTEORDER_NATIVE;

			/* Create the parameter list. */
			pv = AUpvnew(2);
			AUpvsetparam(pv, 0, _AF_SAMPLES_PER_BLOCK);
			AUpvsetvaltype(pv, 0, AU_PVTYPE_LONG);
			l = samplesPerBlock;
			AUpvsetval(pv, 0, &l);

			AUpvsetparam(pv, 1, _AF_BLOCK_SIZE);
			AUpvsetvaltype(pv, 1, AU_PVTYPE_LONG);
			l = blockAlign;
			AUpvsetval(pv, 1, &l);

			track->f.compressionParams = pv;
		}
		break;

		case WAVE_FORMAT_YAMAHA_ADPCM:
		case WAVE_FORMAT_OKI_ADPCM:
		case WAVE_FORMAT_CREATIVE_ADPCM:
		case IBM_FORMAT_ADPCM:
			_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE ADPCM data format 0x%x is not currently supported", formatTag);
			return AF_FAIL;
			break;

		case WAVE_FORMAT_MPEG:
			_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE MPEG data format is not supported");
			return AF_FAIL;
			break;

		case WAVE_FORMAT_MPEGLAYER3:
			_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE MPEG layer 3 data format is not supported");
			return AF_FAIL;
			break;

		default:
			_af_error(AF_BAD_NOT_IMPLEMENTED, "WAVE file data format 0x%x not currently supported", formatTag);
			return AF_FAIL;
			break;
	}

	_af_set_sample_format(&track->f, track->f.sampleFormat, track->f.sampleWidth);

	return AF_SUCCEED;
}

static status ParseData (AFfilehandle filehandle, AFvirtualfile *fp,
	u_int32_t id, size_t size)
{
	_Track	*track;

	assert(filehandle != NULL);
	assert(fp != NULL);
	assert(!memcmp(&id, "data", 4));

	track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK);

	track->fpos_first_frame = af_ftell(fp);
	track->data_size = size;

	return AF_SUCCEED;
}

static status ParsePlayList (AFfilehandle filehandle, AFvirtualfile *fp,
	u_int32_t id, size_t size)
{
	_Instrument	*instrument;
	u_int32_t	segmentCount;
	int		segment;

	af_fread(&segmentCount, 4, 1, fp);
	segmentCount = LENDIAN_TO_HOST_INT32(segmentCount);

	if (segmentCount == 0)
	{
		filehandle->instrumentCount = 0;
		filehandle->instruments = NULL;
		return AF_SUCCEED;
	}

	for (segment=0; segment<segmentCount; segment++)
	{
		u_int32_t	startMarkID, loopLength, loopCount;

		af_fread(&startMarkID, 4, 1, fp);
		startMarkID = LENDIAN_TO_HOST_INT32(startMarkID);
		af_fread(&loopLength, 4, 1, fp);
		loopLength = LENDIAN_TO_HOST_INT32(loopLength);
		af_fread(&loopCount, 4, 1, fp);
		loopCount = LENDIAN_TO_HOST_INT32(loopCount);
	}

	return AF_SUCCEED;
}

static status ParseCues (AFfilehandle filehandle, AFvirtualfile *fp,
	u_int32_t id, size_t size)
{
	_Track		*track;
	u_int32_t	markerCount;
	int		i;

	track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK);

	af_fread(&markerCount, 4, 1, fp);
	markerCount = LENDIAN_TO_HOST_INT32(markerCount);
	track->markerCount = markerCount;

	if (markerCount == 0)
	{
		track->markers = NULL;
		return AF_SUCCEED;
	}

	if ((track->markers = _af_marker_new(markerCount)) == NULL)
		return AF_FAIL;

	for (i=0; i<markerCount; i++)
	{
		u_int32_t	id, position, chunkid;
		u_int32_t	chunkByteOffset, blockByteOffset;
		u_int32_t	sampleFrameOffset;
		_Marker		*marker = &track->markers[i];

		af_fread(&id, 4, 1, fp);
		id = LENDIAN_TO_HOST_INT32(id);

		af_fread(&position, 4, 1, fp);
		position = LENDIAN_TO_HOST_INT32(position);

		af_fread(&chunkid, 4, 1, fp);
		chunkid = LENDIAN_TO_HOST_INT32(chunkid);

		af_fread(&chunkByteOffset, 4, 1, fp);
		chunkByteOffset = LENDIAN_TO_HOST_INT32(chunkByteOffset);

		af_fread(&blockByteOffset, 4, 1, fp);
		blockByteOffset = LENDIAN_TO_HOST_INT32(blockByteOffset);

		/*
			sampleFrameOffset represents the position of
			the mark in units of frames.
		*/
		af_fread(&sampleFrameOffset, 4, 1, fp);
		sampleFrameOffset = LENDIAN_TO_HOST_INT32(sampleFrameOffset);

		marker->id = id;
		marker->position = sampleFrameOffset;
		marker->name = _af_strdup("");
		marker->comment = _af_strdup("");
	}

	return AF_SUCCEED;
}

/* Parse an adtl sub-chunk within a LIST chunk. */
static status ParseADTLSubChunk (AFfilehandle filehandle, AFvirtualfile *fp,
	u_int32_t id, size_t size)
{
	_Track		*track;
	AFfileoffset	endPos=af_ftell(fp)+size;

	track = _af_filehandle_get_track(filehandle, AF_DEFAULT_TRACK);

	while (af_ftell(fp) < endPos)
	{
		char		chunkID[4];
		u_int32_t	chunkSize;

		af_fread(chunkID, 4, 1, fp);
		af_fread(&chunkSize, 4, 1, fp);
		chunkSize = LENDIAN_TO_HOST_INT32(chunkSize);

		if (memcmp(chunkID, "labl", 4)==0 || memcmp(chunkID, "note", 4)==0)
		{
			_Marker *marker=NULL;
			u_int32_t id;
			long length=chunkSize-4;
			char *p=_af_malloc(length);

			af_fread(&id, 4, 1, fp);
			af_fread(p, length, 1, fp);

			id = LENDIAN_TO_HOST_INT32(id);

			marker = _af_marker_find_by_id(track, id);

			if (marker != NULL)
			{
				if (memcmp(chunkID, "labl", 4)==0)
				{
					free(marker->name);
					marker->name = p;
				}
				else if (memcmp(chunkID, "note", 4)==0)
				{
					free(marker->comment);
					marker->comment = p;
				}
				else
					free(p);
			}
			else
				free(p);

			/*
				If chunkSize is odd, skip an extra byte
				at the end of the chunk.
			*/
			if ((chunkSize % 2) != 0)
				af_fseek(fp, 1, SEEK_CUR);
		}
		else
		{
			/* If chunkSize is odd, skip an extra byte. */
			af_fseek(fp, chunkSize + (chunkSize % 2), SEEK_CUR);
		}
	}
	return AF_SUCCEED;
}

/* Parse an INFO sub-chunk within a LIST chunk. */
static status ParseINFOSubChunk (AFfilehandle filehandle, AFvirtualfile *fp,
	u_int32_t id, size_t size)
{
	AFfileoffset	endPos=af_ftell(fp)+size;

	while (af_ftell(fp) < endPos)
	{
		int		misctype = AF_MISC_UNRECOGNIZED;
		u_int32_t	miscid, miscsize;

		af_fread(&miscid, 4, 1, fp);
		af_fread(&miscsize, 4, 1, fp);
		miscsize = LENDIAN_TO_HOST_INT32(miscsize);

		if (memcmp(&miscid, "IART", 4) == 0)
			misctype = AF_MISC_AUTH;
		else if (memcmp(&miscid, "INAM", 4) == 0)
			misctype = AF_MISC_NAME;
		else if (memcmp(&miscid, "ICOP", 4) == 0)
			misctype = AF_MISC_COPY;
		else if (memcmp(&miscid, "ICMT", 4) == 0)
			misctype = AF_MISC_ICMT;
		else if (memcmp(&miscid, "ICRD", 4) == 0)
			misctype = AF_MISC_ICRD;
		else if (memcmp(&miscid, "ISFT", 4) == 0)
			misctype = AF_MISC_ISFT;

		if (misctype != AF_MISC_UNRECOGNIZED)
		{
			char	*string = _af_malloc(miscsize);

			af_fread(string, miscsize, 1, fp);

			filehandle->miscellaneousCount++;
			filehandle->miscellaneous = _af_realloc(filehandle->miscellaneous, sizeof (_Miscellaneous) * filehandle->miscellaneousCount);

			filehandle->miscellaneous[filehandle->miscellaneousCount-1].id = filehandle->miscellaneousCount;
			filehandle->miscellaneous[filehandle->miscellaneousCount-1].type = misctype;
			filehandle->miscellaneous[filehandle->miscellaneousCount-1].size = miscsize;
			filehandle->miscellaneous[filehandle->miscellaneousCount-1].position = 0;
			filehandle->miscellaneous[filehandle->miscellaneousCount-1].buffer = string;
		}
		else
		{
			af_fseek(fp, miscsize, SEEK_CUR);
		}

		/* Make the current position an even number of bytes.  */
		if (miscsize % 2 != 0)
			af_fseek(fp, 1, SEEK_CUR);
	}
	return AF_SUCCEED;
}

static status ParseList (AFfilehandle filehandle, AFvirtualfile *fp,
	u_int32_t id, size_t size)
{
	u_int32_t	typeID;

	af_fread(&typeID, 4, 1, fp);
	size-=4;

	if (memcmp(&typeID, "adtl", 4) == 0)
	{
		/* Handle adtl sub-chunks. */
		return ParseADTLSubChunk(filehandle, fp, typeID, size);
	}
	else if (memcmp(&typeID, "INFO", 4) == 0)
	{
		/* Handle INFO sub-chunks. */
		return ParseINFOSubChunk(filehandle, fp, typeID, size);
	}
	else
	{
		/* Skip unhandled sub-chunks. */
		af_fseek(fp, size, SEEK_CUR);
		return AF_SUCCEED;
	}
	return AF_SUCCEED;
}

static status ParseInstrument (AFfilehandle filehandle, AFvirtualfile *fp,
	u_int32_t id, size_t size)
{
	u_int8_t	baseNote;
	int8_t		detune, gain;
	u_int8_t	lowNote, highNote, lowVelocity, highVelocity;
	u_int8_t	padByte;

	af_fread(&baseNote, 1, 1, fp);
	af_fread(&detune, 1, 1, fp);
	af_fread(&gain, 1, 1, fp);
	af_fread(&lowNote, 1, 1, fp);
	af_fread(&highNote, 1, 1, fp);
	af_fread(&lowVelocity, 1, 1, fp);
	af_fread(&highVelocity, 1, 1, fp);
	af_fread(&padByte, 1, 1, fp);

	return AF_SUCCEED;
}

bool _af_wave_recognize (AFvirtualfile *fh)
{
	u_int8_t	buffer[8];

	af_fseek(fh, 0, SEEK_SET);

	if (af_fread(buffer, 1, 8, fh) != 8 || memcmp(buffer, "RIFF", 4) != 0)
		return AF_FALSE;
	if (af_fread(buffer, 1, 4, fh) != 4 || memcmp(buffer, "WAVE", 4) != 0)
		return AF_FALSE;

	return AF_TRUE;
}

status _af_wave_read_init (AFfilesetup setup, AFfilehandle filehandle)
{
	_Track		*track;
	u_int32_t	type, size, formtype;
	u_int32_t	index = 0;
	bool		hasFormat, hasData, hasCue, hasList, hasPlayList, hasFrameCount,
			hasINST, hasINFO;
	_WAVEInfo	*wave = _af_malloc(sizeof (_WAVEInfo));

	assert(filehandle != NULL);
	assert(filehandle->fh != NULL);

	hasFormat = AF_FALSE;
	hasData = AF_FALSE;
	hasCue = AF_FALSE;
	hasList = AF_FALSE;
	hasPlayList = AF_FALSE;
	hasFrameCount = AF_FALSE;
	hasINST = AF_FALSE;
	hasINFO = AF_FALSE;

	filehandle->formatSpecific = wave;
	filehandle->instruments = NULL;
	filehandle->instrumentCount = 0;
	filehandle->miscellaneous = NULL;
	filehandle->miscellaneousCount = 0;

	track = _af_track_new();
	filehandle->tracks = track;
	filehandle->trackCount = 1;

	af_fseek(filehandle->fh, 0, SEEK_SET);

	af_fread(&type, 4, 1, filehandle->fh);
	af_fread(&size, 4, 1, filehandle->fh);
	size = LENDIAN_TO_HOST_INT32(size);
	af_fread(&formtype, 4, 1, filehandle->fh);

	assert(!memcmp(&type, "RIFF", 4));
	assert(!memcmp(&formtype, "WAVE", 4));

#ifdef DEBUG
	printf("size: %d\n", size);
#endif

	/* Include the offset of the form type. */
	index += 4;

	while (index < size)
	{
		u_int32_t	chunkid = 0, chunksize = 0;
		status		result;

#ifdef DEBUG
		printf("index: %d\n", index);
#endif
		af_fread(&chunkid, 4, 1, filehandle->fh);

		af_fread(&chunksize, 4, 1, filehandle->fh);
		chunksize = LENDIAN_TO_HOST_INT32(chunksize);

#ifdef DEBUG
		_af_printid(BENDIAN_TO_HOST_INT32(chunkid));
		printf(" size: %d\n", chunksize);
#endif

		if (memcmp(&chunkid, "fmt ", 4) == 0)
		{
			result = ParseFormat(filehandle, filehandle->fh, chunkid, chunksize);
			if (result == AF_FAIL)
				return AF_FAIL;

			hasFormat = AF_TRUE;
		}
		else if (memcmp(&chunkid, "data", 4) == 0)
		{
			/* The format chunk must precede the data chunk. */
			if (!hasFormat)
			{
				_af_error(AF_BAD_HEADER, "missing format chunk in WAVE file");
				return AF_FAIL;
			}

			result = ParseData(filehandle, filehandle->fh, chunkid, chunksize);
			if (result == AF_FAIL)
				return AF_FAIL;

			hasData = AF_TRUE;
		}
		else if (memcmp(&chunkid, "inst", 4) == 0)
		{
			result = ParseInstrument(filehandle, filehandle->fh, chunkid, chunksize);
			if (result == AF_FAIL)
				return AF_FAIL;
		}
		else if (memcmp(&chunkid, "fact", 4) == 0)
		{
			hasFrameCount = AF_TRUE;
			result = ParseFrameCount(filehandle, filehandle->fh, chunkid, chunksize);
			if (result == AF_FAIL)
				return AF_FAIL;
		}
		else if (memcmp(&chunkid, "cue ", 4) == 0)
		{
			hasCue = AF_TRUE;
			result = ParseCues(filehandle, filehandle->fh, chunkid, chunksize);
			if (result == AF_FAIL)
				return AF_FAIL;
		}
		else if (memcmp(&chunkid, "LIST", 4) == 0 || memcmp(&chunkid, "list", 4) == 0)
		{
			hasList = AF_TRUE;
			result = ParseList(filehandle, filehandle->fh, chunkid, chunksize);
			if (result == AF_FAIL)
				return AF_FAIL;
		}
		else if (memcmp(&chunkid, "INST", 4) == 0)
		{
			hasINST = AF_TRUE;
			result = ParseInstrument(filehandle, filehandle->fh, chunkid, chunksize);
			if (result == AF_FAIL)
				return AF_FAIL;
		}
		else if (memcmp(&chunkid, "plst", 4) == 0)
		{
			hasPlayList = AF_TRUE;
			result = ParsePlayList(filehandle, filehandle->fh, chunkid, chunksize);
			if (result == AF_FAIL)
				return AF_FAIL;
		}

		index += chunksize + 8;

		/* All chunks must be aligned on an even number of bytes */
		if ((index % 2) != 0)
			index++;

		af_fseek(filehandle->fh, index + 8, SEEK_SET);
	}

	/* The format chunk and the data chunk are required. */
	if (!hasFormat || !hasData)
	{
		return AF_FAIL;
	}

	/*
		At this point we know that the file has a format chunk
		and a data chunk, so we can assume that track->f and
		track->data_size have been initialized.
	*/
	if (hasFrameCount == AF_FALSE)
	{
		/*
			Perform arithmetic in double-precision so as
			to preserve accuracy.
		*/
		track->totalfframes = ceil((double) track->data_size /
			_af_format_frame_size(&track->f, AF_FALSE));
	}

	if (track->f.compressionType != AF_COMPRESSION_NONE &&
		(track->f.compressionType == AF_COMPRESSION_G711_ULAW ||
		track->f.compressionType == AF_COMPRESSION_G711_ALAW))
	{
		track->totalfframes = track->data_size / track->f.channelCount;
	}

	/*
		A return value of AF_SUCCEED indicates successful parsing.
	*/
	return AF_SUCCEED;
}

AFfilesetup _af_wave_complete_setup (AFfilesetup setup)
{
	AFfilesetup	newsetup;
	_TrackSetup	*track;

	if (setup->trackSet && setup->trackCount != 1)
	{
		_af_error(AF_BAD_NUMTRACKS, "WAVE file must have 1 track");
		return AF_NULL_FILESETUP;
	}

	track = _af_filesetup_get_tracksetup(setup, AF_DEFAULT_TRACK);

	if (track->sampleFormatSet)
	{
		switch (track->f.sampleFormat)
		{
			case AF_SAMPFMT_FLOAT:
				if (track->sampleWidthSet &&
					track->f.sampleWidth != 32)
				{
					_af_error(AF_BAD_WIDTH,
						"Warning: invalid sample width for floating-point WAVE file: %d (must be 32 bits)\n",
						track->f.sampleWidth);
					_af_set_sample_format(&track->f, AF_SAMPFMT_FLOAT, 32);
				}
				break;

			case AF_SAMPFMT_DOUBLE:
				_af_error(AF_BAD_SAMPFMT, "WAVE format does not support double-precision floating-point data");
				return AF_NULL_FILESETUP;
				break;

			case AF_SAMPFMT_UNSIGNED:
				if (track->sampleWidthSet)
				{
					if (track->f.sampleWidth < 1 || track->f.sampleWidth > 32)
					{
						_af_error(AF_BAD_WIDTH, "invalid sample width for WAVE file: %d (must be 1-32 bits)\n", track->f.sampleWidth);
						return AF_NULL_FILESETUP;
					}
					if (track->f.sampleWidth > 8)
					{
						_af_error(AF_BAD_SAMPFMT, "WAVE integer data of more than 8 bits must be two's complement signed");
						_af_set_sample_format(&track->f, AF_SAMPFMT_TWOSCOMP, track->f.sampleWidth);
					}
				}
				else
				/*
					If the sample width is not set but the user requests
					unsigned data, set the width to 8 bits.
				*/
					_af_set_sample_format(&track->f, track->f.sampleFormat, 8);
				break;

			case AF_SAMPFMT_TWOSCOMP:
				if (track->sampleWidthSet)
				{
					if (track->f.sampleWidth < 1 || track->f.sampleWidth > 32)
					{
						_af_error(AF_BAD_WIDTH, "invalid sample width %d for WAVE file (must be 1-32)", track->f.sampleWidth);
						return AF_NULL_FILESETUP;
					}
					else if (track->f.sampleWidth <= 8)
					{
						_af_error(AF_BAD_SAMPFMT, "Warning: WAVE format integer data of 1-8 bits must be unsigned; setting sample format to unsigned");
						_af_set_sample_format(&track->f, AF_SAMPFMT_UNSIGNED, track->f.sampleWidth);
					}
				}
				else
				/*
					If no sample width was specified, we default to 16 bits
					for signed integer data.
				*/
					_af_set_sample_format(&track->f, track->f.sampleFormat, 16);
				break;
		}
	}
	/*
		Otherwise set the sample format depending on the sample
		width or set completely to default.
	*/
	else
	{
		if (track->sampleWidthSet == AF_FALSE)
		{
			track->f.sampleWidth = 16;
			track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
		}
		else
		{
			if (track->f.sampleWidth < 1 || track->f.sampleWidth > 32)
			{
				_af_error(AF_BAD_WIDTH, "invalid sample width %d for WAVE file (must be 1-32)", track->f.sampleWidth);
				return AF_NULL_FILESETUP;
			}
			else if (track->f.sampleWidth > 8)
				/* Here track->f.sampleWidth is in {1..32}. */
				track->f.sampleFormat = AF_SAMPFMT_TWOSCOMP;
			else
				/* Here track->f.sampleWidth is in {1..8}. */
				track->f.sampleFormat = AF_SAMPFMT_UNSIGNED;
		}
	}

	if (track->f.compressionType != AF_COMPRESSION_NONE &&
		track->f.compressionType != AF_COMPRESSION_G711_ULAW &&
		track->f.compressionType != AF_COMPRESSION_G711_ALAW)
	{
		_af_error(AF_BAD_NOT_IMPLEMENTED, "compression format not supported in WAVE format");
		return AF_NULL_FILESETUP;
	}

	if (track->byteOrderSet &&
		track->f.byteOrder != AF_BYTEORDER_LITTLEENDIAN &&
		track->f.compressionType == AF_COMPRESSION_NONE)
	{
		_af_error(AF_BAD_BYTEORDER, "WAVE format only supports little-endian data");
		return AF_NULL_FILESETUP;
	}

	if (track->f.compressionType == AF_COMPRESSION_NONE)
		track->f.byteOrder = AF_BYTEORDER_LITTLEENDIAN;
	else
		track->f.byteOrder = AF_BYTEORDER_BIGENDIAN;

	if (track->aesDataSet)
	{
		_af_error(AF_BAD_FILESETUP, "WAVE files cannot have AES data");
		return AF_NULL_FILESETUP;
	}

	if (setup->instrumentSet)
	{
		if (setup->instrumentCount > 1)
		{
			_af_error(AF_BAD_NUMINSTS, "WAVE files can have 0 or 1 instrument");
			return AF_NULL_FILESETUP;
		}
		else if (setup->instrumentCount == 1)
		{
			if (setup->instruments[0].loopSet &&
				setup->instruments[0].loopCount > 0 &&
				(track->markersSet == AF_FALSE || track->markerCount == 0))
			{
				_af_error(AF_BAD_NUMMARKS, "WAVE files with loops must contain at least 1 marker");
				return AF_NULL_FILESETUP;
			}
		}
	}

	/* Make sure the miscellaneous data is of an acceptable type. */
	if (setup->miscellaneousSet)
	{
		int	i;
		for (i=0; i<setup->miscellaneousCount; i++)
		{
			switch (setup->miscellaneous[i].type)
			{
				case AF_MISC_COPY:
				case AF_MISC_AUTH:
				case AF_MISC_NAME:
				case AF_MISC_ICRD:
				case AF_MISC_ISFT:
				case AF_MISC_ICMT:
					break;
				default:
					_af_error(AF_BAD_MISCTYPE, "illegal miscellaneous type [%d] for WAVE file", setup->miscellaneous[i].type);
					return AF_NULL_FILESETUP;
			}
		}
	}

	/*
		Allocate an AFfilesetup and make all the unset fields correct.
	*/
	newsetup = _af_filesetup_copy(setup, &_af_wave_default_filesetup, AF_FALSE);

	/* Make sure we do not copy loops if they are not specified in setup. */
	if (setup->instrumentSet && setup->instrumentCount > 0 &&
		setup->instruments[0].loopSet)
	{
		free(newsetup->instruments[0].loops);
		newsetup->instruments[0].loopCount = 0;
	}

	return newsetup;
}

bool _af_wave_instparam_valid (AFfilehandle filehandle, AUpvlist list, int i)
{
	int	param, type, lval;

	AUpvgetparam(list, i, &param);
	AUpvgetvaltype(list, i, &type);
	if (type != AU_PVTYPE_LONG)
		return AF_FALSE;

	AUpvgetval(list, i, &lval);

	switch (param)
	{
		case AF_INST_MIDI_BASENOTE:
			return ((lval >= 0) && (lval <= 127));

		case AF_INST_NUMCENTS_DETUNE:
			return ((lval >= -50) && (lval <= 50));

		case AF_INST_MIDI_LOVELOCITY:
			return ((lval >= 1) && (lval <= 127));

		case AF_INST_MIDI_HIVELOCITY:
			return ((lval >= 1) && (lval <= 127));

		case AF_INST_MIDI_LONOTE:
			return ((lval >= 0) && (lval <= 127));

		case AF_INST_MIDI_HINOTE:
			return ((lval >= 0) && (lval <= 127));

		case AF_INST_NUMDBS_GAIN:
			return AF_TRUE;

		default:
			return AF_FALSE;
	}

	return AF_TRUE;
}


syntax highlighted by Code2HTML, v. 0.9.1