/* 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.
 * Dan Nelson added FreeBSD modifications, 23 Mar 1997.
 * Andrew Richards complete reorganisation, new features, 25 Mar 1997
 * Edouard Lafargue added sajber jukebox support, 12 May 1997
 */ 


#include "amp.h"

#include <sys/types.h>
#include <sys/stat.h>

#ifndef __BEOS__
//#include <sys/uio.h>
#endif

#include <sys/socket.h>
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define AUDIO
#include "audio.h"
#include "formats.h"
#include "getbits.h"
#include "huffman.h"
#include "layer2.h"
#include "layer3.h"
#include "position.h"
#include "rtbuf.h"
#include "transform.h"
#include "controldata.h"
//#include "guicontrol.h"

#ifndef __BEOS__
typedef int bool;
#endif

void statusDisplay(struct AUDIO_HEADER *header, int frameNo)
{
        int minutes,seconds;

        if ((A_SHOW_CNT || A_SHOW_TIME) && !(frameNo%10))
                msg("\r");
        if (A_SHOW_CNT && !(frameNo%10) ) {
                msg("{ %d } ",frameNo);
        }
        if (A_SHOW_TIME && !(frameNo%10)) {
                seconds=frameNo*1152/t_sampling_frequency[header->ID][header->sampling_frequency];
                minutes=seconds/60;
                seconds=seconds % 60;
                msg("[%d:%02d]",minutes,seconds);
        }
        if (A_SHOW_CNT || A_SHOW_TIME)
                fflush(stderr);
}

int decodeMPEG(void)
{
struct AUDIO_HEADER header;
int cnt,g,snd_eof;
uid_t my_uid = getuid();

#ifdef LINUX_REALTIME
        set_realtime_priority();

        setreuid(my_uid, my_uid);

        prefetch_initial_fill();
#endif /* LINUX_REALTIME */  

        initialise_globals();

#ifndef LINUX_REALTIME
        if (A_FORMAT_WAVE) wav_begin();
#endif /* LINUX_REALTIME */
        
        if ((g=gethdr(&header))!=0) {
                report_header_error(g);
                return -1;
        }

        if (header.protection_bit==0) getcrc();

#ifdef LINUX_REALTIME
        if (setup_fancy_audio(&header)!=0) {
                warn("Cannot set up direct-to-DMA audio. Exiting\n");
                return -1;
        }
#else
        if (setup_audio(&header)!=0) {
                warn("Cannot set up audio. Exiting\n");
                return -1;
        }
#endif /* LINUX_REALTIME */

        
        show_header(&header);

        if (header.layer==1) {
                if (layer3_frame(&header,cnt)) {
                        warn(" error. blip.\n");
                        return -1;
                }
        } else if (header.layer==2)
                if (layer2_frame(&header,cnt)) {
                        warn(" error. blip.\n");
                        return -1;
                }

#ifdef LINUX_REALTIME
        if (start_fancy_audio(&header)!=0) {
                warn("Cannot start direct-to-DMA audio. Exiting\n");
                return -1;
        }
#endif /* LINUX_REALTIME */


        /*
         * decoder loop **********************************
         */
        snd_eof=0;
        cnt=0;
        while (!snd_eof) {
                while (!snd_eof && ready_audio()) {
                        if ((g=gethdr(&header))!=0) {
                                report_header_error(g);
#ifdef LINUX_REALTIME
                                cleanup_fancy_audio();
#else
                                if (g==GETHDR_EOF && A_FORMAT_WAVE) wav_end(&header);
#endif /* LINUX_REALTIME */
                                snd_eof=1;
                                break;
                        }

                        if (header.protection_bit==0) getcrc();

                        statusDisplay(&header,cnt);     

                        if (header.layer==1) {
                                if (layer3_frame(&header,cnt)) {
                                        warn(" error. blip.\n");
                                        return -1;
                                }
                        } else if (header.layer==2)
                                if (layer2_frame(&header,cnt)) {
                                        warn(" error. blip.\n");
                                        return -1;
                                }
                        cnt++;
                }
#ifdef LINUX_REALTIME
                if (block_fancy_audio(snd_eof)!=0) {
                        warn("Problems with direct-to-DMA audio\n");
                        return -1;
                }
#endif
        }
#ifdef LINUX_REALTIME
        if (stop_fancy_audio()!=0) {
                warn("Cannot stop direct-to-DMA audio. Exiting\n");
                return -1;
        }
#endif
        return 0;
}

int main(int argc,char **argv)
{
int argPos;

        argPos=args(argc,argv); /* process command line arguments */

        initialise_decoder();   /* initialise decoder */

        if (argc == 1) {        /* Start amp as a GUI backend. */
                A_QUIET = TRUE; /* TODO: change this to A_GUI_CONTROLLED */
#ifndef OS_SunOS
                gui_control();
#endif
        } else {
                if (A_AUDIO_PLAY) {     /* play each specified file */
                        if (argPos<argc)
                                for(;argPos<argc;argPos++) {
#ifdef LINUX_REALTIME
                                if (geteuid() != 0) 
                                        die("effective UID not root, cannot use realtime buffering\n");
                                rt_play(argv[argPos]);
#else /* LINUX_REALTIME */
                                play(argv[argPos],0);
#endif /* LINUX_REALTIME */
                                }
                        else
                                displayUsage();
                } else {                /* convert the file to some format */
                        if ((argPos+2)==argc)
                                play(argv[argPos],argv[argPos+1]);
                        else {
                                warn("Invalid number of parameters\n");
                                displayUsage();
                                die("");
                        }
        }

        msg("\nThank you for using amp!\n");
        exit(0);
    }
  exit(0);
}

/* call this once at the beginning
 */
void initialise_decoder(void)
{
        premultiply();
        imdct_init();
        calculate_t43();
}

/* call this before each file is played
 */
void initialise_globals(void)
{
        append=data=nch=0; 
        f_bdirty=TRUE;
        bclean_bytes=0;

        memset(s,0,sizeof s);
        memset(res,0,sizeof res);
}

void report_header_error(int err)
{
        switch (err) {
                case GETHDR_ERR: die("error reading mpeg bitstream. exiting.\n");
                                        break;
                case GETHDR_NS : warn("this is a file in MPEG 2.5 format, which is not defined\n");
                                 warn("by ISO/MPEG. It is \"a special Fraunhofer format\".\n");
                                 warn("amp does not support this format. sorry.\n");
                                        break;
                case GETHDR_FL1: warn("ISO/MPEG layer 1 is not supported by amp (yet).\n");
                                        break;
                case GETHDR_FF : warn("free format bitstreams are not supported. sorry.\n");
                                        break;  
                case GETHDR_SYN: warn("oops, we're out of sync.\n");
                                        break;
                case GETHDR_EOF: 
                default:                ; /* some stupid compilers need the semicolon */
        }       
}

/* TODO: there must be a check here to see if the audio device has been opened
 * successfuly. This is a bitch because it requires all 6 or 7 OS-specific functions
 * to be changed. Is anyone willing to do this at all???
 */
int setup_audio(struct AUDIO_HEADER *header)
{
        if (A_AUDIO_PLAY)  
                if (AUDIO_BUFFER_SIZE==0)
                        audioOpen(t_sampling_frequency[header->ID][header->sampling_frequency],
                                        (header->mode!=3 && !A_DOWNMIX),A_SET_VOLUME);
                else
                        audioBufferOpen(t_sampling_frequency[header->ID][header->sampling_frequency],
                                        (header->mode!=3 && !A_DOWNMIX),A_SET_VOLUME);
        return 0;
}

void close_audio(void)
{
        if (A_AUDIO_PLAY)
                if (AUDIO_BUFFER_SIZE!=0)
                        audioBufferClose();
                else
                        audioClose();
}

int ready_audio(void)
{
#ifdef LINUX_REALTIME
        return ready_fancy_audio();
#else
        return 1;
#endif
}

/* TODO: add some kind of error reporting here
 */
void play(char *inFileStr, char *outFileStr)
{
        if (strcmp(inFileStr,"-")==0)
                in_file=stdin;
        else {
                if ((in_file=fopen(inFileStr,"r"))==NULL) {
                        warn("Could not open file: %s\n",inFileStr);
                        return;
                }
        }
        if (outFileStr) {
                if (strcmp(outFileStr,"-")==0)
                        out_file=stdout;
        else
                if ((out_file=fopen(outFileStr,"w"))==NULL) {
                        warn("Could not write to file: %s\n",outFileStr);
                        return;
                }
                msg("Converting: %s\n",inFileStr);
        }

        if (A_AUDIO_PLAY)
                msg("Playing: %s\n",inFileStr);

        /*
         * 
         */

        decodeMPEG();
        
        close_audio();
        fclose(in_file);
        if (!A_AUDIO_PLAY) fclose(out_file);
        msg("\n");
}

