
/*
 * ADPCMx Decrunch : sound datatype
 *
 */

#include "classbase.h"

#define TARGET_SIZE 16384

extern __asm ULONG DecompressADPCM2(	register __a0 UBYTE *Source,
					register __d0 ULONG Length,
					register __a1 UBYTE *Destination,
					register __d1 ULONG JoinCode	);

extern __asm ULONG DecompressADPCM3(	register __a0 UBYTE *Source,
					register __d0 ULONG Length,
					register __a1 UBYTE *Destination,
					register __d1 ULONG JoinCode	);
#define	DB(x)	

Class *initClass (struct ClassBase * cb)
{
	Class *cl;
	
	if (cl = MakeClass (LibName, SOUNDDTCLASS, NULL, NULL, 0L))
	{
		cl->cl_Dispatcher.h_Entry = (ULONG (*)())Dispatch;
		cl->cl_UserData = (ULONG) cb;
		AddClass (cl);
	}
	
	return (cl);
}


ULONG __asm Dispatch (register __a0 Class * cl,
		      register __a2 Object * o,
		      register __a1 Msg msg)
{
	struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
	ULONG retval = 0L;
	
	switch (msg->MethodID)
	{
	case OM_NEW:
		if (retval = DoSuperMethodA (cl, o, msg))
		{
		if (!(ConvertObjectData (cb, cl, (Object *) retval, ((struct opSet *) msg)->ops_AttrList)))
			{
			CoerceMethod (cl, (Object *) retval, OM_DISPOSE);
			retval = NULL;
			}
		}
		break;
		
	 /* Let the superclass handle everything else */
	
	default:
		retval = (ULONG) DoSuperMethodA (cl, o, msg);
		break;
	}
	return (retval);
}

struct ADPCMHeader
{
	UBYTE Identifier[6];		/* $00 */
	ULONG Frequency;		/* $06 */
};

/*****************************************************************************/

BOOL ConvertObjectData (struct ClassBase * cb,
			Class * cl,
			Object * o,
			struct TagItem * attrs)
{
	struct FileInfoBlock *fib;
	struct VoiceHeader *vhdr;
	struct ADPCMHeader ADPCMHeader;
	STRPTR Title;
	BPTR FH;
	LONG ErrorCode=0;
	ULONG Memory;
	ULONG Size;
	ULONG SampleSize;
	UBYTE *Sample;
	
	Title = (STRPTR) GetTagData (DTA_Name, NULL, attrs);
	
	getdtattrs (cb, o,
		SDTA_VoiceHeader, &vhdr,
		DTA_Handle, &FH,
		TAG_DONE);
	
	if (FH && vhdr)
	{
		/* Allocate a temporary file info block */
		if (!(fib = (struct FileInfoBlock *) AllocMem (sizeof (struct FileInfoBlock), NULL)))
		{
			ErrorCode=ERROR_NO_FREE_STORE;
		}
		else
		{
			/* Get the size of the file */
			if (ExamineFH (FH, fib))
			{
				Size = fib->fib_Size;
			}
			else
			{
				Seek (FH, 0, OFFSET_END);
				Size = Seek (FH, 0, OFFSET_BEGINNING);
			}
			
			/* Free the temporary file info block */
			FreeMem (fib, sizeof (struct FileInfoBlock));
			
			/* Calculate size from actual file length */
			Size-=sizeof(struct ADPCMHeader);
			
			/* Read in sample header */
			if (Read(FH,&ADPCMHeader,sizeof(struct ADPCMHeader))==sizeof(struct ADPCMHeader))
			{
				/* Check if it is a supported ADPCM format */
				if (ADPCMHeader.Identifier[5] != '2' && ADPCMHeader.Identifier[5] != '3')
				{
					ErrorCode=ERROR_OBJECT_WRONG_TYPE;
				}
				else
				{
					ULONG Bits;
					
					/* Retrieve the number of compression bits */
					if (ADPCMHeader.Identifier[5] == '2') Bits=2;
					if (ADPCMHeader.Identifier[5] == '3') Bits=3;
					
					/* Calculate the real sample size */
					if (Bits==2) SampleSize=Size*4 ;
					if (Bits==3) SampleSize=Size*8/3;
					
					/* sound.datatype V40 can replay */
					/* directly from Fast RAM */
					
					Memory = (SuperClassBase->lib_Version>39) ?
								MEMF_ANY : MEMF_CHIP;
					
					/* Allocate a buffer with the calculated size */
					if (!(Sample=AllocVec(SampleSize,Memory)))
					{
						ErrorCode=ERROR_NO_FREE_STORE;
					}
					else
					{
						UBYTE *ADPCMBuffer;
						ULONG ADPCMSize;
						
						if (Bits==2) ADPCMSize=(TARGET_SIZE+3)/4;
						if (Bits==3) ADPCMSize=(TARGET_SIZE+7)/8*3;
						
						if (ADPCMBuffer=AllocVec(ADPCMSize, MEMF_ANY))
						{
							ULONG Position,SampleOffset;
							ULONG JoinCode=0;
							
							for (Position=0,SampleOffset=0; Position<Size; )
							{
								ULONG Left=Size-Position;
								ULONG Do = Left < ADPCMSize ?
										   Left : ADPCMSize ;
								LONG Got;
								
								if (Got=Read(FH, ADPCMBuffer, Do) != Do)
								{
									if (Got<0) ErrorCode=IoErr();
									else ErrorCode=ERROR_BAD_HUNK;
									break;
								}
								
								if (Bits==2) JoinCode=DecompressADPCM2(ADPCMBuffer, Do, Sample+SampleOffset, JoinCode);
								if (Bits==3) JoinCode=DecompressADPCM3(ADPCMBuffer, Do, Sample+SampleOffset, JoinCode);
								
								Position+=Do;
								
								if (Bits==2) SampleOffset+=Do*4;
								if (Bits==3) SampleOffset+=Do*8/3;
							}
							
							if (!ErrorCode)
							{
								/* Fill in the VoiceHeader */
								memset(vhdr,0,sizeof(struct VoiceHeader));
								vhdr->vh_OneShotHiSamples= Size;
								vhdr->vh_SamplesPerSec	= ADPCMHeader.Frequency;
								vhdr->vh_Octaves	= 1;
								vhdr->vh_Compression	= CMP_NONE;
								vhdr->vh_Volume		= 64;
								
								/* Tell the super-class about the attributes */
								setdtattrs (cb, o,
									DTA_ObjName,	Title,
									SDTA_Sample,	Sample,
									SDTA_SampleLength,SampleSize,
									SDTA_Period,	(ULONG)(SysBase->ex_EClockFrequency*5)/(ULONG)vhdr->vh_SamplesPerSec,
									SDTA_Volume,	64,
									SDTA_Cycles,	1,
									TAG_DONE);
							}
							else
							{
								FreeVec(Sample);
							}
							FreeVec(ADPCMBuffer);
						}
					}
				}
			}
		}
	}
	if (ErrorCode)
	{
		SetIoErr(ErrorCode);
		return(FALSE);
	}
	return(TRUE);
}


/*****************************************************************************/

ULONG setdtattrs (struct ClassBase * cb, Object * o, ULONG data,...)
{
	return (SetDTAttrsA (o, NULL, NULL, (struct TagItem *) & data));
}

/*****************************************************************************/

ULONG getdtattrs (struct ClassBase * cb, Object * o, ULONG data,...)
{
	return (GetDTAttrsA (o, (struct TagItem *) & data));
}
