/* rxplay.c */

#if 1
	char *_procname = "playproc";
	long _BackGroundIO = 0;
	#define puts(S) /**/
#else
	puts(s)
	char *s;
	{
	Write(Output(),s,strlen(s));
	Write(Output(),"\n",1);
	}
#endif

#include <exec/types.h>
#include <devices/audio.h>
#include <exec/memory.h>
#include <libraries/dos.h>

#define MakeID(a,b,c,d) ( (a)<<24|(b)<<16|(c)<<8|(d) )
#define FORM MakeID('F','O','R','M')
#define BODY MakeID('B','O','D','Y')
#define SVX  MakeID('8','S','V','X')
#define BMHD MakeID('B','M','H','D')
#define CMAP MakeID('C','M','A','P')
#define ILBM MakeID('I','L','B','M')
#define VHDR MakeID('V','H','D','R')
#define CHAN MakeID('C','H','A','N')

#define RIGHT 2
#define LEFT 1
#define STEREO (LEFT | RIGHT)

int chan[2]={ LEFT, RIGHT };

#define min(a,b) ((a)<=(b)?(a):(b))

#define NAMESIZE 12

struct Samp {
	char *lmem, *rmem, name[NAMESIZE];
	int length, rate, type;
	};

#define SAMPSIZE sizeof(struct Samp)

typedef struct Unit * UNIT;

WORD delta[16]={-34,-21,-13,-8,-5,-3,-2,-1,0,1,2,3,5,8,13,21};

#define MAXSAMP 10

struct Samp *samparr[MAXSAMP+1];

struct MsgPort *rexxport ;

int back_music_on = 0 ;

#define RXFB_RESULT  17

static struct rexxmsg {
	struct Message cm_Node;
	LONG   RFU1;
	LONG   RFU2;
	LONG   rm_Action;
	LONG   rm_Result1;
	LONG   rm_Result2;
	char   *cm_Args[16];
	LONG   RFU7;
	LONG   RFU8;
	LONG   RFU9;
	LONG   RFU10;
	LONG   RFU11;
	LONG   RFU12;
} msg;

struct MsgPort *port[2];

struct IOAudio IOBlks[2];
struct IOAudio IOB;

int lastunit=0;

main(argc,argv)
char **argv;
{
register unsigned short i;
struct rexxmsg *msg;
char s[256];

/* allocate audio chanels LEFT and RIGHT */

for (i=0; i<=1; i++) port[i]=(struct MsgPort *)CreatePort(0,0);

allocate_channel(&IOB);

for (i=0; i<=1; i++) {
	movmem(&IOB, &(IOBlks[i]), sizeof(struct IOAudio));
	IOBlks[i].ioa_Request.io_Message.mn_ReplyPort = port[i];
	IOBlks[i].ioa_Request.io_Unit=(UNIT)( i ? RIGHT : LEFT );
	}

for (i=1; i<argc; i++) execute(argv[i]);

if ( rexxport=(struct MsgPort *)CreatePort("rexx_play",0) )
	for (;;) {
		while (msg=(struct rexxmsg *)GetMsg(rexxport)) {
			strcpy(s, msg->cm_Args[0]);
			msg->rm_Result2=0;
			if (*s == 'w') execute(s);
			ReplyMsg((struct Message *)msg);
			if (*s != 'w') execute(s);
			}
		WaitPort(rexxport);
		}
clall(0);
}

clall(n)
{
register unsigned short i;

for (i=1; i<MAXSAMP; i++)
	if (samparr[i]) unload_it(samparr[i]);
deallocate_channel (&IOB);

for (i=0; i<=1; i++) if (port[i]) DeletePort(port[i]);
if (rexxport) DeletePort(rexxport);

if (back_music_on) backstop();

exit(n);
}

execute(s)
char *s;
{
static int verbose=0;

if (verbose) puts(s);
switch (*s++) {
	case 'd': dir(); break;
	case 'l': doload(s); break;
	case 'n': break;
	case 'p': doplay(s); break;
	case 'q': clall(0); break;
	case 'u': unload(s); break;
	case 'v': verbose ^= 1; break;
	case 'w': waitio();	break;
	case '+': backstart(s);	break;
	case '-': backstop();	break;
	default : puts("Command not found"); break;
	}
}

waitio()
{
register unsigned short i;

for (i=0; i<=1; i++)
	if (lastunit & chan[i])
		WaitIO(&(IOBlks[i]));

IOB.ioa_Request.io_Command=CMD_RESET;
DoIO(& IOB);
}

int find_it(s)
char *s;
{
register int i;

for (i=1; i<MAXSAMP; i++)
	if (samparr[i] && !stricmp(samparr[i]->name,s)) return i;
return 0;
}

dir()
{
register unsigned short i;

for (i=1; i<MAXSAMP; i++)
	if (samparr[i]) puts(samparr[i]->name);
}

unload(s)
char *s;
{
int n=find_it(s);

if (n) { unload_it(samparr[n]); samparr[n]=NULL; }
	else puts("Not found");
}

backstart(s)
char *s;
{
int n=find_it(s);
char *channel=(samparr[n]->lmem ? samparr[n]->lmem : samparr[n]->rmem);

if (n) {
	playsound(channel,samparr[n]->length,samparr[n]->rate);
	back_music_on = 1 ;
	}
	else puts("Not found");
}

backstop()
{
back_music_on = 0 ;
pokew(0xdff096, 3);
pokew(0xdff0a8, 0);
pokew(0xdff0b8, 0);
}

playsound(address, len, speed)
char *address;
{
pokel(0xdff0a0, address);
pokel(0xdff0b0, address);
pokew(0xdff0a4, len/2);
pokew(0xdff0b4, len/2);
pokew(0xdff0a6, speed);
pokew(0xdff0b6, speed);
pokew(0xdff0a8, 16);
pokew(0xdff0b8, 16);
pokew(0xdff096, 0x8003);
}

pokew(loc, val)
short *loc;
{
*loc= (short) val;
}

pokel(loc, val)
long *loc;
{
*loc= (long) val;
}

doplay(s)
char *s;
{
int n=find_it(s);

if (n) play(&IOB,samparr[n]);
	else puts("Not found");
}

/*****************************************/
/** perform load once got file and path **/
/*****************************************/

struct Samp *load_it(fname)
char *fname;
{
char *name;
BPTR in=NULL;
struct Samp *samp=NULL;
int len,pos,dummy;
ULONG k,x;
UWORD e1,e2,z;
UBYTE comp=0;
char *basename();

name=basename(fname);
if (strlen(name) >= NAMESIZE) { puts("Name too long"); goto abort; }
if ((in=Open(fname,MODE_OLDFILE))==0) { puts("File not found"); goto abort; }
samp=(struct Samp *)AllocMem(SAMPSIZE,MEMF_PUBLIC | MEMF_CLEAR);
if (samp==NULL) goto abort;
strcpy(samp->name,name);

Read(in,&k,4);
if (k!=FORM) {			/* not in standard form */
	samp->rate=350;		/* default playback rate */
	samp->type=LEFT;	/* use left (ch 0) as default */
	Seek(in,0,1);
	pos=Seek(in,0,-1);
	samp->length=pos;
	}
else {
	Read(in,&x,4);	/* skip length */
	Read(in,&x,4);	/* get form type */
	if (x!=SVX) {
		puts("Not 8SVX file");
		goto abort;
		}

	samp->type=RIGHT;	/* default if IFF but not Perfect Sound IFF */

getchunk:

	Read(in,&x,4);	/* get chunk type */
	while (x!=BODY && x!=CHAN && x!=VHDR) { /* skip if not what we want */
		skip(in);
		Read(in,&x,4);
		if (x==-1 || x==0) {
			puts("Bad IFF file--Can't load.");
			goto abort;
			}
		}

	if (x==CHAN) {
		Read(in,&x,4);
		if (x!=4) {
			puts("Bad CHAN length.");
			goto abort;
			}
		Read(in,&x,4);
		samp->type=0;
		if (x & 2) samp->type |= LEFT;
		if (x & 4) samp->type |= RIGHT;
		goto getchunk;
		}

	if (x==VHDR) {
		Read(in,&x,4); /* should be 20, length of VHDR */
		if (x!=20) {
			puts("Length of VHDR != 20");
			goto abort;
			}
		Read(in,&dummy,4); /* should be length of sample */
		Read(in,&dummy,4); /* repeat */
		Read(in,&dummy,4); /* cycles */
		Read(in,&z,2);   /* get samples per second */
		Read(in,&dummy,1);
		samp->rate=3579546/z;
		Read(in,&comp,1);
		Seek(in,4,0);		/* skip volume */
		goto getchunk;
		}


	/* this must be a body (only thing left) */

	Read(in,&samp->length,4);
	if (samp->type==STEREO) samp->length=samp->length>>1;
	}

/* read in data--either BODY or dump */

len=samp->length;
if (comp==1) len=(len-2)*2;

if (samp->type & LEFT ) {
	samp->lmem=(char *)AllocMem(len,MEMF_CHIP);
	if (samp->lmem==0) goto abort;
	if (comp)
		e1=cread(in,samp->lmem,samp->length);
	else
		e1=Read (in,samp->lmem,samp->length);
	}

if (samp->type & RIGHT) {
	samp->rmem=(char *)AllocMem(len,MEMF_CHIP);
	if (samp->rmem==0) goto abort;
	if (comp)
		e2=cread(in,samp->rmem,samp->length);
	else
		e2=Read (in,samp->rmem,samp->length);
	}

if (e1==-1 || e2==-1) {
	puts("ERROR loading file!");
	goto abort;
	}

Close(in);

samp->length=len;
return samp;

abort:
if (in) Close(in);
if (samp) {
	if (samp->lmem) FreeMem(samp->lmem, len);
	if (samp->rmem) FreeMem(samp->rmem, len);
	FreeMem(samp,SAMPSIZE);
	}
return NULL;
}

doload(s)
char *s;
{
register unsigned short i;

if (find_it(s)) { puts("Already loaded"); return; }
for (i=1; i<MAXSAMP; i++) if (samparr[i]==NULL) break;
if (i==MAXSAMP) { puts("No room!!!"); return; }
samparr[i]=(struct Samp *)load_it(s);
}

/***************************/
/* Play a digitized sample */
/***************************/

play(IOB,samp)
struct Samp *samp;
struct IOAudio *IOB;
{
register int i;

lastunit=samp->type;	/* global, used by waitio */

IOB->ioa_Request.io_Unit=(UNIT)lastunit;
IOB->ioa_Request.io_Command=CMD_RESET;
DoIO(IOB);

IOB->ioa_Request.io_Command=CMD_STOP;
DoIO(IOB);

for (i=0; i<=1; i++)
	if (lastunit & chan[i])
		tochan(&(IOBlks[i]),(i ? samp->rmem : samp->lmem),
					samp->length,samp->rate,1);

IOB->ioa_Request.io_Unit=(UNIT)lastunit;
IOB->ioa_Request.io_Command=CMD_START;
DoIO(IOB);
}

unload_it(samp)
struct Samp *samp;
{
if (samp) {
	if (samp->lmem) FreeMem(samp->lmem, samp->length);
	if (samp->rmem) FreeMem(samp->rmem, samp->length);
	FreeMem(samp,SAMPSIZE);
	}
}

/*****************************/
/* allocate an audio channel */
/*****************************/

allocate_channel (IOB)
struct IOAudio *IOB;
{
struct MsgPort *port;
int i;
char *x;
static UBYTE allocation_map[] = {STEREO};

x=(char *)IOB;
for (i=0; i<sizeof(struct IOAudio); i++) x[i]=(char)0;

if (OpenDevice("audio.device", 0, IOB, 0)) puts("can't open audio.device");

/* now allocate a channel */

IOB->ioa_Request.io_Message.mn_Node.ln_Pri = 127;
if ((port=(struct MsgPort *)CreatePort(0, 0)) == 0) puts("can't open port");
IOB->ioa_Request.io_Message.mn_ReplyPort = port;
IOB->ioa_Request.io_Command = ADCMD_ALLOCATE;
IOB->ioa_Data = allocation_map;
IOB->ioa_Length = sizeof (allocation_map);

SendIO(IOB);
if (WaitIO(IOB)) puts("error in allocation");
}

/*******************************/
/* deallocate an audio channel */
/*******************************/

deallocate_channel (IOB)
struct IOAudio *IOB;
{
struct MsgPort *port;

if (IOB) {
	if (IOB->ioa_Request.io_Device) {
		IOB->ioa_Request.io_Command = ADCMD_FREE;
		DoIO(IOB);
		CloseDevice(IOB);
		}
	if (port = IOB->ioa_Request.io_Message.mn_ReplyPort) DeletePort(port);
	}
}

/*****************************************/
/* Play a sample on an allocated channel */
/*****************************************/

tochan (IOB, data, length, rate,cyc)
char *data;
struct IOAudio *IOB;
{
IOB->ioa_Request.io_Command = CMD_WRITE;
IOB->ioa_Request.io_Flags = ADIOF_PERVOL;

IOB->ioa_Data = data;
IOB->ioa_Length = (ULONG)length;
IOB->ioa_Period = rate;
IOB->ioa_Volume = 64;
IOB->ioa_Cycles = cyc;

BeginIO(IOB);
}

/************************/
/* compressed data read */
/************************/

/* Uses 'Fibonacci delta' */

cread(fp,data,len)
register int len;
register char *data;
{
BYTE x,d;
register LONG i,j;
BYTE buf[512];

Read(fp,&d,1);   /* skip padding */
Read(fp,&x,1);   /* get inital value */

len=len-2;
if (Read(fp,buf,min(512,len))==-1) return(-1);
j=0;
for (i=0;i<len;i++) {
	*data=x+delta[15&(buf[j]>>4)];
	x=*data++;
	*data=x+delta[buf[j++]&15];
	x=*data++;
	if (j==512) {
		j=0;
		if (Read(fp,buf,min(512,(len-i-1)))==-1) return(-1);
		}
	}
return 0;
}

/***************/
/* skip a form */
/***************/

skip(in)
{
ULONG x;

Read(in,&x,4);	/* get length */
Seek(in,x,0);	/* seek past it */
if (x&1) Seek(in,1,0);		/* skip padding if odd */
}

char *basename(s)
char *s;
{
register char *p;

for (p=s; *p; p++)
	if (*p=='/' || *p==':') s=p+1;
return s;
}

MemCleanup() {}
