 
/* DeliTracker-Player for ADPCM audio samples */

#define PLAYER_VERSION 1
#define PLAYER_REVISION 0

/* Note: TAB SIZE = 4 */

/* Includes */

#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/intuition.h>
#include <libraries/dos.h>
#include <dos/rdargs.h>
#include <utility/tagitem.h>
#include <devices/audio.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <string.h>
#include <stdarg.h>
#include "DeliPlayer.h"

/* Externals */

extern struct DosLibrary *DOSBase;
extern struct IntuitionBase *IntuitionBase;

/* DeliTracker's stuff */

struct DeliTrackerGlobals *DeliBase;
struct MsgPort *DeliPort;

/* Copyright and info */

UBYTE AboutString[]="a player for ADPCM audio samples\n"
					"in MONO ADPCM2 or ADPCM3 format.\n"
					"(c) 1995 by Christian Buchner";


void __asm __saveds DeliProcess(void);
ULONG __asm __saveds Check(void);
ULONG __asm __saveds InitPlayer(void);
ULONG __asm __saveds EndPlayer(void);
ULONG __asm __saveds InitSound(void);
ULONG __asm __saveds EndSound(void);
ULONG __asm __saveds StartInt(void);
ULONG __asm __saveds StopInt(void);
ULONG __asm __saveds Faster(void);
ULONG __asm __saveds Slower(void);
ULONG __asm __saveds VolBalance(void);
void __stdargs Message(UBYTE *Msg,...);

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	);


/* Tag list for DeliTracker */

struct TagItem PlayerTagArray[]=
{
	DTP_RequestDTVersion,	16,
	DTP_PlayerVersion,		(PLAYER_VERSION<<16)+PLAYER_REVISION,
	DTP_PlayerName,			(ULONG)"ADPCM-Player",
	DTP_Creator,			(ULONG)AboutString,
	DTP_Description,		(ULONG)"a player for ADPCM samples",
	DTP_Flags,				PLYF_SONGEND|PLYF_ANYMEM,
	DTP_DeliBase,			(ULONG)&DeliBase,
	DTP_Check2,				(ULONG)&Check,
	DTP_Process,			(ULONG)&DeliProcess,
	DTP_Priority,			0,
	DTP_StackSize,			4096,
	DTP_MsgPort,			(ULONG)&DeliPort,
	DTP_InitPlayer,			(ULONG)&InitPlayer,
	DTP_EndPlayer,			(ULONG)&EndPlayer,
	DTP_InitSound,			(ULONG)&InitSound,
	DTP_EndSound,			(ULONG)&EndSound,
	DTP_StartInt,			(ULONG)&StartInt,
	DTP_StopInt,			(ULONG)&StopInt,
	DTP_Volume,				(ULONG)&VolBalance,
	DTP_Balance,			(ULONG)&VolBalance,
	DTP_Faster,				(ULONG)&Faster,
	DTP_Slower,				(ULONG)&Slower,
	TAG_DONE
};


/* Variables and data for parsing the options (same as in CLIInterface.c) */

UBYTE IDString[]="ADPCM";

struct Process *MyProc;

BOOL ChanInit;
struct MsgPort *LeftReply[2];
struct MsgPort *RightReply[2];
struct IOAudio *LeftAudio[2];
struct IOAudio *RightAudio[2];

BOOL Playing;

#define CHIP_SIZE 4096
UBYTE *ChipBuffer[2];

UBYTE *ModMem;
ULONG ModLen;

ULONG Bits;
ULONG Frequency;
ULONG LeftVolume=64;
ULONG RightVolume=64;
ULONG RealLen;

BOOL BufPlaying[2];
ULONG Position;
ULONG JoinCode;


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

/* Our player's Process */

void __asm __saveds DeliProcess(void)
{
	BOOL ProcActive=TRUE;
	ULONG Signals;
	struct DeliMessage *DeliMessage;
	UWORD i;
	
	DOSBase=(struct DosLibrary*)DeliBase->DOSBase;
	IntuitionBase=(struct IntuitionBase*)DeliBase->IntuitionBase;
	
	MyProc=(struct Process*)FindTask(NULL);
	MyProc->pr_Task.tc_Node.ln_Pri=21;
	
	while(ProcActive)
	{
		ULONG SigMask = SIGBREAKF_CTRL_C | (1L<<DeliPort->mp_SigBit);
		
		if (ChanInit) SigMask |=	(1L<<LeftReply[0]->mp_SigBit) | 
									(1L<<LeftReply[1]->mp_SigBit) ;
		
		Signals=Wait(SigMask);
		
		if (Signals & SIGBREAKF_CTRL_C)
		{
			ProcActive=FALSE;
		}
		
		if (Signals & (1L<<DeliPort->mp_SigBit))
		{
			if (DeliMessage=(struct DeliMessage*)GetMsg(DeliPort))
			{
				DeliMessage->Result=(DeliMessage->Function)();
				ReplyMsg((struct Message*)DeliMessage);
			}
		}
		
		if (ChanInit)
		{
			LONG ChipMax, Left, Do, DMALen;
			
			for (i=0;i<2;i++)
			{
				if (Signals & (1L<<LeftReply[i]->mp_SigBit))
				{
					if (BufPlaying[i])
					{
						WaitPort(LeftReply[i]);		GetMsg(LeftReply[i]);
						WaitPort(RightReply[i]);	GetMsg(RightReply[i]);
						BufPlaying[i]=FALSE;
					}
					
					if (Playing)
					{
						if (Bits==2) ChipMax = (CHIP_SIZE+3)/4;
						if (Bits==3) ChipMax = (CHIP_SIZE+7)/8*3;
						
						if (Position>=RealLen)
						{
							/* Signal Songend */
							dt_SongEnd();
							
							Position=0;
						}
						
						Left = RealLen-Position;
						Do = Left < ChipMax ? Left : ChipMax;
						
						if (Do>0)
						{
							UBYTE *Source=ModMem+10+Position;
							
							if (Bits==2) JoinCode=DecompressADPCM2(Source, Do, ChipBuffer[i], JoinCode);
							if (Bits==3) JoinCode=DecompressADPCM3(Source, Do, ChipBuffer[i], JoinCode);
							
							Position+=Do;
							
							if (Bits==2) DMALen = Do*4;
							if (Bits==3) DMALen = Do*8/3;
							
							LeftAudio[i]->ioa_Data =
							RightAudio[i]->ioa_Data = ChipBuffer[i];
							
							LeftAudio[i]->ioa_Length =
							RightAudio[i]->ioa_Length = DMALen;
							
							LeftAudio[i]->ioa_Period = 
							RightAudio[i]->ioa_Period=(*(struct ExecBase**)(4))->ex_EClockFrequency*5/Frequency;
							
							LeftAudio[i]->ioa_Volume=LeftVolume;
							RightAudio[i]->ioa_Volume=RightVolume;
							
							LeftAudio[i]->ioa_Cycles=
							RightAudio[i]->ioa_Cycles=1;
							
							LeftAudio[i]->ioa_Request.io_Flags|=ADIOF_PERVOL;
							RightAudio[i]->ioa_Request.io_Flags|=ADIOF_PERVOL;
							
							LeftAudio[i]->ioa_Request.io_Command=
							RightAudio[i]->ioa_Request.io_Command=CMD_WRITE;
							
							Forbid();
							BeginIO(LeftAudio[i]);
							BeginIO(RightAudio[i]);
							BufPlaying[i]=TRUE;
							Permit();
						}
					}
				}
			}
		}
	}
}


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

/* Module check routine */

ULONG __asm __saveds Check(void)
{
	BOOL Result=TRUE;
	
	if (!strncmp(IDString,DeliBase->ChkData,5))
	{
		if ( ((UBYTE*)DeliBase->ChkData)[5]=='2' ||
			 ((UBYTE*)DeliBase->ChkData)[5]=='3' )
		{
			Result=FALSE;
		}
	}
	return(Result);
}


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

/* Init player (alloc channels, etc...) */

ULONG __asm __saveds InitPlayer(void)
{
	BOOL Error=TRUE;
	UWORD i;
	
	UBYTE LeftArray[2]={1,8};
	UBYTE RightArray[2]={2,4};
	
	for (i=0;i<2;i++)
	{
		if (!(LeftReply[i]=CreateMsgPort())) break;
		if (!(RightReply[i]=CreateMsgPort())) break;
		
		if (!(LeftAudio[i]=CreateIORequest(LeftReply[i],sizeof(struct IOAudio)))) break;
		if (!(RightAudio[i]=CreateIORequest(RightReply[i],sizeof(struct IOAudio)))) break;
		
		if (!(ChipBuffer[i]=AllocVec(CHIP_SIZE,MEMF_CHIP))) break;
	}
	if (i==2)
	{
		LeftAudio[0]->ioa_Request.io_Message.mn_Node.ln_Pri=127;
		LeftAudio[0]->ioa_Length=sizeof(LeftArray);
		LeftAudio[0]->ioa_Data=LeftArray;
		LeftAudio[0]->ioa_Request.io_Flags|=ADIOF_NOWAIT;
		if (!OpenDevice("audio.device",0L,(struct IORequest *)LeftAudio[0],0))
		{
			LeftAudio[1]->ioa_Request.io_Device=LeftAudio[0]->ioa_Request.io_Device;
			LeftAudio[1]->ioa_Request.io_Unit=LeftAudio[0]->ioa_Request.io_Unit;
			LeftAudio[1]->ioa_AllocKey=LeftAudio[0]->ioa_AllocKey;
			
			RightAudio[0]->ioa_Length=sizeof(RightArray);
			RightAudio[0]->ioa_Request.io_Message.mn_Node.ln_Pri=127;
			RightAudio[0]->ioa_Data=RightArray;
			RightAudio[0]->ioa_Request.io_Flags|=ADIOF_NOWAIT;
			if (!OpenDevice("audio.device",0L,(struct IORequest *)RightAudio[0],0))
			{
				RightAudio[1]->ioa_Request.io_Device=RightAudio[0]->ioa_Request.io_Device;
				RightAudio[1]->ioa_Request.io_Unit=RightAudio[0]->ioa_Request.io_Unit;
				RightAudio[1]->ioa_AllocKey=RightAudio[0]->ioa_AllocKey;
				
				ModMem=dt_GetListDataPos(0);
				ModLen=dt_GetListDataSize(0);
				if (ModMem[5]=='2') Bits=2;
				if (ModMem[5]=='3') Bits=3;
				Frequency= *(ULONG*)(ModMem+6);
				RealLen=ModLen-10;
				
				ChanInit=TRUE;
				
				Error=FALSE;
			}
		}
	}
	
	if (Error)
	{
		EndPlayer();
	}
	
	DeliBase->SndNum=1;
	
	return(Error);
}


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

/* Clean up the Player (deallocate, etc..) */

ULONG __asm __saveds EndPlayer(void)
{
	WORD i;
	
	for (i=1;i>=0;i--)
	{
		if (ChipBuffer[i])
		{
			FreeVec(ChipBuffer[i]);
			ChipBuffer[i]=NULL;
		}
		
		if (RightAudio[i])
		{
			if (i==0 && RightAudio[i]->ioa_Request.io_Device)
			{
				CloseDevice(RightAudio[i]);
				RightAudio[i]->ioa_Request.io_Device=NULL;
			}
			DeleteIORequest(RightAudio[i]);
			RightAudio[i]=NULL;
		}
		
		if (RightReply[i])
		{
			DeleteMsgPort(RightReply[i]);
			RightReply[i]=NULL;
		}
		
		if (LeftAudio[i])
		{
			if (i==0 && LeftAudio[i]->ioa_Request.io_Device)
			{
				CloseDevice(LeftAudio[i]);
				LeftAudio[i]->ioa_Request.io_Device=NULL;
			}
			DeleteIORequest(LeftAudio[i]);
			LeftAudio[i]=NULL;
		}
		
		if (LeftReply[i])
		{
			DeleteMsgPort(LeftReply[i]);
			LeftReply[i]=NULL;
		}
	}
	
	ChanInit=FALSE;
	
	return(0);
}


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

/* Initialize the "Module" */

ULONG __asm __saveds InitSound(void)
{
	Playing=FALSE;
	BufPlaying[0]=BufPlaying[1]=FALSE;
	Position=0;
	JoinCode=0;
	
	return(0);
}


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

/* End sound */

ULONG __asm __saveds EndSound(void)
{
	return(0);
}


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

/* Start sound */

ULONG __asm __saveds StartInt(void)
{
	Playing=TRUE;
	
	Signal(MyProc,(1L<<LeftReply[0]->mp_SigBit));
	Signal(MyProc,(1L<<LeftReply[1]->mp_SigBit));
	
	return(0);
}


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

/* Stop sound */

ULONG __asm __saveds StopInt(void)
{
	UWORD i;
	
	Playing=FALSE;
	
	for (i=0;i<2;i++)
	{
		if (BufPlaying[i])
		{
			AbortIO(LeftAudio[i]);
			AbortIO(RightAudio[i]);
			WaitPort(LeftReply[i]);		GetMsg(LeftReply[i]);
			WaitPort(RightReply[i]);	GetMsg(RightReply[i]);
			BufPlaying[i]=FALSE;
		}
	}
	
	return(0);
}


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

/* Play Faster */

ULONG __asm __saveds Faster(void)
{
	Frequency+=100;
	return(0);
}


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

/* Slower */

ULONG __asm __saveds Slower(void)
{
	Frequency-=100;
	return(0);
}


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

/* Volume and Balance */

ULONG __asm __saveds VolBalance(void)
{
	LeftVolume= DeliBase->SndVol*DeliBase->SndLBal/64;
	RightVolume=DeliBase->SndVol*DeliBase->SndRBal/64;
	return(0);
}


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

/* Show a message to the user */

void __stdargs Message(UBYTE *Msg,...)
{
	va_list Arg;
	struct EasyStruct Req={sizeof(struct EasyStruct),0,"ADPCM-Player message",0,"Okay"};
	Req.es_TextFormat=Msg;
	va_start(Arg,Msg);
	
	if (IntuitionBase)
	{
		EasyRequestArgs(NULL,&Req,0,Arg);
	}
	else
	{
		VPrintf(Msg,Arg);
		Printf("\n");
	}
	
	va_end(Arg);
}
