
#define UNIXFILES
#include <sys/file.h>
#include <sys/types.h>
#include <sys/time.h>
#include <signal.h>
#include <stdio.h>
#include <vaxuba/dsc.h>
#include <vaxuba/dsreg.h>
#include <sys/stat.h>
#include "sfheader.h"
#include <errno.h>
#include "virgil.h"

short fid;
float tick;
#define FUDGE 0 /* guess at how long it takes to read 64k bytes in */
float tval = FUDGE;  /* fudge to allow time to read in buffers */

main(argc,argv)
int argc;
char *argv[];

{
	char *buffer,*cp,*sfname;
	long nsamps,blksize,originalsize,sf,result,sfd,i,nskips,chans;
	int status,shutup(),pid,(*oldint)(),(*oldquit)(),rtn,w,errno;
	int loadword(), dmaword;
	float srate,dur,pdur,inskip;
	double atof();
	SFHEADER sfh;
	struct stat sfst;
	
	srate=dur=pdur=inskip=chans=tick=0;

	if(argc == 1) {
		printf("usage: play [-s skip] [-d dur] [-r srate] [-c nchans] [-b beats] file_name\ndefaults:s=0, dur=all, beats=0, r&c read from header\n");
		exit(1);
	}
	while((*++argv)[0] == '-') {
		argc -= 2; /* Take away two args */
		for(cp = argv[0]+1; *cp; cp++) {
			switch(*cp) { /* Grap options */
			case 'r': 
				srate = atof(*++argv);
				printf("Sampling rate reset to %f\n",srate);
				break;
			case 'd': 
				pdur = atof(*++argv);
				printf("Play duration is %f\n",pdur);
				break;
			case 's': 
				inskip = atof(*++argv);
				tval += inskip;
				printf("Input skip is %f\n",inskip);	
				break;
			case 'c':
				chans = atoi(*++argv);
				printf("Number of channels reset to %d\n",
								chans);
				break;
			case 'b':
				tick = atof(*++argv);
				printf("Beat turned on every %f seconds\n",tick);
				break;
			default:  
				printf("Don't know about option: %c\n",*cp);
			}
		}

	}
	while(argc-- > 1) {
        sfname = *argv++;
	readopensf(sfname,sfd,sfh,sfst,"play",result)
	if(result < 0) exit(0);
	printf("name: %s   sr: %f   nchans: %d  class: %d\n",sfname,
                sfsrate(&sfh),sfchans(&sfh),sfclass(&sfh));
	srate = (srate ? srate : sfsrate(&sfh));
	chans = (chans ? chans : sfchans(&sfh));
	originalsize = ((sfst.st_size - SIZEOF_HEADER)/SF_SHORT)/chans;
	dur = pdur ? pdur : ((float)originalsize/srate) - inskip;
	printf("play duration = %f\n",dur);

	if((inskip + dur) > (.01 + originalsize/srate)) {
		printf("The file is not that big. Duration = %f\n",dur);
		exit(1);
	}
				
	nsamps=dur * srate * chans;

	nskips = inskip * srate * (float)chans * SF_SHORT;
	nskips -= nskips % (chans * SF_SHORT);
	if(sflseek(sfd,nskips,0) == -1) {
		printf("Bad lseek on soundfile\n");
		exit(1);
	}

	blksize=sfst.st_blksize;
	if(sfclass(&sfh) != 2) {
	       printf("You can't listen to this file, not made of integers.\n");
	       exit(1);
	}
	if(tick) startclock(tick);
	if((int)srate == 14000) chans *= 8; /* uses DACs 3&4 for 14k */
	fflush(stdout);
	if ((pid = fork()) == 0) {
		(void) signal(SIGINT,SIG_IGN);
		(void) signal(SIGQUIT,SIG_IGN);
		(void) signal(SIGTSTP,SIG_IGN);
		(void) signal(SIGSTOP,SIG_IGN);
		if((dmaword = loadword(srate,chans,DTOA)) < 0) {
			perror("loadword");
			exit(1);
		}

		i=dsdac42(sfd,nsamps,blksize,dmaword);  
		exit(i);
	}
	if(tick) stopclock();
	oldint = signal(SIGINT, shutup);
	oldquit = signal(SIGQUIT, shutup);
	/* wait for process, or interrupts */
	while ((w = wait(&status)) != pid) {
		if (w == -1) {
			if (errno == EINTR)
				continue;
			break;
		}
	}
	signal(SIGINT,oldint);
	signal(SIGQUIT,oldquit);
	close(sfd);
	if(tick) tval = FUDGE + inskip;  /* reset for next file */
	}
}

shutup() 
{
	int fid;
	if((fid = open("/dev/drreset0",0)) < 0) 
		perror("shutup"); 
}

extern  errno;
/* how many buffers */
# define UBSIZE		4
# define BP16BIT        2

short *dsbuf;

char *ddserrs[] = {
	"missing arguments/parameters",
	"converters in use",
	"buffer size wasn't modulo bsize",
	"buffer size was too small",
	"disk error",
	"converter error",
	"dsreset clobbered us",
	"sync time out",
	""
};


dsdac42(sfd,  nsamps, blksize, dmaword) 
	int sfd;
	long	nsamps; /* number of samples to convert */
	long	blksize; /* File system block size */
{
	struct ds_seq dsseq;
	struct ds_err dserr;
	struct ds_fs dsf;
	register int dacfid, cnt=0, erno;
	short j;

		/*
		printf("DSCOUNT=%D ",
			 nsamps * BP16BIT);
		 */

	if (nsamps < 1) {
	 fprintf(stderr, "dsdac: can't convert 0 samples!\n"); 
		return(-1);
	}


	/* ds driver will use this buffer for sample data transfer */
	if (dsbuf == NULL)
		if ((dsbuf = (short *) valloc((unsigned) 
		    blksize * UBSIZE)) == NULL) {
			perror("valloc");
			return(1);
		}
		while ((dacfid = open("/dev/dr", 2)) < 0) 
			if (errno != ENXIO) {
				perror("dsdac:open");
				return(-1);
			}

		dsf.bnosiz = sfd;
		if (ioctl(dacfid, DS42BSD, &dsf) == -1) 
			{ perror("dsblks"); goto errout; }
		dsf.bnosiz = dmaword;
		/* just to set parameters for my board */
		if (ioctl(dacfid, DSWORD, &dsf) == -1)
			{ perror("dsword"); goto errout; }
		if (write(dacfid, (char *) dsbuf, nsamps * BP16BIT) == -1) {
			perror("write");
			if (ioctl(dacfid, DSERRS, &dserr) == -1) 
				{ perror("dserrs"); goto errout; }
			switch (dserr.errors) {
				case EDS_ARGS : erno = 0; break;
				case EDS_ACC : erno = 1; break;
				case EDS_MOD : erno = 2; break;
				case EDS_SIZE : erno = 3; break;
				case EDS_DISK : erno = 4; break;
				case EDS_CERR : erno = 5; break;
				case EDS_RST : erno = 6; break;
				default: erno = 8; break;
			}
		fprintf(stderr, "%d %s dmacsr=%o, asccsr=%o, errors=%o\n",erno,  
				ddserrs[erno], 
					dserr.dma_csr & ((u_short) -1), 
					dserr.asc_csr & ((u_short) -1),
					dserr.errors & ((u_short) -1));
		    puts("E_DACS");
		    goto errout;
		}
		if(close(dacfid))
			printf("Bad close\n");
	free((char *) dsbuf);
	puts("!");
	return(0);

errout:
	free((char *) dsbuf);
	close(dacfid);
	return(1);
}

struct itimerval *value;

startclock(tick)
float tick;
{
	int telltime(),msecs;
	char *malloc();
	
	value = (struct itimerval *)malloc(sizeof(value));
	value->it_value.tv_sec = value->it_interval.tv_sec = (int)tick;
	msecs = (int)(1000000. * (tick - (float)value->it_value.tv_sec));
	value->it_value.tv_usec = value->it_interval.tv_usec = msecs;
	(void) signal(SIGALRM,telltime);
	setitimer(ITIMER_REAL,value,value);
}
telltime()
{ 
	static int position = 0;
	if(position++  < 8 ) printf(" %5.3f",tval);
	else {printf(" %5.3f\n",tval); position = 0; }
	fflush(stdout);
	tval += tick;
}
stopclock()
{
	value->it_value.tv_sec = value->it_interval.tv_sec = 0;
}

#include "virgil.h"
loadword(opsrate,opchans,function)
float opsrate;
{ 
	int val = opsrate;
	int word;
	switch(val) {
	case 7000 : 
		word = CLOCK56 | DIV8; 
		break;
	case 7500 : 
		word = CLOCK60 | DIV8; 
		break;
	case 14000 : 
		word = CLOCK56 | DIV4; 
		break;
	case 15000 :
		word = CLOCK60 | DIV4;  
		break;
	case 28000 :
		word = CLOCK56 | DIV2 ; 
		break;
	case 30000 : 
		word = CLOCK60 | DIV2 ; 
		break;
	case 56000 :
		word = CLOCK56 | DIV1 ;
		break;
	case 1 : 
		word = PULSE; 
		printf(" Use pulse generator.  Make sure it is on.\n");
		break;
	default : 
		printf(" Not one of my available sampling rates, tryagain.");
		return(-1);
	}
	switch(opchans) {
	case 1 :
		word |= CHAN1; 
		break;
	case 2 :
		word |= CHAN1 | CHAN2 ;
		break;
	case 4 :
		word |= CHAN1 | CHAN2 | CHAN3 | CHAN4 ;
		if(function == ATOD) {
			printf("You cannot do A-D in 4 channels\n");
			return(-1);
		}
		break;
	case 8 :
		word |= CHAN3 ;  	/* to use for 14k conversion */
		break;
	case 16 :
		word |= CHAN3 | CHAN4 ; /* to use for 14k stereo conversion */
		break;
	default :
		printf( " Bad channel specification \n");
		return(-1);
	}
	return(word | (function == ATOD ? function | IOLINK : function));
}
