/* Written by Theo Vosse, Oct. '93
   Questions or comment: vosse@ruls41.LeidenUniv.nl
   Have Fun...
 */

/* The construct Fail, Catch, EndCatch
   is a simple macro mechanism for exception handling

#define Fail goto ERRORLABEL
#define Catch goto SUCCESSLABEL; ERRORLABEL:
#define EndCatch SUCCESSLABEL:

*/


struct TX16WHeader /* As described in the FAQ */
{
	char filetype[6]	  /* = "LM8953" */;
	Byte nulls[10];
	Byte dummy_aeg[6];	  /* space for the AEG (never mind this) */
	Byte format;		  /* 0x49 = looped, 0xC9 = non-looped */
	Byte sample_rate;	  /* 1 = 33 kHz, 2 = 50 kHz, 3 = 16 kHz */
	Byte atc_length[3];	  /* Length of the attack part + sample frequency */
	Byte rpt_length[3];   /* Length of the repeat part + sample frequency */
	Byte unused[2];		  /* set these to null, to be on the safe side */
};


static OSErr TX16WSound::ReadFile(int fRefNum, long int fSize)
{
	int *dest;
	int ampl;
	long int i = 0;
	struct TX16WHeader header;
	OSErr iErr = noErr;
	long int count = sizeof(header);
	Byte state = 0;
	Byte *ptr;
	Byte prev, ch;

	if ((iErr = FSRead(fRefNum, &count, &header)) != noErr) Fail;
	fSize -= 32;
	if (header.sample_rate == 3)
	{
		sampleRate = 16667;
	}
	else /* Macintosh does not support 50kHz sounds! */
	{
		sampleRate = 33333;
	}
	nrOfSamples = fSize * 2 / 3; /* Somewhat simple! */
	fileData = (int **) NewHandle(nrOfSamples * sizeof(int));
	if (fileData == NULL)
	{
		return (MemError());
	}
	if ((sndHandle = NewHandle(nrOfSamples + sizeof(SndStruct))) == NULL)
	{
		return (MemError());
	}
	HLock(fileData);
	HLock(sndHandle);
	dest = *fileData;
	count = 0;
	while (i != nrOfSamples)
	{
		prev = ch;
		if (count == 0)
		{
			count = (fSize > nrOfSamples? nrOfSamples: fSize);
			ptr = (Byte *) *sndHandle;
			iErr = FSRead(fRefNum, &count, ptr);
			fSize -= count;
			if (count == 0 || iErr != noErr) Fail;
		}
		ch = *ptr++;
		count--;
		switch (state)
		{
			case 0:
				state = 1;
				break;
			case 1:
				state = 2;
				ampl = ((prev & 0xFF) << 4) | ((ch & 0xF0) >> 4);
				break;
			case 2:
				state = 0;
				ampl = ((ch & 0xFF) << 4) | (prev & 0x0F);
				break;
		}
		if (state != 1)
		{
			/* Here, the 12 bits is written to an array of ints */
			if (ampl >= 2048) *dest++ = (ampl - 4096) << 4;
			else *dest++ = ampl << 4;
			i++;
		}
	}
	/* This section converts the 16 bits signed ints to unsigned bytes, which
	   the Macintosh can play */
	for (i = 0, ptr = (Byte *) *sndHandle + sizeof(SndStruct), dest = *fileData; i != nrOfSamples; i++, ptr++, dest++)
	{
		*ptr = (*dest >> 8) ^ 0x80;
	}
	/* Macintosh sound stuff */
	InitHeader((SndStruct *) *sndHandle, (long int) sampleRate << 16, nrOfSamples);
	sndStart = sizeof(SndStruct);
	HUnlock(sndHandle);
	HUnlock(fileData);
Catch
	if (fileData != NULL) HUnlock(fileData);
	Signal(iErr);
	return (-1);
EndCatch
	return (noErr);
}

OSErr Sound::WriteTX16WSound(unsigned char *fName, int vRefNum)
{
	struct TX16WHeader header;
	OSErr iErr;
	long int i;
	int j;
	long int nrWords = nrOfSamples + nrOfSamples % 2;
	int w1, w2;

	/* Initialize the header with zeros */
	memset(&header, 0, sizeof(header));
	strcpy(header.filetype, "LM8953"); /* ID */
	memset(header.dummy_aeg + 2, 0x7F, 4); /* Plain envelope */
	header.format = 0xC9; /* Non-looping */
	switch (sampleRate)
	{
		case 16667: header.sample_rate = 3; break;
		case 33333: header.sample_rate = 1; break;
		default:
			ErrorMessage("\pUnsupported TX16W frequency", "\p", "\p", "\p");
			return (-1);
	}
	if (nrWords >= 0x20000)
	{
		ErrorMessage("\pSound too large for TX16W", "\p", "\p", "\p");
	}
	header.atc_length[0] = nrWords;
	header.atc_length[1] = nrWords >> 8;
	header.atc_length[2] = (nrWords >> 16) & 0x01;
	header.rpt_length[0] = 0x40; /* One block for an empty repeat part */
	switch (header.sample_rate == 1)
	{
		case 1:
			header.atc_length[2] |= 0x06;
			header.rpt_length[2] |= 0x52;
			break;
		case 2:
			header.atc_length[2] |= 0x10;
			header.rpt_length[2] |= 0x00;
			break;
		case 3:
			header.atc_length[2] |= 0xF6;
			header.rpt_length[2] |= 0x52;
			break;
	}
	/* Open Macintosh file */
	if ((iErr = NewOpenFile(fName, vRefNum, 'SndC', 'TX16')) != noErr) Fail;
	/* Write header */
	if ((iErr = WriteBytes((Byte *) &header, sizeof(header))) != noErr) Fail;
	/* Write 2 words at a time */
	for (i = 0; i != nrWords; i += 2)
	{
		w1 = GetWord(i) >> 4; if (w1 < 0) w1 += 4096;
		w2 = GetWord(i + 1) >> 4; if (w2 < 0) w2 += 4096;
		if ((iErr = WriteByte(w1 >> 4)) != noErr) Fail;
		if ((iErr = WriteByte(((w1 & 0x0F) << 4) | (w2 & 0x0F))) != noErr) Fail;
		if ((iErr = WriteByte(w2 >> 4)) != noErr) Fail;
	}
	/* Write empty repeat part (0x40 words = 96 bytes) */
	for (j = 0; j != 96; j++)
	{
		if ((iErr = WriteByte(0)) != noErr) Fail;
	}
	i += 0x40;
	/* Fill up to 256 bytes; the TX16W seems to like that */
	while ((i & 0x100) != 0)
	{
		if ((iErr = WriteByte(0)) != noErr) Fail;
		i++;
	}
Catch
	Signal(iErr);
EndCatch
	return (CloseFile());
}
