/*
 * OmniPlay, v0.98
 * by David Champion
 *
 * command parser & playroutine
 * 29 Aug 92
 */


#include <exec/types.h>
#include <exec/exec.h>

#include <graphics/gfxbase.h>
#include <dos/dos.h>
#include <devices/audio.h>
#include <clib/exec_protos.h>
#include <stdio.h>
#include "oplay.h"

/* prototypes */
void	main(int, char **);
void	play(const char *);
void	info(const char *, const char *);
UWORD	toperiod(const UWORD);
struct IOAudio	*GetIOB(ULONG);
struct IOAudio	*CopyIOB(struct IOAudio *);
void	FreeIOB(struct IOAudio *);
void	FreeCopy(struct IOAudio *);
ULONG	LoadIOB(struct FileHandle *, struct IOAudio *, ULONG, char *);
void	clean(int);
void	Usage(void);
extern	ULONG	readfile(char *);
extern	void	propinfo(char *, char *, ULONG);
extern	char	*tmpnam(char *);

/* constants */
const UBYTE	ch_l[4]	= {1, 8, 1, 8};
const UBYTE	ch_r[4]	= {2, 4, 2, 4};
const UBYTE	ch_e[4]	= {1, 4, 2, 8};
const UBYTE	ch_s[4]	= {3, 5, 10, 12};

/* globals */
char *version = "$VER: OmniPlay 0.98 (3.9.92)";
struct FileHandle	*fp;
UBYTE	*chan		= ch_e;
char	utype		= AUTO;
UWORD	uvol		= 0;
UWORD	urate		= 0;
UWORD	vol		= 0;
UWORD	rate		= 0;
ULONG	bufsiz		= 4000;
char	*myname;
char	cvmode		= CV_NONE;
BOOL	showprops	= 1;
UWORD	displaymode	= NTSC;
LONG	oldpri;
BOOL	genulawtab	= 0;
BOOL	showmax		= 0;
int	max;
signed char		logs[256];
struct Library		*PPBase;


void main(int ac, char **av)
{
char	option;
char	*optarg;
LONG	pri = -1;
struct GfxBase	*GfxBase;

	if ( !ac ) exit(0);		/* No WB usage */

	myname=av[0];

	if ( GfxBase = (struct Library *)OpenLibrary("graphics.library", 0L) ) {
		displaymode = GfxBase->DisplayFlags;	/* PAL or NTSC? */
		CloseLibrary(GfxBase);
	}
	else info("Can't get display mode; assuming NTSC", myname);

		/* get pplib */
	PPBase = (struct Library *)OpenLibrary("powerpacker.library", 33L);

	oldpri = SetTaskPri((struct Task *)FindTask(NULL), pri);
	if ( ac == 1 ) Usage();
	shift;
	while ( ac > 0 ) {
		if ( USERBREAK ) {
			info("User break", myname);
			break;
		}
		if ( av[0][0] ==  '-' ) {
			option	= av[0][1];
			if ( strlen(av[0]) == 2 ) {
				shift;
				optarg=av[0];
			}
			else optarg=&av[0][2];
			switch ( option ) {
			case	'h':
				Usage();
				break;
			case	'v':
				uvol = atoi(optarg);
				break;
			case	'r':
				urate = atoi(optarg);
				break;
			case	'c':
				if ( optarg[0] == 'l' ) chan = ch_l;
				else if ( optarg[0] == 'r' ) chan = ch_r;
				else if ( optarg[0] == 'e' ) chan = ch_e;
				else if ( optarg[0] == 's' ) chan = ch_s;
				else {
					info("Unknown channel", optarg);
					clean(10);
				}
				break;
			case	'b':
				bufsiz = atoi(optarg);
				break;
			case	'-':
				play( optarg );
				break;
			case	'p':
				pri = atoi(optarg);
				if ( ( pri < -10 ) || (pri > 10) ) {
					info("Can't change priority out of {-10, 10}", av[0]);
					break;
				}
				SetTaskPri((struct Task *)FindTask(NULL), pri);
			case	'w':
				Delay(atoi(optarg)*50);
				break;
			case	's':
				if ( (strcmp(optarg, "0")) && (strcmp(optarg, "1")) ) info("Bad usage of -s option", av[0]);
				else showprops = (BOOL)atoi(optarg);
				break;
			case	'd':
				if ( !strcmp(optarg, "PAL") ) displaymode = PAL;
				else if ( !strcmp(optarg, "NTSC") ) displaymode = NTSC;
				else info("Bad usage of -d option", av[0]);
				break;
			case	'f':
				if ( strlen(optarg) != 1 ) {
					info("Bad usage of -f option", av[0]);
					break;
				}
				switch( optarg[0] ) {
				case	'u':
					utype = RAWU;
					break;
				case	's':
					utype = RAWS;
					break;
				case	'l':
					utype = RAWL;
					break;
				case	'a':
					utype = AUTO;
					break;
				default:
					info("Bad usage of -f option", av[0]);
					break;
				}
				break;
			case	'g':
				if ( (strcmp(optarg, "0")) && (strcmp(optarg, "1")) ) info("Bad usage of -g option", av[0]);
				else genulawtab = (BOOL)atoi(optarg);
				break;
			case	'm':
				if ( (strcmp(optarg, "0")) && (strcmp(optarg, "1")) ) info("Bad usage of -m option", av[0]);
				else showmax = (BOOL)atoi(optarg);
				break;
			default:
				info("Unknown switch", av[0]);
				clean(10);
			}
		}
		else {
			play( av[0] );
		}
		shift;
	}
	clean(0);
}

void play(const char *fn)
{
struct IOAudio	*buf1, *buf2;
register ULONG	len;
register ULONG	size;
register int	b2used=0;

	if ( showprops ) printf("\n"COL3"File      : %s"COL1"\n", fn);

	if ( (len = readfile(fn)) == 0L ) return;

	rate = toperiod(rate);

	if ( showmax && (cvmode & CV_ULAW) ) printf(COL3"U-law max "COL1": %d.\n", max);

	if ( !(buf1 = (struct IOAudio *)GetIOB(bufsiz)) ) {
		info("Can't allocate buffer one", fn);
		Close(fp);
		return;
	}
	buf1->ioa_Period = rate;
	buf1->ioa_Volume = vol;
	buf1->ioa_Cycles = 1;
	if ( !(buf2 = (struct IOAudio *)CopyIOB(buf1)) ) {
		info("Can't allocate buffer two", fn);
		FreeIOB(buf1);
		Close(fp);
		return;
	}

	do {
		if ( USERBREAK ) {
			info("User break", fn);
			len = 0;
		}
		if ( len <= 0 ) {
			WaitIO((struct ioRequest *)buf1);
			break;
		}
		size = MIN(len, bufsiz);
		if ( !LoadIOB(fp, buf1, size, fn) ) {
			if ( b2used ) WaitIO((struct IOAudio *)buf2);
			break;
		}
		len -= size;
		buf1->ioa_Length = size;
		BeginIO((struct ioRequest *)buf1);

		if ( b2used ) WaitIO((struct ioRequest *)buf2);
		if ( USERBREAK ) {
			info("User break", fn);
			len = 0;
		}
		if ( len <= 0 ) {
			WaitIO((struct ioRequest *)buf1);
			break;
		}
		size = MIN(len, bufsiz);
		if ( !LoadIOB(fp, buf2, size, fn) ) {
			WaitIO((struct IOAudio *)buf1);
			break;
		}
		b2used++;
		len -= size;
		buf2->ioa_Length = size;
		BeginIO((struct ioRequest *)buf2);

		WaitIO((struct ioRequest *)buf1);
	} while ( TRUE );

	FreeIOB(buf1);
	FreeCopy(buf2);

	Close(fp);

	return;
}

void info(const char *msg, const char *offender)
{
	fprintf(stderr, "%s: %s: %s\n", myname, offender, msg);
	return;
}

UWORD toperiod(const UWORD sps)
{
	if ( displaymode == PAL  )	return( 1000000000 / (sps*281) );
		/* not PAL, assume NTSC (GENLOC not an option here... */
	else				return( 1000000000 / (sps*279) );
}

struct IOAudio *GetIOB(ULONG size)
{
struct IOAudio	*IOB;
void	*buf;
struct Port	*port;

	if ( !(port = (struct Port *)CreatePort("omniplay", 0)) ) return(NULL);
	if ( !(buf  = (void *)AllocMem(size, MEMF_CHIP)) ) {
		DeletePort(port);
		return(NULL);
	}
	if ( !(IOB = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR)) ) {
		DeletePort(port);
		FreeMem(buf, size);
		return(NULL);
	}
	IOB->ioa_Request.io_Message.mn_Node.ln_Pri = 0;
	IOB->ioa_Request.io_Message.mn_ReplyPort = port;
	IOB->ioa_Data = chan;
	IOB->ioa_Length = 4;
	IOB->ioa_Request.io_Command = ADCMD_ALLOCATE;
	OpenDevice("audio.device", 0, (struct ioRequest *)IOB, 0);
	if ( !IOB->ioa_AllocKey ) {
		DeletePort(port);
		FreeMem(buf, size);
		FreeMem(IOB, sizeof(struct IOAudio));
		return(NULL);
	}
	IOB->ioa_Request.io_Command = CMD_WRITE;
	IOB->ioa_Request.io_Flags = ADIOF_PERVOL;
	IOB->ioa_Data = buf;
	IOB->ioa_Length = size;

	return((struct IOAudio *)IOB);
}

struct IOAudio *CopyIOB(struct IOAudio *oldIOB)
{
struct IOAudio *IOB;
void 	*buf;

	if ( !oldIOB ) return(NULL);
	if ( !(buf = (void *)AllocMem(oldIOB->ioa_Length, MEMF_CHIP)) ) return(NULL);
	if ( !(IOB = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC|MEMF_CLEAR)) ) {
		FreeMem(buf, oldIOB->ioa_Length);
		return(NULL);
	}
	CopyMem(oldIOB, IOB, sizeof(struct IOAudio));
	IOB->ioa_Data = buf;
	return((struct IOAudio *)IOB);
}

void FreeIOB(struct IOAudio *IOB)
{
	if ( !IOB ) return;
	if ( IOB->ioa_Data ) FreeMem((UBYTE *)IOB->ioa_Data, bufsiz);
	IOB->ioa_Request.io_Command = ADCMD_FREE;
	BeginIO((struct IOAudio *)IOB);
	WaitIO((struct IOAudio *)IOB);
	DeletePort(IOB->ioa_Request.io_Message.mn_ReplyPort);
	CloseDevice((struct IOAudio *)IOB);
	FreeMem(IOB, sizeof(struct IOAudio));
	return;
}

void FreeCopy(struct IOAudio *IOB)
{
	if ( !IOB ) return;
	if ( IOB->ioa_Data ) FreeMem((UBYTE *)IOB->ioa_Data, bufsiz);
	FreeMem(IOB, sizeof(struct IOAudio));
	return;
}

ULONG LoadIOB(struct FileHandle *lfp, struct IOAudio *IOB, ULONG size, char *fn)
{
int	i;
UWORD	shword;

	if ( !size ) return(0L);
	if ( cvmode & CV_DOWN ) {
		if ( cvmode & CV_BEND ) {
			for(i=0; i<size; i++) {
				Read(lfp, &shword, 2);
				IOB->ioa_Data[i] = (UBYTE)(shword >> 8);
			}
			IOB->ioa_Length = size;
		}
		else if ( cvmode & CV_LEND ) {
			for(i=0; i<size; i++) {
				Read(lfp, &shword, 2);
				IOB->ioa_Data[i] = (UBYTE)(shword & 0xff);
			}
			IOB->ioa_Length = size;
		}
	}
	else IOB->ioa_Length = Read(lfp, IOB->ioa_Data, size);

	if ( cvmode & CV_ULAW ) {
		for(i=0; i<size; i++) IOB->ioa_Data[i] = logs[IOB->ioa_Data[i]];
	}
	if ( cvmode & CV_FLIP ) {
		for(i=0; i<size; i++) IOB->ioa_Data[i] ^= 0x80;
	}

	return(IOB->ioa_Length);
}

void clean(int code)
{
	SetTaskPri((struct Task *)FindTask(NULL), oldpri);
	if ( PPBase ) CloseLibrary((struct Library *)PPBase);
	exit(code);
}

void Usage(void)
{
	fprintf(stderr, "%s: Usage: %s [-b"COL3"bufsize"COL1"] [-c"COL3"l|r|s"
		COL1"] [-h] [-r"COL3"rate"COL1"] [-v"COL3"volume"COL1"] [-f"COL3"a|l|s|u"COL1"]\n",
		myname, myname);
	fprintf(stderr, "\t[-w"COL3"ticks"COL1"] [-s"COL3"0|1"COL1"] [-p"
		COL3"pri"COL1"] [-d"COL3"PAL|NTSC"COL1"] [-g"COL3"0|1"COL1"] [-m"COL3"0|1"COL1"]\n");
	fprintf(stderr, "\t[--"COL3"file"COL1"] "COL3"file"COL1" ["COL3"file"COL1" ["COL3"..."COL1"] ]\n\n");
	fprintf(stderr, "\t-b:\tsize of one audio buffer (there are two)\n");
	fprintf(stderr, "\t\tThe faster your input device, the smaller this should be.\n");
	fprintf(stderr, "\t-c:\tchannel: left, right, or stereo\n");
	fprintf(stderr, "\t-h:\thelp.  This stuff here.\n");
	fprintf(stderr, "\t-r:\toverride sound file's playback rate, in samples per second\n");
	fprintf(stderr, "\t-v:\toverride sound file's volume.  0 <= volume <= 64\n");
	fprintf(stderr, "\t-f:\tforce playback as Auto, u-Law, Signed raw, or Unsigned raw\n");
	fprintf(stderr, "\t-w:\tpause between sounds.  One tick is 1/50 second.\n");
	fprintf(stderr, "\t-p:\tset %s's task priority\n", myname);
	fprintf(stderr, "\t-s:\tshow (1) or don't show (0) optional properties.\n");
	fprintf(stderr, "\t-d:\toverride default electrical standard.\n");
	fprintf(stderr, "\t-g:\tgenerate log tables for ulaw files.\n");
	fprintf(stderr, "\t-m:\tshow maximum encoded value for ulaw files.\n");
	fprintf(stderr, "\t--:\tplay a file whose name begins with '-'.\n\n");
	fprintf(stderr, "Any argument can appear anywhere.  Options take effect as processed, after\n");
	fprintf(stderr, "any preceding files have been played.  You can use this to readjust OmniPlay's\n");
	fprintf(stderr, "settings on a per-sound basis.  To remove  rate or volume override, use\n");
	fprintf(stderr, "-r0 or -v0.\n\n");
	fprintf(stderr, "This program is copyright 1992 by Member Entities of the Web of Life.  See the\n");
	fprintf(stderr, "documentation for restrictions on duplication.\n");
	return;
}
