/****************************   Sound.c   *********************************

    Sound is copyright (c) 1988 by Richard Lee Stockton, 21305 60th Ave W.,
Mountlake Terrace, Washington 98043, 206/776-1253(voice), but may be freely
distributed as long as no profit is made from its distribution or sale
without my written permission. I call this concept 'FreeWare', you can
call it whatcha want.

    I also include the source code, (Manx Aztec 5.0d), in the hope that it
will be of benefit to someone in the Amiga programming community. Feel free
to alter it at will, (but at your own risk!). I _would_ appreciate
receiving a copy of whatever it may become and it is always nice to receive
credit, (perhaps just a few lines of glowing tribute.^)

               Long Live Leo and All the Little Schwabies!

                     To Manufacture with Manx 5.0d

                        cc -ps -safnps Sound.c
                        ln +cdb Sound.o -lc16


**************************************************************************/

#include <exec/memory.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>
#include <workbench/icon.h>
#include <libraries/dosextens.h>
#include <graphics/gfxbase.h>
#include <devices/audio.h>
#include <string.h>
#include <functions.h>

/* Less than WorkBench 1.2 need not apply. That's nobody, right? ;-> */

#define  REVISION   33L

/* We'll need 4 buffers in CHIP memory, all else in FAST, if ya got it */

long  BUFSIZE = 1024L;

/* A pretty little HELP message showing valid variable ranges */

#define  USAGE      "\
\033[42m\033[31m\
 USAGE: Sound <file> [56-65535] [v1-v64] [0-55] [e1-e64] [dfglrsq] \
\033[m\n\033[42m\
 ABORT: <CTRL> 'c'\033[33m\
  SAMPLES/SEC  VOLUME  CYCLES  END_VOL   FLAGS   \
\033[m\n\033[42m\033[31m\
 GRAMMA SOFTWARE * freely distributable * 07 Mar \xA9 1991 RLStockton \
\033[m\n"

/* Probably more GLOBALS than are required. 'C' is funny stuff. */

extern struct   GfxBase       *GfxBase=NULL;
extern struct   IconBase      *IconBase=NULL;
extern struct   IntuitionBase *IntuitionBase=NULL;
extern struct   MsgPort       *CreatePort();
extern struct   WBStartup     *WBenchMsg=NULL;
extern struct   WBArg         *argp=NULL;
       struct   IntuiMessage  *message=NULL;
       struct   Window        *StatusWindo=NULL;
       struct   DiskObject    *infofile=NULL;
       struct   IOAudio       *sound[4]={NULL,NULL,NULL,NULL};
       struct   Filehandle    *sFile=NULL;
       struct   FileLock      *lock=NULL, *savelock=NULL;
                long          sactual=0L, sstart=0L, vol=64L, fade=0L,
                               atol(), sps=0L, cycles=1L, startvol=64L,
                               endvol=64L, fadevol=0L;
                short         k=0, stereo=0, left=1, right=1, compflag=0,
                               statusline=1, direct=0;
                UBYTE         sunit[4]={12,10,5,3},
                               sunitL[2]={1,8}, sunitR[2]={2,4};
                BOOL          help=FALSE;
                char          *sbuffer=NULL, *ltoa(), title[108]="",
                               *cbuf[4]={NULL,NULL,NULL,NULL},
                               sname[108], *SafeAllocMem(),
                               *portname[4]={"Snd0","Snd1","Snd2","Snd3"},
                               comptable[16]={-34,-21,-13,-8,-5,-3,-2,-1,
                                     0,1,2,3,5,8,13,21};
                void          loadSound(), setStatusWindo(),
                               soundSound(), cleanup(), quit();

/*********** quit, give-up, go home, finish... Neatness counts! ******/

void quit(qstring)
char    *qstring;
{
   cleanup(qstring);   if(GfxBase) CloseLibrary(GfxBase);
   exit(RETURN_OK);
}

void cleanup(string)
char    *string;
{
   if(sound[0])      /* This cleans up the audio device stuff */
   {
      for(k=3;k>(-1);k--)
      {
         if((sound[k])&&(sound[k]->ioa_Request.io_Device))
            AbortIO((struct IORequest *)sound[k]);
      }
      if(sound[0]->ioa_Request.io_Device)
         CloseDevice((struct IORequest *)sound[0]);
      for(k=3;k>(-1);k--)
      {
         if(sound[k]->ioa_Request.io_Message.mn_ReplyPort)
            DeletePort(sound[k]->ioa_Request.io_Message.mn_ReplyPort);
      }
      for(k=3;k>(-1);k--)
      {
         if(sound[k]) FreeMem(sound[k],(long)sizeof(struct IOAudio));
         if(cbuf[k])  FreeMem(cbuf[k],BUFSIZE);
      }
      sound[0]=sound[1]=sound[2]=NULL;
   }

/* Write any message to out. May be error or could be samples/second */
/* You'll be sorry if you try to Write(Output()) to WorkBench!  8-)  */

   if(help) Write(Output(),string,(long)strlen(string));
   else if(strlen(string)>10L)
   {
      strcpy(title,"Sound Error: ");   strcat(title,string);
      setStatusWindo();  Delay(128L);   sound[3]=NULL;
   }

/* Clean up everything else */

   if(StatusWindo)      CloseWindow(StatusWindo);
   if(IntuitionBase)    CloseLibrary(IntuitionBase);
   if(sFile)            Close((BPTR)sFile); 
   if(sbuffer)          FreeMem(sbuffer,sactual);
   if(infofile)         FreeDiskObject(infofile);
   if(IconBase)         CloseLibrary(IconBase);
   if(!sound[3])        exit(RETURN_FAIL);

/* Reset everything in case we are returning (WB multiple select) */

   StatusWindo=NULL;    IntuitionBase=NULL;
   sbuffer=NULL;   infofile=NULL;   IconBase=NULL;
   sactual=0L; sstart=0L; savelock=NULL; sps=0L; cycles=1L;
   lock=0L; k=0; stereo=0; left=1; right=1; startvol=endvol=vol=64L;
}


/*  Don't Allocate if Low Mem - by Bryce Nesbitt  */
/* Aberations by RLS. 4096 should be fudge enough */

char *SafeAllocMem(size,flags)
long size, flags;
{
   register char *p;
   
   if(p=(char *)AllocMem(size,flags))
      if(AvailMem(MEMF_CHIP)<4096L) {FreeMem(p,size); return(NULL);}
   return(p);
}

/**** Status Window. Uses <CTRL> 'c' to abort.  ****/

void setStatusWindo()
{
    struct Screen    *s;
    struct NewWindow w;
    struct RastPort  *rp;
    struct IntuiText it;
           char      *buff;
    
/* Try to Open Intuition and if successful, make a 'status' window */

    if(StatusWindo) CloseWindow(StatusWindo); StatusWindo=NULL;
    if(!IntuitionBase)
    {
       IntuitionBase = (struct IntuitionBase *)
                       OpenLibrary("intuition.library",REVISION);
    }
    if(!IntuitionBase) quit("");
    
    w.LeftEdge    =    0L;
    w.TopEdge     =    0L;


/* Figure out how big the window should be from the font size, Since */
/* under 2.0 this might NOT be GfxBase->DefaultFont, we have to check */
/* the WorkBench Screen font. Height is easier, it's in s->BarHeight */

    if(!(buff=SafeAllocMem((long)sizeof(struct Screen),MEMF_PUBLIC)))
       quit("");
    GetScreenData(buff,(long)sizeof(struct Screen),WBENCHSCREEN,NULL);
    if(buff==NULL) quit("");
    s             = (struct Screen *)buff;


/* Need to use a dummy IntuiText Struct so we can use IntuiTextLength() */

    it.ITextFont  = s->Font;
    it.IText      = (UBYTE *)title;
    w.Width       = IntuiTextLength(&it)+10;
    if(w.Width>s->Width) w.Width=s->Width;
    w.Height      = s->BarHeight+1;
    FreeMem(buff,(long)sizeof(struct Screen));

    w.DetailPen   =  0x01;
    w.BlockPen    =  0x03;
    w.Title       = (UBYTE *)title;
    w.Flags       = SMART_REFRESH|NOCAREREFRESH|ACTIVATE|WINDOWDRAG;
    w.IDCMPFlags  = VANILLAKEY;   /* So we can get <CTRL> 'c' message */
    w.Type        = WBENCHSCREEN;
    w.FirstGadget = NULL;
    w.CheckMark   = NULL;
    w.Screen      = NULL;
    w.BitMap      = NULL;
    w.MinWidth    = 0;
    w.MinHeight   = 0;
    w.MaxWidth    = 0;
    w.MaxHeight   = 0;
    StatusWindo = OpenWindow(&w);


/* Abort ONLY if cycles = 0 (loop) AND the window failed to open. */
/* This set of conditions would otherwise result in an Endless and */
/* Escape-proof loop. If cycles != 0, sound will end, sometime... */

    if(!StatusWindo) {statusline=0; if(cycles==0L) quit("");}
}


/* This routine de-compresses Fibonacci Delta data, 1 byte at a time */
/* x is the top nibble, y is the bottom. You get 2, 2, 2 bytes from one! */

short decomp(sbuff,i,j,zz)
char *sbuff;
long i,j;
short zz;
{
short x,y;

   x=((sbuff[i])>>4)&15;          y=(sbuff[i])&15;
   sbuff[j]=comptable[x]+zz;        zz=sbuff[j];
   sbuff[j+1L]=comptable[y]+zz;  return(sbuff[j+1L]);
}



/******** Load SoundFile 'sPath' & set cycles-sps-stereo *******/

void loadSound(sPa)
char    *sPa;
{
   struct FileInfoBlock *finfo=NULL;
   struct FileLock      *lock=NULL;
          long          i, j;
          char          string[5], sPath[80];
          short         x, y, z;

/* Allocate 256 bytes as work memory */

   strcpy(sPath,sPa);
   if(!(sbuffer=SafeAllocMem(256L,MEMF_CLEAR|MEMF_PUBLIC)))
        quit("No Work Memory!");

/* Check for and parse IFF data in first 256 bytes of file */

   if(!(sFile=(struct Filehandle *)Open(sPath,MODE_OLDFILE)))
   {
      sactual=256L;      quit("Can't Find SoundFile!");
   }
   Read((BPTR)sFile,sbuffer,256L);        /* load the 1st 256 bytes */
   for(sstart=0L, sps=0L, i=0L; i<252L; i+=4L)
   {
      strncpy(string,sbuffer+i,4);   string[4]=NULL;
      if(!(strcmp(string,"VHDR")))        /* get samples per second */
      {
         for(j=0L;j<(long)((UBYTE)sbuffer[i+20]);j++) sps+=256L;
         sps += (long)((UBYTE)sbuffer[i+21L]);
         if(sbuffer[i+23L]==1) compflag=1;
         if(sbuffer[i+27L]!=1)
            startvol=(64L*(long)(sbuffer[i+28L]*10+sbuffer[i+29L]))/100L;
      }
      if(!(strcmp(string,"CHAN")))            /* Channel Assignment */
      {
         if((sbuffer[i+7L]==6)||(sbuffer[i+11L]==6)) stereo=1;
      }
      if(!(strcmp(string,"BODY")))        /* get size of sound data */
      {
         for(j=0L;j<4L;j++)
            sactual+=((long)((UBYTE)sbuffer[i+7L-j])<<(8*j));
         sstart = i+8L; i=252L;
      }
   }

/* if not in IFF format, get filesize from FileInfoBlock */

   if(!sactual)
   {

/* Allocate a file info block, get size from it, and de-allocate */

      if((!(finfo=(struct FileInfoBlock *)
         SafeAllocMem((long)sizeof(struct FileInfoBlock),MEMF_CLEAR)))
       ||(!(lock=(struct FileLock *)Lock(sname,ACCESS_READ)))
       ||(!(Examine((BPTR)lock,finfo))) )
                    quit("FileInfoBlock Problem!");
      sactual = finfo->fib_Size;      if(lock) UnLock((BPTR)lock);
      if(finfo) FreeMem(finfo,(long)sizeof(struct FileInfoBlock));
   }

/* clean up work area */

   FreeMem(sbuffer,256L); sbuffer=NULL;
   if(compflag) sactual=sactual*2L;

/* Allocate memory for SOUND data. */
/* Later we'll transfer in BUFSIZE chunks to contiguous CHIP memory. */

   if(AvailMem(MEMF_LARGEST)<(sactual+16384L)) direct=1;
   if(direct) return();
   if(!(sbuffer=SafeAllocMem(sactual,MEMF_CLEAR|MEMF_PUBLIC)))
              quit("Memory Allocation Failed!");

/* Load the data into sbuffer */

   Seek((BPTR)sFile,sstart,OFFSET_BEGINNING);
   if((Read((BPTR)sFile,sbuffer,sactual)) == -1L) quit("Read Error!");
   Close((BPTR)sFile);  sFile=NULL;

/* decompression */

   if(compflag)
   {
      if(statusline) 
      {
         strcpy(title,"Processing Fibonacci Delta Compression");
         setStatusWindo();
      }
      if(stereo)
      {
         memcpy(sbuffer+(3L*sactual)/4L,sbuffer+(sactual/4L),sactual/4L);
         memcpy(sbuffer+(sactual/4L),sbuffer,sactual/4L);
         j=1L;  sbuffer[0]=sbuffer[j];  z=sbuffer[j];
         for(i=sactual/4L+2L;i<(sactual/2L);i++,j+=2L)
            z=decomp(sbuffer,i,j,z);
         j=sactual/2L; sbuffer[j]=sbuffer[(3L*sactual)/4L+1L];
         z=sbuffer[j];  j++;
         for(i=(3L*sactual)/4L+2L;i<sactual;i++,j+=2L)
            z=decomp(sbuffer,i,j,z);
      }
      else
      {
         memcpy(sbuffer+(sactual/2L),sbuffer,sactual/2L);

/* 1st byte is pad, 2nd byte is 1st value. FOR EACH STEREO SIDE! */

         for(i=sactual/2L+2L,j=1L,z=sbuffer[1];i<sactual;i++,j+=2L)
            z=decomp(sbuffer,i,j,z);
      }
   }
}


void maketitle()
{
long i, j;
   if((fade!=0L)||(fadevol!=0L)) strcpy(title,"Fading ");
   else strcpy(title,"");
   strcat(title,sname);   strcat(title," at ");
   strcat(title,ltoa(sps)); strcat(title," samples per second");
   if(compflag) strcat(title," compressed");
   if(stereo&&left&&right) strcat(title," in stereo");
   if(!fade) {strcat(title,". Volume "); strcat(title,ltoa(vol));}
   j=strlen(title);
   if(j>79L) for(i=0L;i<80L;i++) title[i]=title[i+j-79L];
   title[79]=0;
}


/*****************  make a noise ******************/

void soundSound()
{
    ULONG   class;
    LONG    h, i, j, dactual, dlength, remaining;
    USHORT  code, count;
    short   z;

/* Make sure we have valid values before 'sounding' */

    if(left==right) left=right=1;
    if((sps<56L)||(sps>65534L)) sps=10000L;
    if((startvol<1L)||(startvol>64L)) startvol=64L;
    vol=startvol;
    if((endvol<1L)||(endvol>64L)) endvol=64L;
    if(direct)
    {
       BUFSIZE=AvailMem(MEMF_CHIP|MEMF_LARGEST)/6L;
       if(BUFSIZE>64000L) BUFSIZE=64000L;
       BUFSIZE-=(BUFSIZE%8L);
    }

/* cycles<56 limit only applies to CLI */

    if((cycles<0L)||(cycles>65535L)) cycles=1L;
    if(cycles>1L) fadevol=(endvol-startvol)/(cycles-1L);
    else fadevol=0L;

/* Put up a 'status' window on the top line. */

    if(statusline) {maketitle(); setStatusWindo();}

/* Allocate sound data buffers from CHIP memory. Ports and */
/* Audio Request Structures do NOT require CHIP memory */

   for(k=0;k<4;k++)
   {
     if(!(cbuf[k]=SafeAllocMem(BUFSIZE,
           MEMF_CHIP|MEMF_CLEAR|MEMF_PUBLIC))) quit("No CHIP Memory!");
     if(!(sound[k]=(struct IOAudio *)SafeAllocMem((long)sizeof(struct IOAudio),
                     MEMF_CLEAR|MEMF_PUBLIC))) quit("No IOA Memory!");
     if(!(sound[k]->ioa_Request.io_Message.mn_ReplyPort =
                  CreatePort(portname[k],0L))) quit("No Port Memory!");
   }

/* Open Audio using the first IOAudio as the 'initializer' request */

   sound[0]->ioa_Request.io_Message.mn_Node.ln_Pri = 114;
   if(!right) sound[0]->ioa_Data   = &sunitL[0];
   else if(!left) sound[0]->ioa_Data   = &sunitR[0];
   else sound[0]->ioa_Data   = &sunit[0];
   sound[0]->ioa_Length = 4L;
   if((OpenDevice(AUDIONAME,0L,(struct IORequest *)sound[0],0L))!=NULL)
   {
      sound[0]->ioa_Request.io_Device=NULL;  /* don't AbortIO if no open */
      quit("No Audio Device!");
   }

/* Set all IOAudios. */

   for(k=0;k<4;k++)
   {
      sound[k]->ioa_Request.io_Message.mn_Node.ln_Pri = 114;
      sound[k]->ioa_Request.io_Command = CMD_WRITE;
      sound[k]->ioa_Request.io_Flags   = ADIOF_PERVOL;

/* Note copies of Device & AllocKey from initializer. */

      sound[k]->ioa_Request.io_Device  = sound[0]->ioa_Request.io_Device;
      sound[k]->ioa_AllocKey  = sound[0]->ioa_AllocKey;

/* Each IOAudio has its own CHIP buffer, Port, and Unit (left/right) */

      sound[k]->ioa_Data   = (UBYTE *)cbuf[k];

/* 3579547 divided by 55 = 65083, nearly the maximum Period (65535)  */
/* changing this 'magic' number is all we need to make it 'European' */

      if(GfxBase->DisplayFlags&PAL) sound[k]->ioa_Period=3546895L/sps;
                               else sound[k]->ioa_Period=3579547L/sps;

/* allow for volume setting. gives us effect possibilities in a script */

      sound[k]->ioa_Volume = startvol;

/* One time through this BUFSIZE (or smaller) part of the whole */

      sound[k]->ioa_Cycles = 1L;
   }

/* The compiler wants 'Unit' to be a structure, we just want to mask */
/* into the allocated left/right channels. left=1 or 8, right=2 or 4 */
/*        ...zap! You're a Unit structure! Feel any different?       */

   for(k=2;k>(-1);k-=2)
   {
      if(right) sound[k+1]->ioa_Request.io_Unit = (struct Unit *)
                      ((ULONG)(sound[0]->ioa_Request.io_Unit)&6L);
      if(left)    sound[k]->ioa_Request.io_Unit  = (struct Unit *)
                      ((ULONG)(sound[0]->ioa_Request.io_Unit)&9L);
   }

/* If in STEREO, split file. If in MONO, 'b' buffers use 'a' data */

   if(stereo) remaining=(sactual/2L)-(sactual&1L);
   else
   {
      remaining=sactual;
      sound[1]->ioa_Data   = (UBYTE *)cbuf[0];
      sound[3]->ioa_Data   = (UBYTE *)cbuf[2];
   }

/* dactual is the length of one channel's complete data */

   dactual=remaining;     k=count=0;

/* if DIRECT_FROM_DISK, start at the beginning of the sample */

   if(direct) Seek((BPTR)sFile,sstart,OFFSET_BEGINNING);

/* we be doing loops here */

   do
   {

/* be CERTAIN ioa_Length is an even number & set datalength */

      if(remaining>BUFSIZE) dlength=BUFSIZE;
      else {dlength=remaining; dlength-=(dlength&1L);}

/* Move the data into the proper CHIP buffer of BUFSIZE */

      if(direct)
      {
         if(stereo) Seek((BPTR)sFile,sstart+dactual-remaining,
                             OFFSET_BEGINNING);
         if(compflag)
         {
            Read((BPTR)sFile,cbuf[k]+dlength/2L,dlength/2L);
            if(remaining==dactual)
               {z=cbuf[k][1L+dlength/2L]; cbuf[k][0]=z; i=2L; j=1L;}
            else {i=0L; j=0L;}
            for(;i<(dlength/2L);i++,j+=2L)
               z=decomp(cbuf[k],i+dlength/2L,j,z);
         }
         else Read((BPTR)sFile,cbuf[k],dlength);
      }
      else movmem(sbuffer+(dactual-remaining),cbuf[k],(int)dlength);

/* Don't load or use the right CHIP buffers if MONO. Saves time. */

      if(stereo)
      {
         if(direct)
         {
            if(compflag)
            {
               Seek((BPTR)sFile,sstart+dactual+dactual/2L-remaining,
                      OFFSET_BEGINNING);
               Read((BPTR)sFile,cbuf[k+1]+dlength/2L,dlength/2L);
               if(remaining==dactual)
                  {z=cbuf[k+1][1L+dlength/2L]; cbuf[k+1][0]=z; i=2L; j=1L;}
               else {i=0L; j=0L;}
               for(;i<(dlength/2L);i++,j+=2L)
                  z=decomp(cbuf[k+1],i+dlength/2L,j,z);
            }
            else
            {
               Seek((BPTR)sFile,sstart+sactual-remaining,OFFSET_BEGINNING);
               Read((BPTR)sFile,cbuf[k+1],dlength);
            }
         }
         else movmem(sbuffer+(sactual-remaining),cbuf[k+1],(int)dlength);
      }

/* Data has been moved, so adjust 'remaining' */

      remaining-=dlength;

/* Left and Right Lengths are the same, no matter what! */

      sound[k]->ioa_Length = sound[k+1]->ioa_Length = dlength;

/* Start one set of Left/Right Channels. */

      if(left)  BeginIO((struct IORequest *)sound[k]);
      if(right) BeginIO((struct IORequest *)sound[k+1]);

/* If no QUIET flag, Check Intuition for the ABORT message */

      if(statusline)
      {
      while(message=(struct IntuiMessage *)GetMsg(StatusWindo->UserPort))
         {
            class = message->Class;  code = message->Code;

/* Hi Intuition! Thanks for the message, Have a nice day! */

            ReplyMsg((struct Message *)message);

/* <CTRL> 'c' abort.  1,2,3, easy as a,b,c, baby you and me! */

            if((class==VANILLAKEY)&&(code==3)) quit("");
         }
      }
      if(fade!=0L)
         {vol=(64L*remaining)/dactual;  if(fade>0L) vol=64L-vol;}

/* Is this the last time AND the last cycle? If yes & no, reset. */

      if(remaining<2L)
      {
         cycles--;
         if(cycles!=0L)
         {
            if(direct) Seek((BPTR)sFile,sstart,OFFSET_BEGINNING);
            remaining=dactual; dlength=BUFSIZE;
            if(fadevol!=0L)
            {
               vol=vol+fadevol;
               if(statusline) {maketitle(); setStatusWindo();}
            }
         }
      }
      if(vol>64L) vol=64L;  if(vol<1L) vol=1L;
      for(i=0L;(i<4L)&&(vol>=0L);i++) sound[i]->ioa_Volume = vol;

/* Is this the last time, or what? */

      if(remaining<2L)
         WaitIO((struct IORequest *)sound[k+right]); /* wait for LAST request */
      else
      {
         if(k) k=0; else k=2;    /* switch buffers & wait for PREVIOUS */
         if(count++) WaitIO((struct IORequest *)sound[k+right]);
      }

/* Keep going until we run out of data */

   } while(remaining>1L);                   /* End of Loop */
}


/********** long int to char string (up to 10 digits) ***********/

char *ltoa(num)
          long  num;
{
   static char ostring[11]="";
          short  next = 10,  shift = 0;
   
   if (!num) ostring[next--] = '0';
   while ((num+9L)/10)
      { ostring[next--] = num % 10L + '0';   num /= 10L; }
   next+=1;
   while(next<11)       ostring[shift++] = ostring[next++];
   while(shift<11)      ostring[shift++] = '\0';
   return(ostring);
}


/************************  MAIN  ****************************/

main(argc,argv)
 int           argc;
 char          *argv[];
{
    long      temp=0L;
    short     i=0, WBargs=0;
    char      string[10];
    
    GfxBase = (struct GfxBase *)
                       OpenLibrary("graphics.library",REVISION);
    if(!GfxBase) quit("Dale's not here!");
    if(argc==1) {help=TRUE; quit(USAGE);}    /* No SOUNDfile so quit */

/* If Called From CLI */

    if(argc>1)
    {
       strcpy(sname,argv[1]);   /* 2nd arg MUST be filename */
       for(i=2;i<argc;i++)
       {
          if(argv[i][0]=='d'||argv[i][0]=='D') direct=1;
          else if(argv[i][0]=='q'||argv[i][0]=='Q') statusline=0;
       }
       loadSound(sname);         /* Load 'em up! */
       for(i=2;i<argc;i++)
       {
          if(argv[i][0]=='s'||argv[i][0]=='S')
          {
             if(stereo) stereo=0; else stereo = 1;
          }
          else if(argv[i][0]=='v'||argv[i][0]=='V') startvol=atol(argv[i]+1L);
          else if(argv[i][0]=='l'||argv[i][0]=='L')    right=0;
          else if(argv[i][0]=='r'||argv[i][0]=='R')     left=0;
          else if(argv[i][0]=='e'||argv[i][0]=='E')   endvol=atol(argv[i]+1L);
          else if(argv[i][0]=='f'||argv[i][0]=='F')    {fade=-1L;  endvol=1L;}
          else if(argv[i][0]=='g'||argv[i][0]=='G')    {fade=1L; startvol=1L;}

/* must be sps or cycles, size of arg determines which one */

          else if((argv[i][0]>='0')&&(argv[i][0]<='9'))
          {
             temp = atol(argv[i]);
             if(temp<56L)  cycles = temp;    else  sps = temp;
          }
       }
       soundSound();   quit("");
    }


/* If WorkBench, try to get info from the .info file */

    if(WBenchMsg->sm_NumArgs>1) WBargs=1;
    while((argc==0)&&(WBenchMsg->sm_NumArgs>WBargs)) /*  from WorkBench  */
    {
       argp=(WBenchMsg->sm_ArgList)+WBargs;      /*  CD to lock and  */
       if(lock=(struct FileLock *)argp->wa_Lock)
          savelock=(struct FileLock *)CurrentDir((BPTR)lock);
       strcpy(sname,argp->wa_Name);              /* get the filename */
       if(!IconBase)
          IconBase=(struct IconBase *)OpenLibrary("icon.library",REVISION);
       if((!IconBase) ||   
            ((infofile=(struct DiskObject *)GetDiskObject(sname))==NULL))
                quit("");

/* Valid DIRECT-FROM-DISK strings will have o or O as the first letter. */

       strncpy(string,(char *)FindToolType(infofile->do_ToolTypes,
                       "DIRECT_FROM_DISK"),4);
       if(string[0]=='o'||string[0]=='O')
       {
          if(string[1]=='n'||string[1]=='N') direct = 1;
          else direct = 0;
       }
       loadSound(sname);

/* then check 'ToolTypes' to set cycles, stereo, & samples per second. */
/* Note use of strncpy to copy only what we need. This is an attempt to */
/* take care of a .info editing bug which may cause extra long strings. */

       strncpy(string,(char *)FindToolType(infofile->do_ToolTypes,
                       "CYCLES"),6);

/* We do this check for '0' since atol() thinks ' ', or NULL = '0'  */
/* But if data is blank, or NULL, we want cycles set to 1 (default) */

       if(string[0]=='0') cycles = 0L;
       temp=atol(string);  if(temp) cycles = temp;

/* Valid STEREO strings will have o or O as the first letter. */

       strncpy(string,(char *)FindToolType(infofile->do_ToolTypes,
                       "STEREO"),4);
       if(string[0]=='o'||string[0]=='O')
       {
          if(string[1]=='n'||string[1]=='N')   stereo = 1;
           else  stereo = 0;
       }

/* Valid FADE strings are "GROW" or "FADE".   END_VOLUME 1-64 */

       strncpy(string,(char *)FindToolType(infofile->do_ToolTypes,
                       "EFFECT"),5L);
       if(!(strncmp(string,"GROW",4L))) {fade=1L; vol=1L;}
       else if(!(strncmp(string,"FADE",4L))) fade=-1L;
       strncpy(string,(char *)FindToolType(infofile->do_ToolTypes,
                       "VOLUME"),3L);
       if(strlen(string)) vol=atol(string);
       strncpy(string,(char *)FindToolType(infofile->do_ToolTypes,
                       "END_VOLUME"),3L);
       if(strlen(string)) endvol=atol(string);

/* Set left or right channel only (for special effects) */

       strncpy(string,(char *)FindToolType(infofile->do_ToolTypes,
                       "CHANNEL"),4L);
       if(string[0]=='l'||string[0]=='L') right=0;
       if(string[0]=='r'||string[0]=='R')  left=0;

/* We'll take any old sps because we check for validity before playing */

       strncpy(string,(char *)FindToolType(infofile->do_ToolTypes,
                       "SAMPLES_PER_SECOND"),6);
       temp = atol(string);    if(temp) sps = temp;

       soundSound();                             /* MAKE THAT NOISE! */

          /* restore current DIR */
       if(lock) CurrentDir((BPTR)savelock);  lock=NULL;

/* Then release everything you've used here. (Be a tidy camper.) */

       cleanup("");    WBargs++;
    }
    exit(RETURN_OK);
}

/************************** end of Sound.c ******************************/
