
#define AUDIO
#define BUFSIZE  18432 * 8

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <exec/types.h>
#include <exec/libraries.h>
#include <exec/lists.h>
#include <exec/memory.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>

#include <clib/asl_protos.h>
#include <libraries/asl.h>
#include <utility/tagitem.h>

#include <libraries/prelude.h>
#include <clib/prelude_protos.h>

#include <devices/ahi.h>

#include <clib/powerpc_protos.h>
#include <powerpc/memoryPPC.h>

#include "amp.h"
#include "audio.h"
#include "getbits.h"
#include "huffman.h"
#include "layer2.h"
#include "layer3.h"
#include "transform.h"
#include "misc2.h"
#include "dump.h"

int output=1;
int filesize=0;

extern struct ExecBase *SysBase;
struct PreludeBase *PreludeBase=NULL;

UBYTE version [] = "\0$VER: WarpAMP 0.9 (07.5.98)";

int    mysignal;
ULONG   Signals;

UBYTE   *PlayBuffer[2];
ULONG   Buffer;
ULONG BufferFill;
UBYTE *BufferPointer;

ULONG PlayCnt;
ULONG PlayMode;
ULONG PlayFreq;
BOOL snd_eof;
int noout=1;
int ppcmem=1;
int filespec=0;
char infile[1024];
struct FileRequester *smr=0;
char *name;
char fname[1024];
FILE *fp;
char prog[1024];
FILE *fil=0;
int useprog=0;
int repeat=0;
int quit=0;

extern struct Library *AslBase;

struct TagItem tags[]={
ASLFR_DoPatterns,
1,
ASLFR_InitialPattern,
(ULONG)"#?.(mpg|mpeg|mp3|mp2)",
ASLFR_DoMultiSelect,1,
0,0};

struct TagItem tags2[]={
ASLFR_DoPatterns,
1,
ASLFR_InitialPattern,
(ULONG)"#?.(prg)",
0,0};


//-------------------------------------

struct Device     *AHIDevice = NULL;
struct MsgPort    *AHIPort = NULL;
struct AHIRequest *AHIReq = NULL;
struct AHIRequest *AHIReq2 = NULL;
struct AHIRequest *AHIReqs[2];
struct AHIRequest *link = NULL;

void CloseAHIDevice()
{
        if (AHIDevice)
        {
                CloseDevice((struct IORequest *) AHIReq);
                AHIDevice = NULL;
        }
        if (AHIReq)
        {
                DeleteIORequest(AHIReq);
                AHIReq = NULL;
        }
        if (AHIPort)
        {
                DeleteMsgPort(AHIPort);
                AHIPort = NULL;
        }

        if (AHIReq2)
        {
                DeleteIORequest(AHIReq2);
                AHIReq2 = NULL;
        }
}

int InitAHIDevice(ULONG unit, ULONG version)
{
        if (AHIPort = CreateMsgPort())
        {
                if (AHIReq = (AHIRequest *) CreateIORequest(AHIPort, sizeof(struct AHIRequest)))
                {
                        AHIReq->ahir_Version = version;
                        if (!OpenDevice(AHINAME, unit, (struct IORequest *) AHIReq, 0))
                                AHIDevice = AHIReq->ahir_Std.io_Device;
                }
        }

        if (!AHIDevice)
        {
                CloseAHIDevice();
                return FALSE;
        }

        if (!(AHIReq2 = (AHIRequest *) CreateIORequest(AHIPort, sizeof(struct AHIRequest))))
        {
                CloseAHIDevice();
                return FALSE;
        }

        *AHIReq2 = *AHIReq;

        return TRUE;
}

//-------------------------------------

int prelude=0;

int main(int argc, char **argv)
{
        struct Task *ThisTask;
        int f;
        BYTE OldPri;
        struct PrlCtrl *prl;
        int ahiunit = 0;

 AslBase=(struct Library *)OpenLibrary("asl.library",36);
 if (!AslBase)
 {
  printf("Could not open asl.library!\n");
  exit(0);
 }
        prelude=0;
        ppcmem=1;
        filespec=0;
        output=1;
        for (f=1;f<argc;f++)
        {
         if (strstr(argv[f],"-nobat")) ppcmem=0;
         else if (strstr(argv[f],"-quiet")) output=0;
         else if (strstr(argv[f],"-prelude")) prelude=1;       
         else if (strstr(argv[f],"-program"))
         {
          useprog=1;
          if (f+1<argc) {strcpy(prog,argv[f+1]);useprog=2;}
          f=f+1;
         }
         else
         {
          strcpy(infile,argv[f]);filespec=1;
         }
        }

        ThisTask=FindTask(NULL);

if (!prelude)
{
        if (!InitAHIDevice(ahiunit, 4))
        {
                fprintf(stderr, "\nError: Can't open AHI V4\n\n");
                return(5);
        }
        AHIReqs[0] = AHIReq;
        AHIReqs[1] = AHIReq2;
        link = NULL;
}
else
{
        PreludeBase=(struct PreludeBase *)OpenLibrary("prelude.library", 2);
        if(PreludeBase==NULL) {
                fprintf(stderr, "\nError: Can't open prelude.library v2\n\n");
                return(5);
        }
}

        if (!ppcmem)
        {
         PlayBuffer[0]=AllocVecPPC(BUFSIZE, MEMF_CLEAR|MEMF_PUBLIC,8);
         PlayBuffer[1]=AllocVecPPC(BUFSIZE, MEMF_CLEAR|MEMF_PUBLIC,8);
        }
        else
        {
         PlayBuffer[0]=AllocVecPPC(BUFSIZE,MEMF_CLEAR|MEMF_PUBLIC|MEMF_BAT|MEMF_CACHEOFF,8);
         PlayBuffer[1]=AllocVecPPC(BUFSIZE,MEMF_CLEAR|MEMF_PUBLIC|MEMF_BAT|MEMF_CACHEOFF,8);
        }

        if (PlayBuffer[0]==NULL || PlayBuffer[1]==NULL)
        {
                if (PlayBuffer[0]) FreeVecPPC(PlayBuffer[0]);
                fprintf(stderr, "\nError: Out of memory for playback buffers\n\n");
                return(5);
        }

if (prelude)
{
        prl=PreludeBase->PrlCtrl;
        prl->PL_SigTask=ThisTask;
        mysignal=AllocSignal(-1);
        prl->PL_SigMask=1L<<mysignal;
}

        A_DUMP_BINARY=FALSE;
        A_QUIET=FALSE;
        A_FORMAT_WAVE=FALSE;
        A_SHOW_CNT=FALSE;
        A_SET_VOLUME=-1;
        A_SHOW_TIME=1;
        A_AUDIO_PLAY=FALSE;
        A_WRITE_TO_FILE=TRUE;
        A_MSG_STDOUT=FALSE;
        A_DOWNMIX=FALSE;


        initialise_decoder();


        if ((argc<2)||(!filespec))
        {
         int f;
         smr=AllocAslRequest(ASL_FileRequest,0);
         if (!smr)
         {
          printf("Could not open File-Requester!\n");
          if (AslBase) CloseLibrary(AslBase);
         }
         if (!useprog)
         {
          if (!AslRequest(smr,tags))
          {
           printf("Could not open File-Requester!\n");
           if (AslBase) CloseLibrary(AslBase);
          }
         }
         else if (useprog!=2)
         {
          if (!AslRequest(smr,tags2))
          {
           printf("Could not open File-Requester!\n");
           if (AslBase) CloseLibrary(AslBase);
          }
          name=&(fname[0]);
          strcpy(name,((struct FileRequester *)smr)->fr_Drawer);
          if (strlen(name)>0)
          if ((name[strlen(name)-1]!='/')&&(name[strlen(name)-1]!=':')) strcat(name,"/");
          strcat(name,smr->fr_File);
          strcpy(prog,name);
         }

        }
        Buffer=0;
        BufferPointer=PlayBuffer[0];
        BufferFill=0;
        PlayCnt=0;

        if (output)
        {
        fprintf(stderr, "\n\-------------------------------------------\n");
        fprintf(stderr,"WarpAMP 0.9 - 1998, (C) by Steffen Haeuser\n");
        fprintf(stderr,"based on amp (C) Tomislav Uzelac  1996,1997\n");
        fprintf(stderr,"Prelude Play Code based on PreludeAMP which is\n");
        fprintf(stderr,"(C) by Thomas Wenzel\n");
        fprintf(stderr,"Use without infile to invoke Filerequester\n");
        fprintf(stderr,"The Filerequester supports MultiSelect\n");
        fprintf(stderr,"use -program to load a program consisting\n");
        fprintf(stderr,"of a textfile, one title per line\n");
        fprintf(stderr,"use a small-case repeat in the last line\n");
        fprintf(stderr,"to run the program in a loop\n");
        fprintf(stderr,"use -nobat to enable Caches\n");
        fprintf(stderr,"use -prelude, if you own a Prelude Soundboard.\n");
        fprintf(stderr,"-------------------------------------------\n");
        if (prelude) fprintf(stderr,"prelude.library Native Mode used.\n");
        else fprintf(stderr,"AHI Mode used.\n");
        }
        OldPri=SetTaskPri(ThisTask, 5);

        if (filespec)
        {
          fp=fopen(infile,"r");
          if (!fp)
          {
           printf("Could not open file %s!\n",name);
           if (AslBase) CloseLibrary(AslBase);
           if (PreludeBase) CloseLibrary((struct Library *)PreludeBase);
           if (smr) FreeAslRequest(smr);
           exit(0);
          }
          else fclose(fp);
          play(infile,0);
          goto endit; // Goto's considered harmful :)
        }
        else if (!useprog)
        {
         for (f=1;f<=smr->fr_NumArgs;f++)
         {
          name=&(fname[0]);
          strcpy(name,((struct FileRequester *)smr)->fr_Drawer);
          if (strlen(name)>0)
          if ((name[strlen(name)-1]!='/')&&(name[strlen(name)-1]!=':')) strcat(name,"/");
          strcat(name,smr->fr_ArgList[f-1].wa_Name);
          fp=fopen(name,"r");
          if (!fp)
          {
           printf("Could not open file %s!\n",name);
           if (AslBase) CloseLibrary(AslBase);
           if (PreludeBase) CloseLibrary((struct Library *)PreludeBase);
           if (smr) FreeAslRequest(smr);
           exit(0);
          }
          else fclose(fp);
          strcpy(infile,name);
          play(infile, 0);
          if (quit) goto endit;
         }
        }
        else
        {
         char test[1024];
         if (output) printf("\nPROGRAM: %s\n",prog);
do
        {
         fil=fopen(prog,"r");
         if (fil)
         {
          while(!feof(fil))
          {
           char ch;
           char test2[1024];
           char bla[10];
           strcpy(test2,"");
           do
           {
            fscanf(fil,"%s",test);
            strcat(test2,test);
            ch=fgetc(fil);
            fseek(fil,-1,SEEK_CUR);
            bla[0]=ch;
            bla[1]=0;
            if (ch!=10) strcat(test2,bla);
           } while(ch!=10);
           strcpy(test,test2);
           if ((!feof(fil))&&(!strstr(test,"repeat")))
           {
            fp=fopen(test,"r");
            if (!fp)
            {
             printf("Could not open file %s!\n",test);
             if (AslBase) CloseLibrary(AslBase);
             if (PreludeBase) CloseLibrary((struct Library *)PreludeBase);
             if (smr) FreeAslRequest(smr);
             exit(0);
            }
            else fclose(fp);
            strcpy(infile,test);
            play(infile,0);
            if (quit) goto endit;
           }
           else if (strstr(test,"repeat"))
           {
            repeat=1;
           }
          }
         }
         else printf("Could not open Program File!!!\n");
        } while(repeat);
        if (fil) fclose(fil);
       }
endit:
        SetTaskPri(ThisTask, OldPri);

if (!prelude)
{
        // Abort any pending iorequests
        if (PlayCnt > 0)
        {
                AbortIO((struct IORequest *) AHIReqs[Buffer^1]);
                WaitIO((struct IORequest *) AHIReqs[Buffer^1]);
        }
        if (PlayCnt > 1)
        {
                AbortIO((struct IORequest *) AHIReqs[Buffer]);
                WaitIO((struct IORequest *) AHIReqs[Buffer]);
        }
}
else
{
        PrlStop(0);
        KillPrlPlayList();
}
        FreeVecPPC(PlayBuffer[0]);
        FreeVecPPC(PlayBuffer[1]);
        if(mysignal != -1) FreeSignal(mysignal);

if (!prelude) CloseAHIDevice();
else
{
        if (PreludeBase) CloseLibrary((struct Library *) PreludeBase);
}
        if (smr) FreeAslRequest(smr);
        if (AslBase) CloseLibrary((struct Library *)AslBase);
        return(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 play(char *inFileStr, char *outFileStr) {
        if (strcmp(inFileStr,"-")==0) {
                in_file=stdin;
        }
        else {
                BPTR MyLock;
                struct FileInfoBlock FIB;
                if ((in_file=fopen(inFileStr,"r"))==NULL) {
                        fprintf(stderr, "Could not open file: %s\n",inFileStr);
                        return;
                }
                MyLock=Lock(inFileStr,ACCESS_READ);
                Examine(MyLock,&FIB);
                filesize=FIB.fib_Size;
                if (MyLock) UnLock(MyLock);

        }
        if (outFileStr) {
                if (strcmp(outFileStr,"-")==0)
                        {out_file=stdout;noout=1;}
        else
                {
                 if ((out_file=fopen(outFileStr,"w"))==NULL) {
                        fprintf(stderr, "Could not write to file: %s\n",outFileStr);
                        return;
                 }
                 noout=0;
                }
        }

        decodeMPEG();

        fclose(in_file);
        if ((!A_AUDIO_PLAY)&&(!noout)) fclose(out_file);
        if (output) fprintf(stderr, "\n");
}

int decodeMPEG(void) {
        struct AUDIO_HEADER header;
        int cnt=0,g;

        initialise_globals();

        if ((g=gethdr(&header))!=0) {
                report_header_error(g);
                return -1;
        }

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

        show_header(&header);

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


if (prelude)
{
        if (nch==2) PlayMode = PRL_FMT | PRL_FMTX | PRL_Stereo;
        else        PlayMode = PRL_FMT | PRL_FMTX;
}
        PlayFreq=t_sampling_frequency[header.ID][header.sampling_frequency];

        /*
         * decoder loop **********************************
         */
        snd_eof=FALSE;
        cnt=0;
        while (!snd_eof) {
                while (!snd_eof) {
                        if ((g=gethdr(&header))!=0) {
                        report_header_error(g);
                                snd_eof=TRUE;
                                break;
      }

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

                        statusDisplay(&header,cnt);

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

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


void statusDisplay(struct AUDIO_HEADER *header, int frameNo) {
        int minutes,seconds;
        if (!output) return;
        if ((A_SHOW_CNT || A_SHOW_TIME) && !(frameNo%40))
                fprintf(stderr, "\r");
        if (A_SHOW_CNT && !(frameNo%10) ) {
                fprintf(stderr, "Frame { %d } ",frameNo);
        }
        if (A_SHOW_TIME && !(frameNo%40)) {
                seconds=frameNo*1152/t_sampling_frequency[header->ID][header->sampling_frequency];
                minutes=seconds/60;
                seconds=seconds % 60;
                fprintf(stderr, "Time [%d:%02d]", minutes, seconds);
        }
        if (A_SHOW_CNT || A_SHOW_TIME)
                fflush(stderr);
}

BOOL WayBehind=FALSE;
void printout(void) {
        int len;
        int j;

        if (nch==2) j=32 * 18 * 2;
        else        j=32 * 18;

        len=sizeof(short)*j;

        memcpy(BufferPointer, sample_buffer, len);
        BufferPointer += len;
        BufferFill    += len;

        if (BufferFill >= BUFSIZE)
        {
if (!prelude)
{
                        AHIReqs[Buffer]->ahir_Std.io_Command = CMD_WRITE;
                        AHIReqs[Buffer]->ahir_Std.io_Flags   = 0;
                        AHIReqs[Buffer]->ahir_Std.io_Data    = PlayBuffer[Buffer];
                        AHIReqs[Buffer]->ahir_Std.io_Length  = BUFSIZE;
                        AHIReqs[Buffer]->ahir_Std.io_Offset  = 0;
                        AHIReqs[Buffer]->ahir_Type = (nch == 2 ? AHIST_S16S : AHIST_M16S);
                        AHIReqs[Buffer]->ahir_Frequency = PlayFreq;
                        AHIReqs[Buffer]->ahir_Volume = 0x10000;
                        AHIReqs[Buffer]->ahir_Position = 0x8000;
                        AHIReqs[Buffer]->ahir_Link = link;
                        SendIO((struct IORequest *) AHIReqs[Buffer]);
                        PlayCnt++;

                        if (link)
                        {
                                Signals = Wait(SIGBREAKF_CTRL_C | (1L << AHIPort->mp_SigBit));

                                WaitIO((struct IORequest *) link);
                        }

                        link = AHIReqs[Buffer];
                        Buffer ^= 1;

                        BufferPointer = PlayBuffer[Buffer];
                        BufferFill=0;
}
else
{
                PrlPlay(PlayBuffer[Buffer], BUFSIZE, PlayMode, PlayFreq);
                Buffer=1-Buffer;

                BufferPointer=PlayBuffer[Buffer];
                BufferFill=0;

                PlayCnt++;
                if(PlayCnt >1) Signals=Wait(1L<<mysignal | SIGBREAKF_CTRL_C);
}

                if(Signals & SIGBREAKF_CTRL_C) {snd_eof=TRUE;quit=1;}
        }
}

void die(char *str, ...) {
}
