/************/
/**SoundZAP**/
/************/

/*
 *  This program was compiled with Matthew Dillon's DICE C compiler, which
 *  automatically opens and closes the Amiga libraries as they are needed.
 *  If your compiler does not do this you will have to Add the appropriate
 *  code (or get DICE!!).
 */


#include "SoundZAP.h"

void main(int argc, char *argv[])
{
    char comline[33];
    struct options *Opt;


    if (argc<2) GiveUsage();

    printf(SZVER);
    printf(" -- 3 June 1992 -- (c) Michael Cramer\n\n");

    if ((Opt=(struct options *)malloc(sizeof(struct options)))==NULL)
        CleanUp(Opt,5);

    Opt->BuffSize    = DEFAULT_SIZE;
    Opt->Size        = 0;
    Opt->InSign      = TRUE;
    Opt->OutSign     = TRUE;
    Opt->KillChunk   = FALSE;
    Opt->FibDeltIn   = FALSE;
    Opt->MuLawIn     = FALSE;
    Opt->MuLawOut    = FALSE;
    Opt->SampRate    = 0;
    Opt->NewRate     = 0;
    Opt->Data        = NULL;
    Opt->Amp         = 3;
    Opt->Bits        = 8;
    Opt->InType      = UNKNOWN;
    Opt->OutType     = IFF;
    Opt->inname[0]   = '\0';
    Opt->outname[0]  = '\0';
    Opt->FileName[0] = '\0';
    Opt->Auth        = NULL;
    Opt->Copy        = NULL;

    while (--argc > 0)
    {
        strcpy(comline,argv[argc]);
        if (comline[0]=='-')
            ProcessOpt(Opt,comline);
        else
        {
           strcpy(Opt->outname,Opt->inname);
           strcpy(Opt->inname,comline);
        }
    }

    if (strlen(Opt->inname)==0)
        GiveUsage();

    if (strlen(Opt->outname)==0)
    {
        int l;

        strcpy(Opt->outname,Opt->inname);
        l=strlen(Opt->outname);
        if(Opt->outname[l-4]=='.')
            Opt->outname[l-4]='\0';
        if(Opt->outname[l-3]=='.')
            Opt->outname[l-3]='\0';
        AFlag=TRUE;
    }
    else
        AFlag=FALSE;
    strcpy(Opt->FileName,Opt->outname);
    AnalyzeData(Opt);
    CleanUp(Opt,0);
}


void GiveUsage()
{
    printf(SZVER);
    printf("\nUsage:  SoundZAP [<options>] SOURCE [DESTINATION]\n");
    printf("    (actually, the options can appear anywhere)\n\n");
    printf("    options     -a<n>   Amplify by n/3 times\n");
    printf("\t\t-b<n>   Use a buffer size of n kilobytes\n");
    printf("\t\t-c<n>   Physically resample data to n\n");
    printf("\t\t-f      Assume input data is RAW\n");
    printf("\t\t-l      Output u-lawed data\n");
    printf("\t\t-n      Don't create extra chunks in IFF output\n");
    printf("\t\t-r<n>   Change sample rate to n\n");
    printf("\t\t-s      Output a Sun .au file\n");
    printf("\t\t-u      Output UNSIGNED data\n");
    printf("\t\t-w      Output RAW data\n");
    printf("\nSee documentation for more info.\n");
    printf("mrc113@psuvm.psu.edu\n");
    exit(0);
}

void ProcessOpt(struct options *Opt, char com[])
{
    int len;

    switch (com[1])
    {
        case 'a' : len=strlen(com)-2;
                   if (len==0)
                        Opt->Amp=0;
                   Opt->Amp=(UBYTE)atoi(com+2);
                   break;

        case 'b' :  len=strlen(com)-2;
                    if (len==0)
                        Opt->BuffSize=0;
                    Opt->BuffSize=(ULONG)atoi(com+2)*1024;
                    break;

        case 'c' :  Opt->NewRate = GetRateFromString(Opt, com+2);
                    break;

        case 'f' :  Opt->InType=RAW;
                    break;

        case 's' :  Opt->OutType=AU;
                    Opt->MuLawOut=TRUE;
                    Opt->NewRate=8000;
                    break;

        case 'w' :  Opt->OutType=RAW;
                    break;

        case 'l' :  Opt->MuLawOut=TRUE;
                    break;

        case 'n' :  Opt->KillChunk=TRUE;
                    break;

        case 'r' :  Opt->SampRate = GetRateFromString(Opt, com+2);
                    break;

        case 'u' :  Opt->OutSign=FALSE;
                    break;

        default  :  CleanUp(Opt,4);
    }
}

UWORD GetRateFromString(struct options *Opt, char *c)
{
        int len;
        UWORD n;

        len=strlen(c);
        if (len==0)
            CleanUp(Opt,3);
        if (len==1)
        {
            switch (c[0])
           {
               case '5' :  n=5696;
                           break;

               case '7' :  n=7596;
                           break;

               case '8' :  n=8000;
                           break;

               case '2' :  n=22790;
                           break;

               default  :  n=11395;
           }
       }
       else
       {
            n=(UWORD)atoi(c);
            if (!n) n=11395;
       }
       return n;
}

void CleanUp(struct options *Opt, int Error)
{
    if (Opt->Data) free(Opt->Data);
    if (Opt->Auth) free(Opt->Auth);
    if (Opt->Copy) free(Opt->Copy);
    if (Opt) free(Opt);
    if (in)   Close(in);
    if (out)  Close(out);
    if (Error==0) exit(0);
    else
    {
        printf("%s",ErrorMessages[Error]);
        exit(20);
    }
}

void AnalyzeData(struct options *Opt)
{
    ULONG MagicWord;
    char IsMac[8];

    if ((in=(struct FileHandle *)Open(Opt->inname,MODE_OLDFILE))==NULL)
        CleanUp(Opt,6);

    if(Opt->BuffSize==0)
    {
        Seek(in,0,OFFSET_END);
        Opt->BuffSize = (Seek(in,0,OFFSET_BEGINNING) + 1) & ~1;
    }

    if ((Opt->Data=(BYTE *)malloc(Opt->BuffSize))==NULL)
        CleanUp(Opt,5);

    Read(in,&MagicWord,4);
    if (Opt->InType==UNKNOWN)
    {
        if (MagicWord==0x2e736e64)                /* '.snd' */
            ConvertAU(Opt);
        else if (MagicWord==0x464f524d)           /* 'FORM' */
            ConvertIFF(Opt);
        else if (MagicWord==0x43726561)           /* 'Crea' */
            ConvertVOC(Opt);
        else if (MagicWord==0x52494646)           /* 'RIFF' */
            ConvertWAV(Opt);
        else
        {
        Seek(in,65,OFFSET_BEGINNING);
        Read(in,IsMac,8);
        if (!strcmp(IsMac,"FSSDSFX!"))
            ConvertMAC(Opt);
        else
            GuidoCheck(Opt);
        }
    }
    ConvertRaw(Opt);
}

/*  This routine is a modified version of Guido van Rossum's (guido@cwi.nl)
 *  'whatsound' routine. It guesses the sound file type (signed/unsigned/u-law)
 *  by checking how the values in the file are distributed. Thanks Guido!!
 */

void GuidoCheck(struct options *Opt)
{
    LONG a,n;
    ULONG bin[4];
    int x;


    Opt->InType=RAW;
    for (a=0; a<4; a++)
        bin[a]=0;
    do
    {
        n=Read(in,Opt->Data,Opt->BuffSize);
        for(a=0; a<n; a++)
            bin[(UBYTE)Opt->Data[a]/64]++;
    }
    while (n==Opt->BuffSize);
    if((bin[2]==0) && (bin[3]==0))
        CleanUp(Opt,7);

    x=((bin[0]+bin[3])*100)/(bin[1]+bin[2]);

    if(x>=300)
        Opt->InSign=TRUE;
    else if ( x <= 33)
        Opt->InSign=FALSE;
    else if ( (x >= 50) && (x <= 200))
    {
        Opt->MuLawIn=TRUE;
        if(Opt->SampRate==0) Opt->SampRate=8000;
    }

    if(!Opt->SampRate)
        Opt->SampRate=11395;
    Seek(in,0,OFFSET_END);
    Opt->Size=Seek(in,0,OFFSET_BEGINNING);
}


void ConvertRaw(struct options *Opt)
{
    LONG b_read,i,scale,b_written,total,t,BuffSize;
    int max;
    signed char logs[256];
    unsigned char ulawbytes[256];
    BPTR op=Output();
    BYTE max2=0, *Data, x;


    if(AFlag)
        switch (Opt->OutType)
        {
            case IFF :  strcat(Opt->outname,".iff");
                        break;

            case RAW :  strcat(Opt->outname,".raw");
                        break;

            case AU  :  strcat(Opt->outname,".au");
                        break;

            default  :  strcat(Opt->outname,".out");
        }

    if((out=(struct FileHandle *)Open(Opt->outname,MODE_NEWFILE))==NULL)
        CleanUp(Opt,8);

    if (Opt->MuLawIn && (!Opt->MuLawOut))
    {
        Opt->Amp=3;
        max=getscale(Opt);
        maketable(logs,max);
        if (Opt->InType==AU)
            Seek(in,32,OFFSET_BEGINNING);
        else
            Seek(in,0,OFFSET_BEGINNING);
    }

    if (Opt->MuLawOut && (!Opt->MuLawIn))
        maketableII(ulawbytes);

    scale=Opt->Bits/8;

    if (!Opt->Amp)
    {
        LONG tmppos;
        BYTE a;

        total=0;
        tmppos=Seek(in,0,OFFSET_CURRENT);
        do
        {
            b_read=Read(in,Opt->Data,Opt->BuffSize)/scale;
            for(i=0; i<b_read; i++)
                max2=MAX(max2,Opt->Data[i*scale]);
            total+=b_read;
        }
        while (total < Opt->Size);
        Seek(in,tmppos,OFFSET_BEGINNING);
    }

    if(Opt->NewRate)
        Opt->Size = Opt->Size * Opt->NewRate / Opt->SampRate;

    PrintIOInfo(Opt);

    switch (Opt->OutType)
    {
        case IFF :  WriteIFFStuff(Opt); break;
        case AU  :  WriteAUStuff(Opt); break;
    }

    if ( Opt->FibDeltIn )
    {
        BuffSize = Opt->BuffSize >> 1;
        Data = Opt->Data + BuffSize;
        Read (in, &x, 1);
        Read (in, &x, 1);
    }
    else
    {
        BuffSize = Opt->BuffSize;
        Data = Opt->Data;
    }

    total=0;
    do
    {
        b_read=Read(in, Data, BuffSize)/scale;

        if (Opt->FibDeltIn)
        {
            x = D1Unpack(Data,b_read,Opt->Data,x);
            b_read <<= 1;
        }

        if(Opt->MuLawIn && (!Opt->MuLawOut))
            for (i=0; i<b_read; i++)
                Opt->Data[i]=logs[(UBYTE)Opt->Data[i]];
        else
        {
            if(scale!=1)
                for (i=0; i<b_read; i++)
                    Opt->Data[i]=Opt->Data[i*scale];

            if(Opt->Amp && (Opt->Amp!=3))
                for (i=0; i<b_read; i++)
                    Opt->Data[i]=Opt->Amp*Opt->Data[i]/3;
            else if((Opt->Amp==0) && (max2 != 127))
                for (i=0; i<b_read; i++)
                    Opt->Data[i]=Opt->Data[i]*127/max2;
        }

        if(Opt->NewRate)
        {
            for(t=0; t<(b_read * Opt->NewRate / Opt->SampRate); t++)
                Opt->Data[t]=Opt->Data[t * Opt->SampRate / Opt->NewRate];
            b_read = b_read * Opt->NewRate / Opt->SampRate;
        }

        if(Opt->InSign != Opt->OutSign)
            for (i=0; i<b_read; i++)
                Opt->Data[i] ^= 0x80;

        if(Opt->MuLawOut && (!Opt->MuLawIn))
            for (i=0; i<b_read; i++)
                Opt->Data[i] = ulawbytes[(unsigned char)Opt->Data[i]];

        total+=b_read;

        b_written=Write(out,Opt->Data,b_read);
        FileLen+=b_written;
        if(b_written!=b_read) CleanUp(Opt,10);
    }
    while((total < Opt->Size) && (b_read > 0));

    if((Opt->OutType == IFF) && (total & 1))
        FileLen+=Write(out,'\0',1);

    if(Opt->OutType == IFF)
        FixSize(Opt, total);

    CleanUp(Opt,0);
}

void FixSize(struct options *Opt, ULONG total)
{
    FileLen-=8;
    Seek(out,4,OFFSET_BEGINNING);
    Write(out,&FileLen,4);
    Seek(out,BodySpot+4,OFFSET_BEGINNING);
    Write(out,&total,4);
    Seek(out,20,OFFSET_BEGINNING);
    Write(out,&total,4);
}


void WriteIFFStuff(struct options *Opt)
{
    ULONG i;
    ChunkHeader Head;
    time_t t;
    struct tm *tp;
    char datetime[50];

    Voice8Header V8H = {0,0,32,8363,1,0,Unity};

    Head.ckID = FORM;
    Head.ckSize = 0;

    FileLen+=Write(out,&Head,8);

    i=ID_8SVX;
    FileLen+=Write(out,&i,4);

    Head.ckID = ID_VHDR;
    Head.ckSize = 20;
    FileLen+=Write(out,&Head,8);

    V8H.oneShotHiSamples = 0;
    V8H.samplesPerSec = Opt->NewRate ? Opt->NewRate : Opt->SampRate;
    FileLen+=Write(out,&V8H,20);

    if (!Opt->KillChunk)
    {
        i=strlen(Opt->FileName);
        printf("NAME: %s\n",Opt->FileName);
        Head.ckID = ID_NAME;
        Head.ckSize = i;
        FileLen+=Write(out,&Head,8);
        FileLen+=Write(out,Opt->FileName,i);
        FileLen+=Write(out,'\0',i & 1);

        if(!Opt->Auth)
        {
            if(!(Opt->Auth=(char *)malloc(strlen(SZVER)+25)))
                CleanUp(Opt,5);
            strcpy(Opt->Auth,SZVER);
            strcat(Opt->Auth," by mrc113@psuvm.psu.edu");
        }
        i=strlen(Opt->Auth);
        printf("AUTH: %s\n",Opt->Auth);
        Head.ckID  = ID_AUTH;
        Head.ckSize = i;
        FileLen+=Write(out,&Head,8);
        FileLen+=Write(out,Opt->Auth,i);
        FileLen+=Write(out,'\0',i & 1);


        t=time(NULL);
        tp=localtime(&t);
        i=strftime(datetime,49,"Converted: %d %b %Y %X by SoundZAP",tp);
        printf("ANNO: %s\n",datetime);
        Head.ckID = ID_ANNO;
        Head.ckSize = i;
        FileLen+=Write(out,&Head,8);
        FileLen+=Write(out,datetime,i);
        FileLen+=Write(out,'\0',i & 1);

        if(strlen(Opt->Copy))
        {
            i=strlen(Opt->Copy);
            printf("Copyright: %s\n",Opt->Copy);
            Head.ckID = ID_Copyright;
            Head.ckSize = i;
            FileLen+=Write(out,&Head,8);
            FileLen+=Write(out,Opt->Copy,i);
            FileLen+=Write(out,'\0',i & 1);
        }
    }

    BodySpot=Seek(out,0,OFFSET_CURRENT);

    Head.ckID = ID_BODY;
    Head.ckSize = 0L;
    FileLen+=Write(out,&Head,8);
}

void WriteAUStuff(struct options *Opt)
{
    AUHeader AUH;

    AUH.magic=0x2e736e64;   /* '.snd' */
    AUH.hrd_size=24;
    AUH.channels=1;
    AUH.sample_rate=8000;
    Opt->NewRate=8000;
    AUH.encoding=1;
    AUH.data_size=0;
    FileLen+=Write(out,&AUH,24);
}

void PrintIOInfo(struct options *Opt)
{
    printf("\n%s -> %s\n\n",Opt->inname,Opt->outname);
    printf("Convert from ");
    switch (Opt->InType)
    {
        case IFF  : printf("IFF 8SVX to "); break;
        case AU   : printf(".au to "); break;
        case RAW  : printf("Raw data to "); break;
        case AIFF : printf("AIFF to "); break;
        case VOC  : printf(".voc to "); break;
        case WAV  : printf(".wav to "); break;
        case MAC  : printf("Macintosh to "); break;
        default   : printf(" <UNKNOWN> to ");
    }
    switch (Opt->OutType)
    {
        case IFF  : printf("IFF 8SVX\n"); break;
        case AU   : printf(".au\n"); break;
        case RAW  : printf("Raw data\n"); break;
/*
        case AIFF : printf("AIFF\n"); break;
        case VOC  : printf(".voc\n"); break;
        case WAV  : printf(".wav\n"); break;
        case MAC  : printf("Macintosh\n"); break;
 */
        default   : printf(" <UNKNOWN>\n");
    }
    printf("\nInput data is ");
    if(Opt->MuLawIn)
        printf("u-lawed, ");
    else
        printf(Opt->InSign ? "signed, " : "unsigned, ");
    if (Opt->FibDeltIn) printf("Fibonacci Delta encoded, ");
    printf("and the output data is ");
    if(Opt->MuLawOut)
        printf("u-lawed\n");
    else
        printf(Opt->OutSign ? "signed\n" : "unsigned\n");

    printf("Input data is %d bits per sample, %d samples per second\n",Opt->Bits,Opt->SampRate);

    if (Opt->NewRate)
        printf("Output data is re-sampled to %d samples per second\n",Opt->NewRate);

    printf("\nBuffer size: %d bytes\n",Opt->BuffSize);

    if(!Opt->Amp)
        printf("\nOutput data amplified to maximum\n");
    else if (Opt->Amp != 3)
        printf("\nOutput data amplified %d thirds times\n",Opt->Amp);

    if(Opt->OutType == IFF)
        if(Opt->KillChunk) printf("\nSuppress extra chunk creation\n");
        else printf("\nChunks to be written to IFF file:\n");
}

void ConvertVOC(struct options *Opt)
{
    UBYTE c[2];

    if(!Opt->SampRate)
    {
        Seek(in,30,OFFSET_BEGINNING);
        Read(in,c,2);
        if(c[1]!=0)
            CleanUp(Opt,9);
        Opt->SampRate=1000000/(256-c[0]);
    }
    Opt->InSign=FALSE;
    Seek(in,0,OFFSET_END);
    Opt->Size=Seek(in,0,OFFSET_BEGINNING)-32;
    Seek(in,32,OFFSET_BEGINNING);
    Opt->InType=VOC;
}

void ConvertWAV(struct options *Opt)
{
    ChunkHeader Head;
    ULONG FileSize;
    ULONG FileType;
    LONG b_read;
    WaveFmtChunk fmt;

    Seek(in,0,OFFSET_BEGINNING);
    Read(in,&Head,8);
    if(Head.ckID != RIFF)
        CleanUp(Opt,12);
    FileSize = SCREWINTEL(Head.ckSize);

    b_read=Read(in,&FileType,4);
    if(FileType != WAVE)
        CleanUp(Opt,12);

    while((Head.ckID != DATA) && b_read)
    {
        b_read = Read(in,&Head,8);
        if (b_read)
        {
            Head.ckSize = SCREWINTEL(Head.ckSize);
            if (Head.ckID == FMT)
            {
                Read(in,&fmt,sizeof(WaveFmtChunk));
                if(fmt.wChannels != 0x0100)
                    CleanUp(Opt,15);
                if(fmt.wFormatTag != 0x0100)
                    CleanUp(Opt,9);
                Opt->SampRate = SCREWINTEL(fmt.dwSamplesPerSec);
                Seek(in,Head.ckSize - sizeof(WaveFmtChunk),OFFSET_CURRENT);
            }
            else if (Head.ckID == DATA)
                Opt->Size = SCREWINTEL(Head.ckSize);
            else
                Seek(in,(SCREWINTEL(Head.ckSize) + 1) & ~1 ,OFFSET_CURRENT);
        }
    }
    Opt->InSign=FALSE;
    Opt->InType=WAV;
}

void ConvertMAC(struct options *Opt)
{
    USHORT len;
    char *name;

    Opt->InSign=FALSE;
    Seek(in,0,OFFSET_END);
    Opt->Size=Seek(in,0,OFFSET_BEGINNING)-668;
    Read(in,&len,2);
    name=(char *)malloc(len);
    Read(in,name,len);
    name[len]='\0';
    strcpy(Opt->FileName,name);
    Seek(in,128,OFFSET_BEGINNING);
    if (!Opt->SampRate)
        Opt->SampRate=11395;
    free(name);
    Opt->InType=MAC;
}

/* Thanks to Sean Connolly for sending me info on the .au sound format.*/

void ConvertAU(struct options *Opt)
{
    AUHeader AUHdr;

    Seek(in,0,OFFSET_END);
    Opt->Size=Seek(in,0,OFFSET_BEGINNING)-32;
    Read(in,&AUHdr,28);
    switch (AUHdr.encoding)
    {
        case 1   :  Opt->MuLawIn=TRUE;
                    break;

        case 2   :  Opt->Bits=8;
                    break;

        case 3   :  Opt->Bits=16;
                    Opt->Size/=2;
                    break;

        case 4   :  Opt->Bits=24;
                    Opt->Size/=3;
                    break;

        case 5   :  Opt->Bits=32;
                    Opt->Size/=4;
                    break;

        default  :  CleanUp(Opt,9);
                    break;
    }

    if (!Opt->SampRate)
        Opt->SampRate=AUHdr.sample_rate;

    Opt->InType=AU;
}

void ConvertIFF(struct options *Opt)
{
    struct ChunkInfo List, *This, *Next;
    Voice8Header V8H;
    CommonChunk CC;
    char *data;
    int Help=0;
    BOOL Flag=TRUE;

    List=GetChunks(Get,9,in);

    if (List.Next == NULL)
        Help=11;
    else
    {
        switch(List.Name)
        {
            case ID_8SVX    : Opt->InType=IFF; break;
            case ID_AIFF    : Opt->InType=AIFF; break;
            default         : Help=11;
        }
    }

    Next=List.Next;
    while(Next)
    {
        This=Next;
        Seek(in,This->Pos,OFFSET_BEGINNING);
        switch(This->Name)
        {
            case ID_COMM : Read(in,&CC,18);
                           Opt->Size=CC.numSampleFrames;
                           Opt->Bits=(CC.sampleSize+7)/8;
                           Opt->Bits*=8;
                           break;

            case ID_SSND : Seek(in,8,OFFSET_CURRENT); /*Don't use it for anything so just get to the right place */
                           break;

            case ID_VHDR : Read(in,&V8H,20);
                           if (!Opt->SampRate)
                               Opt->SampRate=V8H.samplesPerSec;
                           if(V8H.sCompression) Opt->FibDeltIn=TRUE;
                           break;

            case ID_BODY : Opt->Size=This->Size;
                           break;

        /* AUTH and Copyright must be preserved! */

            case ID_AUTH : if(!(Opt->Auth=(char *)malloc(This->Size)))
                               CleanUp(Opt,5);
                           Read(in,Opt->Auth,This->Size);
                           Opt->Auth[This->Size]='\0';
                           if (!strncmp(Opt->Auth,"SoundZAP",8))
                           {
                                free(Opt->Auth);
                                Opt->Auth=NULL;
                           }
                           break;

            case ID_Copyright : if(!(Opt->Copy=(char *)malloc(This->Size)))
                                    CleanUp(Opt,5);
                                Read(in,Opt->Copy,This->Size);
                                Opt->Copy[This->Size]='\0';
                                break;

            case ID_NAME : if(!(data=(char *)malloc(This->Size)))
                               CleanUp(Opt,5);
                           Read(in,data,This->Size);
                           data[This->Size]='\0';
                           if (Flag) printf("Chunks found but not preserved:\n");
                           printf("NAME: %s\n",data);
                           free(data);
                           Flag=FALSE;
                           break;

            case ID_ANNO : if(!(data=(char *)malloc(This->Size)))
                               CleanUp(Opt,5);
                           Read(in,data,This->Size);
                           data[This->Size]='\0';
                           if (Flag) printf("Chunks found but not preserved:\n");
                           Flag=FALSE;
                           printf("ANNO: %s\n",data);
                           free(data);
                           break;

            case ID_TEXT : if(!(data=(char *)malloc(This->Size)))
                               CleanUp(Opt,5);
                           Read(in,data,This->Size);
                           data[This->Size]='\0';
                           if (Flag) printf("Chunks found but not preserved:\n");
                           Flag=FALSE;
                           printf("TEXT: %s\n",data);
                           free(data);
                           break;
        }
        Next=This->Next;
        free(This);
    }
    if (Help)
        CleanUp(Opt,Help);
}


/*--------------------------------------------------------------------------*
 * The following routine was extracted from posting by Brian Foley.         *
 * Brian Foley          email: bfoley@greatlakes.Central.Sun.COM            *
 * Systems Engineer     smail:  1000 Town Center                            *
 * Sun Microsystems             Suite 1700                                  *
 * GreatLakes Region            Southfield, MI 48075   (313) 352-7070       *
 *--------------------------------------------------------------------------*/

int ulaw2linear(unsigned char ulawbyte)
{
        static int exp_lut[8] = { 0, 132, 396, 924, 1980, 4092, 8316, 16764 };
               int sign, exponent, mantissa, sample;

        ulawbyte    = ~ulawbyte;
        sign        =  ulawbyte & 0x80;
        exponent    = (ulawbyte >> 4) & 0x07;
        mantissa    =  ulawbyte & 0x0F;
        sample      = (exp_lut[exponent] + (mantissa << (exponent + 3)));
        if ( sign ) sample = -sample;
        return sample;
}

int getscale(struct options *Opt)
{
        int count, max = 0, i;

        do
        {
            count    = Read(in, Opt->Data, Opt->BuffSize);
            for ( i = 0; i < count; i++ )
                max     = MAX(abs(ulaw2linear(Opt->Data[i])), max);
        }
        while ( count == Opt->BuffSize );

        return max;
}

void maketable(signed char *logs, int max)
{
        int i, c, d;

        for ( i = 0; i < 256; i++ )
        {
                c = ( ulaw2linear(i) * ulaw2linear(0) ) / max;
                d = abs(c) & 0xFF;
                if ( d > 0x7F )
                    if ( c > 0 )
                        logs[i] = (signed char) ( c / 256 + 1 );
                    else
                        logs[i] = (signed char) ( c / 256 - 1 );
                else
                    logs[i] = (signed char) ( c / 256 );
        }
}

void maketableII(unsigned char *ubytes)
{
    signed int i;

    for(i=0; i<256; i++)
        ubytes[i]=st_linear_to_ulaw((signed char)i);
}

/*
** This routine converts from linear to ulaw.
**
** Craig Reese: IDA/Supercomputing Research Center
** Joe Campbell: Department of Defense
** 29 September 1989
**
** References:
** 1) CCITT Recommendation G.711  (very difficult to follow)
** 2) "A New Digital Technique for Implementation of Any
**     Continuous PCM Companding Law," Villeret, Michel,
**     et al. 1973 IEEE Int. Conf. on Communications, Vol 1,
**     1973, pg. 11.12-11.17
** 3) MIL-STD-188-113,"Interoperability and Performance Standards
**     for Analog-to_Digital Conversion Techniques,"
**     17 February 1987
**
** Input: Signed 16 bit linear sample
** Output: 8 bit ulaw sample
*/

#define BIAS 0x84   /* define the add-in bias for 16 bit samples */
#define CLIP 32635

unsigned char st_linear_to_ulaw( signed char oldsample )
{
    static int exp_lut[256] = {0,0,1,1,2,2,2,2,3,3,3,3,3,3,3,3,
                               4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,
                               5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
                               5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
                               6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
                               6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
                               6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
                               6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
                               7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7};
    int sign, exponent, mantissa, sample;
    unsigned char ulawbyte;

    sample=oldsample << 8;
    /* Get the sample into sign-magnitude. */
    sign = (sample >> 8) & 0x80;               /* set aside the sign */
    if ( sign != 0 ) sample = -sample;         /* get magnitude */
    if ( sample > CLIP ) sample = CLIP;                /* clip the magnitude */

    /* Convert from 16 bit linear to ulaw. */
    sample = sample + BIAS;
    exponent = exp_lut[( sample >> 7 ) & 0xFF];
    mantissa = ( sample >> ( exponent + 3 ) ) & 0x0F;
    ulawbyte = ~ ( sign | ( exponent << 4 ) | mantissa );
/*
    if ( ulawbyte == 0 ) ulawbyte = 0x02;
*/
    return ulawbyte;
}

struct ChunkInfo GetChunks(ULONG *Names, int n, struct FileHandle *in)
{
    LONG Size, pos=0,b_read;
    ChunkHeader Head;
    struct ChunkInfo List, *ThisNode;
    ID Type;
    int i;

    Seek(in,0,OFFSET_END);
    Size=Seek(in,0,OFFSET_BEGINNING);
    if(Size < 50)
    {
        List.Size=ERROR_IN_FORM;
        List.Next=NULL;
        return List;
    }

    pos+=Read(in,&Head,8);
    if (Head.ckID != FORM)
    {
        List.Size=ERROR_IN_FORM;
        List.Next=NULL;
        return List;
    }

    pos+=Read(in,&List.Name,4);

    b_read=Read(in,&Head,8);
    ThisNode=&List;
    do
    {
    pos+=b_read;
    for (i=1; i<n; i++)
        if (Head.ckID == Names[i])
        {
            if((ThisNode->Next=(void *)malloc(16))==NULL)
            {
                List.Size=NO_MEMORY;
                return List;
            }

            ThisNode=ThisNode->Next;
            ThisNode->Next = NULL;
            ThisNode->Name = Head.ckID;
            ThisNode->Size = Head.ckSize;
            ThisNode->Pos  = pos;
        }
    Seek(in,Head.ckSize+(Head.ckSize & 1),OFFSET_CURRENT);
    pos+=Head.ckSize+(Head.ckSize & 1);
    b_read=Read(in,&Head,8);
    }
    while ((ThisNode->Name!=ID_BODY) && (b_read==8));

    List.Size=GOOD_FILE;
    return List;
}
