/* this file is a part of amp software, (C) tomislav uzelac 1996,1997
*/

/* audio.c  main amp source file 
 *
 * Created by: tomislav uzelac  Apr 1996 
 * Karl Anders Oygard added the IRIX code, 10 Mar 1997.
 * Ilkka Karvinen fixed /dev/dsp initialization, 11 Mar 1997.
 * Lutz Vieweg added the HP/UX code, 14 Mar 1997.
 */ 
#include <unistd.h>

#ifdef A_SIF_LINUX
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <linux/soundcard.h>
#endif /* A_SIF_LINUX */

#ifdef A_SIF_ULTRA
#include <sys/types.h>
#include <sys/stropts.h>
#include <fcntl.h>
#include <sys/audioio.h>
#endif /* A_SIF_ULTRA */

#ifdef A_SIF_HPUX
#include <sys/audio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#endif /* A_SIF_HPUX */

#define AUDIO
#include "audio.h"
#include "formats.h"
#include "getbits.h"
#include "huffman.h"
#include "layer3.h"

int main(int argc,char **argv)
{
struct AUDIO_HEADER header;
int cnt=0,g;

/* process command line arguments
*/
	args(argc,argv);
 
/* print a warm, friendly message
*/
	if (!A_SUPRESS_STDOUT) {
		printf(" amp %d.%d.%d, (C) Tomislav Uzelac 1996,1997\n",MAJOR,MINOR,PATCH);
		printf(" THIS PROGRAM COMES WITH ABSOLUTELY NO WARRANTY\n");
		printf(" PLEASE READ THE DOCUMENTATION FOR DETAILS\n\n");
	}

/* initialize globals
*/
	append=data=nch=0;

/* make room for the wave header if needed
*/
	if (A_FORMAT_WAVE) wav_begin();

/* _the_ loop
 */
	for (cnt=0;;cnt++) {
		/* getbits funcs expect first four bytes after BUFFER_SIZE have same contents
		 * as the first four bytes of the buffer. fillbfr() normally takes care of
		 * this when it hits the end of the buffer, but not this first time
		 */
		if (cnt==1) memcpy(&buffer[BUFFER_SIZE],buffer,4); 

		if ((g=gethdr(&header))!=0) {
		int exit_status;
			if (g != GETHDR_ERR) {
				if (!A_SUPRESS_STDOUT) {
					printf(" this is a file in MPEG 2.5 format, which is not defined\n");
					printf(" by ISO/MPEG. It is \"a special Fraunhofer format\".\n");
					printf(" amp does not support this format. sorry.\n");
				}
				exit_status=0;
			} else {
			/* here we should check whether it was an EOF or a sync err. later.
			*/      
				if (A_FORMAT_WAVE) wav_end(&header);
				if (!A_SUPRESS_STDOUT) {
					printf(" thank you for using amp!\n");
				}

				/* this is the message we'll print once we check for sync errs
				 * printf(" amp encountered a synchronization error\n");
				 * printf(" sorry.\n");
				 */
				exit_status=0;
			}
			exit(exit_status);
		} 

	/* crc is read just to get out of the way.
	 */
		if (header.protection_bit==0) getcrc();
			
		if (A_SHOW_CNT /* && !(cnt%10)*/ ) {
			printf("\r {%d} ",cnt);
			fflush(stdout);
		}

		if (!cnt && A_WRITE_TO_AUDIO) {
#ifdef A_SIF_LINUX
		int play_format = AFMT_S16_LE; /* Signed, intel little endian 16bit */
		int play_stereo;
		int play_sample_rate=t_sampling_frequency[header.ID][header.sampling_frequency];
			if (header.mode==3) play_stereo=0;
			else play_stereo=1;
			if ( ioctl(audio_fd, SNDCTL_DSP_SETFMT,&play_format) < 0 ||
			     ioctl(audio_fd, SNDCTL_DSP_STEREO, &play_stereo) < 0 ||
			     ioctl(audio_fd, SNDCTL_DSP_SPEED, &play_sample_rate) < 0) {
				if (!A_SUPRESS_STDOUT) printf(" could not open /dev/dsp. exiting\n");
				exit(-1);
			}
#endif /* A_SIF_LINUX */
#ifdef A_SIF_HPUX
		int channels;
		int failed = 0;
		int srate = t_sampling_frequency[header.ID][header.sampling_frequency];
			if (header.mode==3) channels=1;
			else channels=2;
			if ( ioctl(audio_fd, AUDIO_SET_DATA_FORMAT, AUDIO_FORMAT_LINEAR16BIT) < 0 ||
			     ioctl(audio_fd, AUDIO_SET_CHANNELS, channels) < 0 ||
			     ioctl(audio_fd, AUDIO_SET_OUTPUT, AUDIO_OUT_SPEAKER | AUDIO_OUT_HEADPHONE | AUDIO_OUT_LINE) < 0 ||
			     ioctl(audio_fd, AUDIO_SET_SAMPLE_RATE, srate) < 0) {
				failed = -1;    
			}
			{
				struct audio_describe description;
				struct audio_gains gains;
				float volume = 1.0;
				if (ioctl(audio_fd, AUDIO_DESCRIBE, &description)) {
					failed = -1;
				}
				if (ioctl (audio_fd, AUDIO_GET_GAINS, &gains)) {
					failed = -1;
				}

				gains.transmit_gain = (int)((float)description.min_transmit_gain +
						  (float)(description.max_transmit_gain
							  - description.min_transmit_gain)
							  * volume);
				if (ioctl (audio_fd, AUDIO_SET_GAINS, &gains)) {
					failed = -1;
				}
			}

			if (ioctl(audio_fd, AUDIO_SET_TXBUFSIZE, 4096 * 8)) {
				failed = -1;
			}
			if (failed) {
				if (!A_SUPRESS_STDOUT) printf(" unable to setup /dev/audio\n");
				exit(-1);
			}
			
#endif /* A_SIF_LINUX */
#ifdef A_SIF_IRIX
		ALconfig audioconfig;
		audioconfig = ALnewconfig();
 
		if (!audioconfig) {
			if (!A_SUPRESS_STDOUT) fprintf(stderr, "out of memory\n");
			exit(-1);
		} else {
			long pvbuf[] = { AL_OUTPUT_COUNT, 0, AL_MONITOR_CTL, 0, AL_OUTPUT_RATE, 0};
 
			if (ALgetparams(AL_DEFAULT_DEVICE, pvbuf, 6) < 0)
				if (oserror() == AL_BAD_DEVICE_ACCESS) {
					if (!A_SUPRESS_STDOUT) fprintf(stderr, "couldn't access audio device\n");
					exit(-1);
				}
 
			if (pvbuf[1] == 0 && pvbuf[3] == AL_MONITOR_OFF) {
				long al_params[] = { AL_OUTPUT_RATE, 0};
 
				al_params[1] = t_sampling_frequency[header.ID][header.sampling_frequency];
				ALsetparams(AL_DEFAULT_DEVICE, al_params, 2);
			} else
				if (!A_SUPRESS_STDOUT &&
				    pvbuf[5] != t_sampling_frequency[header.ID][header.sampling_frequency])
					fprintf(stderr, "audio device is already in use with wrong sample output rate\n");
 
			/* ALsetsampfmt(audioconfig, AL_SAMPFMT_TWOSCOMP); this is the default */
			/* ALsetwidth(audioconfig, AL_SAMPLE_16); this is the default */
 
			if (header.mode == 3) ALsetchannels(audioconfig, AL_MONO);
			/* else ALsetchannels(audioconfig, AL_STEREO); this is the default */
 
			audioport = ALopenport("amp", "w", audioconfig);
			if (audioport == (ALport) 0) {
				if (!A_SUPRESS_STDOUT)
					switch (oserror()) {
					case AL_BAD_NO_PORTS:
						fprintf(stderr, "system is out of ports\n");
						break;
 
					case AL_BAD_DEVICE_ACCESS:
						fprintf(stderr, "couldn't access audio device\n");
						break;
 
					case AL_BAD_OUT_OF_MEM:
						fprintf(stderr, "out of memory\n");
						break;
					}
 
				exit(-1);
			}
		}
#endif /* A_SIF_IRIX */
#ifdef A_SIF_ULTRA
		audio_info_t auinfo;
			printf("GI=%d\n",ioctl(audio_fd,AUDIO_GETINFO,&auinfo));
			auinfo.play.sample_rate=t_sampling_frequency[header.ID][header.sampling_frequency];
			if (header.mode==3) auinfo.play.channels=1;
			else auinfo.play.channels=2;
			auinfo.play.precision=16;
			auinfo.play.encoding=AUDIO_ENCODING_LINEAR;
			auinfo.play.gain=128;
			printf("SI=%d\n",ioctl(audio_fd,AUDIO_SETINFO,&auinfo));
#endif /* A_SIF_ULTRA */
		}
			
		if (layer3_frame(&header,cnt)) {
			if (!A_SUPRESS_STDOUT) printf(" error. blip.\n");
			exit(-1);
		} 
	}
}

static inline void args(int argc,char **argv)
{
/* if the number of these variables gets out of hand, convert them to flags
 * variables without A_ prepended don't have an option letter assigned, but
 * you could do so within this func. (they work otherwise)
 */
	SHOW_HEADER=SHOW_HEADER_DETAIL=FALSE;
	SHOW_SIDE_INFO=SHOW_SIDE_INFO_DETAIL=FALSE;
	SHOW_MDB=SHOW_MDB_DETAIL=FALSE;
	SHOW_HUFFMAN_ERRORS=FALSE;
	SHOW_HUFFBITS=FALSE;
	SHOW_SCFSI=SHOW_BLOCK_TYPE=SHOW_TABLES=SHOW_BITRATE=FALSE;

	A_DUMP_BINARY=FALSE;
	A_SUPRESS_STDOUT=FALSE;
	A_FORMAT_WAVE=FALSE;
	A_SHOW_CNT=FALSE;
	A_WRITE_TO_FILE=TRUE;
	A_WRITE_TO_AUDIO=FALSE;
#ifdef MONO
	AMIGA_MONO=FALSE;
#endif /* MONO */
#ifdef FREQDIV
	AMIGA_FREQDIV=FALSE;
	AMIGA_FDIV=1;
	AMIGA_FSIZE=32;
#endif FREQDIV

	if (argc >= 2) {
		while (1) {
#ifdef MONO
			int c=getopt(argc,argv,"scwdpm24");
#else
			int c=getopt(argc,argv,"scwdp");
#endif /* MONO */
			if (c==-1) break;
			switch (c) {
				case 's': A_SUPRESS_STDOUT=TRUE; break;
				case 'c': A_SHOW_CNT=TRUE; break;
				case 'w': A_FORMAT_WAVE=TRUE; break;
				case 'd': A_DUMP_BINARY=TRUE; break;
				case 'p': A_WRITE_TO_AUDIO=TRUE;A_WRITE_TO_FILE=FALSE;break;
#ifdef MONO
				case 'm': AMIGA_MONO=TRUE; break;
#endif
#ifdef FREQDIV
				case '2':
				    if (!AMIGA_MONO) {
					fprintf(stderr,"freqdiv only with mono\n");
					exit(1);
					}
				    AMIGA_FDIV=2;
				    AMIGA_FSIZE=16;
				    AMIGA_FREQDIV=TRUE;
				    break;
				case '4':
				    if (!AMIGA_MONO) {
					fprintf(stderr,"freqdiv only with mono\n");
					exit (1);
					}
				    AMIGA_FDIV=4;
				    AMIGA_FSIZE=8;
				    AMIGA_FREQDIV=TRUE;
				    break;
#endif
				case '?': printf(" unrecognized option -%c\n",c);
			}
		}
		if ((in_file=fopen(argv[optind],"r"))==NULL) {
			printf("error opening infile, optind=%d arg=%s",optind,argv[optind]);
			exit(-1);
		}
		if (A_WRITE_TO_FILE) if ((out_file=fopen(argv[optind+1],"w"))==NULL) {
			printf("error opening outfile, optind=%d arg=%s",optind,argv[optind+1]);
			exit(-1);
		}
		if (A_WRITE_TO_AUDIO) {
#ifdef A_SIF_LINUX
			if ((audio_fd = open ("/dev/dsp", O_WRONLY, 0)) == -1) {
				printf(" unable to open the audio device\n");
				exit(-1);
			}
#endif /* A_SIF_LINUX */
#ifdef A_SIF_ULTRA
			if ((audio_fd = open("/dev/audio",O_RDWR))==-1) {
				printf(" unable to open the audio device\n");
				exit(-1);
			}
#endif /* A_SIF_ULTRA */
#ifdef A_SIF_HPUX
			if ((audio_fd = open("/dev/audio",O_RDWR))==-1) {
				printf(" unable to open the audio device\n");
				exit(-1);
			}
#endif /* A_SIF_HPUX */
			}
	} else {
		printf(" usage: amp [options] [ MPEG audio bitstream ] [ wave file ]\n");
		printf(" -s     supress writing to stdout\n");
		printf(" -c     display a frame counter\n");
		printf(" -w     write a wave file\n");
		printf(" -d     dump binary data to stdout\n");
		printf(" -p     play the file directly\n");
#ifdef MONO
		printf(" -m     mono (amiga) \n");
#endif
#ifdef FREQDIV
		printf(" -2     freq div by 2 (amiga)\n");
		printf(" -4     freq div by 4 (amiga)\n");
#endif
		printf("\n");
		exit (0);
	}       
}
