/* A sound card application interface 	*/

/* valid options:			 */
/* diagnostics, play, stop, mixerset,rec */
/*prints to stdout for accessablilty to TCL/Tk*/
/*By Paul D. Sharpe 21/11/94 */
/* revision: 3 : 27/2/95 */
/* Voxware driver v.2.4 */
/* This revision is necessary since the caard must be set upon playing  */
/* and recording, not separately					*/
/* Thus rec and play both need the four arguments filename, bitrate,	*/ 
/* channel, samplrate.							*/

#include <unistd.h>
#include <fcntl.h>
#include <sys/soundcard.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define DSP_NAME	"/dev/dsp"
#define MIX_NAME	"/dev/mixer"
#define MAX_BIT_RATE	16
#define MAX_CHANNELS	2
#define MAX_SAMP_RATE	44100
#define ST_SET		1
#define ST_GET		0

typedef int ST_CONFIG[3];

void diagnostics (void);
int *cardctl (int fd,ST_CONFIG parm,int st_flag);
int setcard (void);
int reccard (void);
int record (char *filename[]);
void equate_ST_CONFIG (ST_CONFIG a, ST_CONFIG b);
int play (char *filename[]);

int fd;

int main(int argc, char *argv[])
{
	ST_CONFIG new, curr;

	if (argc>1)  
	{
	  if (!strcmp(argv[1],"diagnostics") ) 
		{
			diagnostics();
			
		}
 
	  else if (!strcmp(argv[1],"play") ) 
		{
		if (argc<3) { fprintf (stderr,"%s play : Need filename",argv[0]); exit (-1);}
		if (!setcard()) {
			fprintf (stderr,"%s  : Sound Card unavailable",argv[0]);
			exit (-1);
		}
		if (argc==6) {
			new[0]=atoi(argv[3]);
			new[1]=atoi(argv[4]);
			new[2]=atoi(argv[5]);
			cardctl (fd,new,ST_SET);


		} 
 		/* For debug purposes */
		/*puts("Set done ");*/
		cardctl (fd,curr,ST_GET);
		/*printf ("%d %d %d",curr[0],curr[1],curr[2]);*/

		/* Play the Sample */
		play(argv[2]);	
		}

	  else if (!strcmp(argv[1],"rec") ) 
		{ 
		if (argc<3) { fprintf (stderr,"%s rec : Need filename",argv[0]); exit (-1);}
		if (!reccard()) {
			fprintf (stderr,"%s  : Sound Card unavailable",argv[0]);
			exit (-1);
		}
		if (argc==6) {
			new[0]=atoi(argv[3]);
			new[1]=atoi(argv[4]);
			new[2]=atoi(argv[5]);
		cardctl (fd,new,ST_SET);
/*puts ("here!");
printf ("%d %d %d",new[0],new[1],new[2]);*/

		}
		/* For debug purposes */
		/*puts("Set done ");
		cardctl (fd,curr,ST_GET);
		printf ("%d %d %d",curr[0],curr[1],curr[2]);*/
		record(argv[2]);
		}
	  else if (!strcmp(argv[1],"mixerset") ) 
		{
		printf("mixerset");
		}
	  }
	  else

		{
	 	printf ("%s:Too few arguments\n",argv[0]);
	 
		}

	return(0);
}

int record (char *filename[])
{
	char *buffer[256];
	int max_count = 2000; /*just a limit for now*/
	int ffd,next_count,l;

	if ((ffd=creat((char *)filename,0x1B4))==-1) 
		{
			fprintf(stderr,"File create failed");
			exit(-1);
		}
	next_count = sizeof(buffer);

	while ((next_count > 0) && (l=read(fd,buffer,next_count))>0)
	{
		if (write(ffd,buffer,l)==-1) 
			{
				fprintf(stderr,"Can't Write to File");
				exit(-1);
			}
		max_count -=1;
		next_count = max_count; /* # of bytes left */
	
	if (next_count > sizeof (buffer))
			next_count = sizeof(buffer);
	}
	close (ffd);
	return(0);
}

int play (char *filename[])
{
	char *buffer[256];
	int ffd,next_count,l;

	if ((ffd=open((char *)filename,O_RDONLY,0))==-1) 
		{
			fprintf(stderr,"file open failed");
			exit(-1);
		}
	next_count = sizeof(buffer);

	while ((next_count > 0) && (l=read(ffd,buffer,next_count))>0)
	{
		if (write(fd,buffer,l)==-1) 
			{
				fprintf(stderr,"Can't Write to Card");
				exit(-1);
			}
	
	if (l < next_count) next_count = 0;
	}
	close (ffd);
	return(0);
}


/*void diagnostics (void)*/
/*checks for bit rate capability	 	*/
/*	     sampling rate range 		*/
/*	     mono/stereo capability	 	*/
/*		mixing facilities 		*/
/* writes to stdout in following format:	*/
/* { BR1 BR2 BR3 } { CH1 CH2 } { CH1_SR_MIN CH1_SR_MAX } { CH2_SR_MIN CH2_SR_MAX} */
/* {valid bitrates} {channels} { sample rate ranges for channels		*/

void diagnostics (void)
{
	
	int i;
	int stflag=0;
	ST_CONFIG orig,parm;
	int *new;
		
	if (!reccard()) 
		{
			fprintf(stderr,"open failed");
			exit(-2);
		}
	
	cardctl(fd,orig,ST_GET); 	/*store original settings, in orig */
	equate_ST_CONFIG(parm,orig);

	printf("\n{ ");
	/*check for bit capability */
	
	for (i=8;i<=MAX_BIT_RATE;i+=8)
	  {	
		parm[0]=i;
		cardctl(fd,parm,ST_SET);
		if (parm[0]==i) printf("%d ",i);
		
	   }
	printf(" } { ");

	/*check for number of channels*/
	for (i=1;i<=MAX_CHANNELS;i++)
	  {			
		parm[1]=i;
	        cardctl(fd,parm,ST_SET);
		if (parm[1]==i)  printf("%d ",parm[1]);
		if (parm[1]==2) stflag=1;
	   }
	printf("} { ");
	



	/*check for sample rate range */
		
	/*  mono */
	/*minimum rate*/
	parm[1]=1;
	parm[2]=0;		/*Set Rate to zero */ 
	cardctl(fd,parm,ST_SET);
	printf("%d ",parm[2]);
	
	/*maximum rate*/
	parm[2]=MAX_SAMP_RATE;	/*Set Rate to highest known sampling rate */ 
	cardctl(fd,parm,ST_SET);
	printf("%d ",parm[2]);

	
	/*stereo (if flag is set)*/
	printf(" } { ");	
	if (stflag) 
	{	
	/*minimum rate*/
	parm[1]=2;
	parm[2]=0;		/*Set Rate to zero */ 
	cardctl(fd,parm,ST_SET);
	printf("%d ",parm[2]);
	
	/*maximum rate*/
	parm[2]=MAX_SAMP_RATE;	/*Set Rate to highest known sampling rate */ 
	cardctl(fd,parm,ST_SET);
	printf("%d ",parm[2]);
	} else printf ("0 0");
	

	printf(" }\n");

/*	
	ioctl(fd,SOUND_PCM_RESET,0);
	ioctl(fd,SOUND_PCM_WRITE_BITS,&parm);
	printf("Current setting: %d",parm);
	ioctl(fd,SOUND_PCM_WRITE_CHANNELS,&parm);
	printf(" %d ",parm);
	ioctl(fd,SOUND_PCM_WRITE_RATE,&parm);
	printf("%d\n",parm);
*/
	cardctl(fd,orig,1);
return;
}
int reccard (void)

{

	
	
		if ((fd=open(DSP_NAME,O_RDONLY,0))==-1) 
		 {
			/*fprintf(stderr,"open failed");*/
			return(0);
		 }
	 
return (1);
	
}	

int setcard (void)

{

	
	
		if ((fd=open(DSP_NAME,O_WRONLY,0))==-1) 
		 {
			/*printf("open failed");*/
			return(0);
		 }
	 
return (1);
	
	
}
/* int *cardctl(int fd,ST_CONFIG parm, int set)				*/
/*cardctl reads the current card configuration if st_flag =ST_GET	*/
/*	  sets   the card configuration	       if st_flag= ST_SET       */
/*	information is passed in integer array of 3			*/
/*	parm[1]= Bit Resolution, 					*/
/*	parm[2]= channels(1 or 2), 					*/
/*	parm[3]= Sampling Rate.						*/

/* RETURNS a an array containing the new seetings of the card		*/
/* NOTE :the card must already be open for reading or writing!		*/
	
int *cardctl (int fd,ST_CONFIG parm,int st_flag)
{	
	ST_CONFIG temp;
	int i,error;


	if (st_flag) 
	 {

	/*printf ("\nbefore %d -",parm[0]);
	printf ("%d -",parm[1]);
	printf ("%d -\n",parm[2]);*/
	
		if (ioctl(fd,SOUND_PCM_WRITE_BITS,&parm[0])==-1) {puts ("BIt fail"); }	
		if (ioctl(fd,SOUND_PCM_WRITE_CHANNELS,&parm[1])==-1) {puts ("ch fail");}
		if (ioctl(fd,SOUND_PCM_WRITE_RATE,&parm[2])==-1) {puts ("rate fail");}
	 }
	else
	 {
		 
	error=ioctl(fd,SOUND_PCM_READ_BITS,&parm[0]);	
	/*printf("err:%d\n",error);*/
	ioctl(fd,SOUND_PCM_READ_CHANNELS,&parm[1]);
	ioctl(fd,SOUND_PCM_READ_RATE,&parm[2]);
		/*equate_ST_CONFIG( parm,temp);*/
	 }
	 
	/* printf ("\n after: ");
	printf ("%d -",parm[0]);
	printf ("%d -",parm[1]);
	printf ("%d -\n",parm[2]);*/
	
	return(temp);
}

/*equates arrays a=b */
void equate_ST_CONFIG (ST_CONFIG a, ST_CONFIG b)
{
	int i;
	for (i=0;i<3;i++) a[i]=b[i];
}
