
/**************************************************************************/
/*			     DSound V1.30				  */
/*     Copyright 1991-1993 by Dave Schreiber, All Rights Reserved	  */
/*									  */
/* To compile with SAS/C version 6, type:				  */
/*    smake								  */
/*									  */
/* Revision history:							  */
/*    V1.30  - DSound now displays the name of the sound sample being	  */
/*	       played, the number of seconds of the sample that have been */
/*	       played, and the total number of seconds in the sample.	  */
/*	       A bug that prevent DSound from being aborted from the	  */
/*	       window when a stereo sample is played has also been fixed. */
/*	       July 17, 1993						  */
/*    V1.20  - Added the ability to stop DSound by typing CTRL-C, and	  */
/*	       added the switch '-w', which keeps the DSound window from  */
/*	       opening. 						  */
/*	       August 23, 1992						  */
/*    V1.10  - Added the ability to play a sound sample repeatedly (in a  */
/*	       loop).							  */
/*	       July 11, 1992						  */
/*    V1.00  - Added a new module (Mem.c) which allows a sample to be     */
/*	       loaded entirely into memory, so samples can be played from */
/*	       floppy disk without first copying to a hard or RAM drive.  */
/*	       DSound also can now play a single channel of a stereo	  */
/*	       out of two speakers.  The small window, used to let the	  */
/*	       user abort a playing sample, as been redone (DSound also   */
/*	       now responds instantly when the user clicks on the Close   */
/*	       gadget).  DSound now checks a given 8SVX sample to make	  */
/*	       sure that it is actually a valid sample.  Finally, DSound  */
/*	       has been made pure (residentiable).                        */
/*	       Second release (April 16, 1992)                            */
/*    V0.94a - Can now play a mono sample out of both speakers at the	  */
/*	       same time (using the -2 switch).                           */
/*	       March 27, 1992 (a little later)                            */
/*    V0.93a - Now handles stereo sound samples.  Either the right or	  */
/*	       left, or both, stereo channels can be played.  Also split  */
/*	       off the code that actually plays the sound sample into a   */
/*	       separate source file (Play.c).                             */
/*	       March 27, 1992						  */
/*    V0.92a - Now gets the length of the sound sample from the head of   */
/*	       the BODY chunk, instead of the VHDR (a workaround to a bug */
/*	       in the Perfect Sound software that would sometimes store   */
/*	       an incorrect length in the VHDR chunk of a sound sample).  */
/*	       November 4, 1991 					  */
/*    V0.91a - First release (September 11, 1991)                         */
/**************************************************************************/

#include <exec/types.h>
#include <exec/exec.h>
#include <devices/audio.h>
#include <dos/dos.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfxbase.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include "dsound.h"

#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/exec.h>
#include <proto/dos.h>

char filename[256];

#define DEF_BUF_SIZE 0xFFFFFFFF

void InterpretArgs(int argc,char *argv[]);
BOOL noFilter=FALSE;
UBYTE volume=0;
UWORD speed=0;
ULONG bufSize=DEF_BUF_SIZE;

void filter_on(void);
void filter_off(void);
char *getDoubleDigit(unsigned int value,char *buf);

char *version="$VER: DSound V1.30 (17.7.93)";
char *copyright="Copyright 1991-1992 by Dave Schreiber, All Rights Reserved";

struct IntuitionBase *IntuitionBase=NULL;
struct GfxBase *GfxBase=NULL;

struct Window *window=NULL;

BPTR file=NULL;

channel audioChannel=UNSPECIFIED;
BOOL bothChan=FALSE;
BOOL readAll=FALSE;
BOOL loop=FALSE;
BOOL titleBarName=TRUE;
BOOL titleBarTime=TRUE;

/*The window definition*/
struct NewWindow newWindow = {
	0,0,
	60,56,
	0,1,
	CLOSEWINDOW,
	SMART_REFRESH|WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE,
	NULL,
	NULL,
	"DSound V1.30",
	NULL,
	NULL,
	5,5,
	640,200,
	WBENCHSCREEN
};

/*This determines whether or not the window will be opened*/
BOOL openTheWdw=TRUE;
ULONG signalMask=SIGBREAKF_CTRL_C;

struct TextFont *titleBarFont=NULL;

main(int argc,char *argv[])
{
   struct Voice8Header vhdr;
   UBYTE foo2[5];
   UBYTE foo[5];
   ULONG chan;
   ULONG sampleLength;
   ULONG lock;
   ULONG titleLength,nameLength,timeLength,colonLength,finalDelta;
   char *chanStr;

   filename[0]=NULL;

   /*Get and interpret the command-line arguments*/
   InterpretArgs(argc,argv);

   /*Exit if there was no sound sample specified*/
   if(filename[0]==NULL)
   {
      WriteMsg("Please specify the name of a sound sample\n");
      cleanup(75);
   }

   /*Open the file*/
   file=Open(filename,MODE_OLDFILE);
   if(file==NULL)
   {
      WriteMsg("Couldn't open the file\n");
      cleanup(100);
   }

   /*If the user hasn't told us not to open the window*/

   if(openTheWdw)
   {
      char temp[256];
      /*Open libraries*/
      GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",0L);
      IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",0L);

      if(GfxBase==NULL || IntuitionBase==NULL)
      {
	 WriteMsg("A shared library could not be opened\n");
	 cleanup(50);
      }

      /*Get the size of the title bar font in a rather illegal way	    */
      /*Note:  programmers at C= should put a GetDefTitleBarFontHeight()    */
      /*function into Intuition before complaining to me about the following*/
      /*code.								    */

      lock=LockIBase(0L);
      newWindow.Height=IntuitionBase->ActiveScreen->Font->ta_YSize+3;
      UnlockIBase(lock);

      window=OpenWindow(&newWindow);

      if(window==NULL)
	 cleanup(110);
      signalMask|=1<<window->UserPort->mp_SigBit;

      titleBarFont=OpenFont(IntuitionBase->ActiveScreen->Font);
      if(titleBarFont==NULL)
	 cleanup(115);

      SetFont(window->RPort,titleBarFont);
      finalDelta=titleLength=TextLength(window->RPort,"DSound V1.30",12);
      colonLength=TextLength(window->RPort,":  ",3);
      if(titleBarTime==FALSE)
      {
	 timeLength=0;
	 if(titleBarName==FALSE)
	    colonLength=0;
      }
      else
	 timeLength=TextLength(window->RPort,"(00:00/00:00) ",14);

      if(titleBarName==FALSE)
	 nameLength=0;
      else
      {
	 strcpy(temp,"\"\"");
	 strcat(temp,filename);
	 nameLength=TextLength(window->RPort,temp,strlen(temp));
      }

      if(60+finalDelta+nameLength+colonLength+timeLength >= window->WScreen->Width)
      {
	 titleBarName=FALSE;
	 if(60+finalDelta+colonLength+timeLength >= window->WScreen->Width)
	 {
	    titleBarTime=FALSE;
	    if(60+titleLength >= window->WScreen->Width)
	       cleanup(199);
	 }
	 else
	    finalDelta+=(colonLength+timeLength);
      }
      else
	 finalDelta+=(colonLength+timeLength+nameLength);

      SizeWindow(window,finalDelta,0);
   }

   /*Read the header*/
   Read(file,foo,4);
   Seek(file,4,OFFSET_CURRENT);
   Read(file,foo2,4);

   foo[4]=foo2[4]=NULL;

   /*Check the header's validity, more or less*/
   if((strcmp(foo,"FORM")!=0) || (strcmp(foo2,"8SVX")!=0))
   {
      WriteMsg("Not a valid IFF 8SVX sound sample file.\n");
      cleanup(120);
   }

   if(strcmp(FindChunk(file,"VHDR"),"VHDR")!=0)
   {
      WriteMsg("Couldn't find the 8SVX header (VHDR).\n");
      cleanup(130);
   }

   /*Skip past the chunk size*/
   Seek(file,4,OFFSET_CURRENT);

   /*Get the VHDR*/
   Read(file,&vhdr,sizeof(struct Voice8Header));

   /*If no buffer size was specified, use a buffer that can hold 1 second*/
   /*of sound*/
   if(bufSize==0xFFFFFFFF)
      bufSize=vhdr.samplesPerSec;

   /*Check for compression*/
   if(vhdr.sCompression!=0)
   {
      WriteMsg("Can't play a compressed sample!\n");
      cleanup(400);
   }

   /*Get the CHAN chunk (which will tell us if the sample is stereo, or,*/
   /*if it is mono, which speaker it should be played out of*/
   chanStr=FindChunk(file,"CHAN");
   if(strcmp(chanStr,"CHAN")==0)
   {
      /*Skip past chunk size*/
      Seek(file,4,OFFSET_CURRENT);
      Read(file,&chan,sizeof(long));

      /*The information we're looking for consists of one longword*/
      switch(chan)
      {
	 case 2:  /*Mono sample, left speaker*/
	    if(bothChan)
	       /*Play out of both channels if -2 used*/
	       audioChannel=MONO_BOTH;
	    else
	       if(audioChannel==UNSPECIFIED)
		  audioChannel=MONO_LEFT;
	    break;
	 case 4:  /*Mono sample, right speaker*/
	    if(bothChan)
	       /*Play out of both channels if -2 used*/
	       audioChannel=MONO_BOTH;
	    else
	       if(audioChannel==UNSPECIFIED)
		  audioChannel=MONO_RIGHT;
	    break;
	 case 6:     /*Stereo*/
	    switch(audioChannel)
	    {
	       /*This reconciles a user's choice with the fact that the*/
	       /*sample is in stereo*/

	       /*Play left stereo channel*/
	       case MONO_LEFT:
		  if(bothChan)
		     audioChannel=STEREO_LEFT_BOTH;
		  else
		     audioChannel=STEREO_LEFT;
		  break;

	       /*Play right stereo channel*/
	       case MONO_RIGHT:
		  if(bothChan)
		     audioChannel=STEREO_RIGHT_BOTH;
		  else
		     audioChannel=STEREO_RIGHT;
		  break;

	       /*Play both channels*/
	       case UNSPECIFIED:
		  audioChannel=STEREO;
		  break;
	    }
	    break;
      }

      /*Find the start of the BODY chunk*/
      chanStr=FindChunk(file,"BODY");
   }
   else
   {
      chan=0;
      if(bothChan)
	 audioChannel=MONO_BOTH;
   }

   if(strcmp(chanStr,"BODY")!=0)
   {
      WriteMsg("Couldn't find body of sample.\n");
      cleanup(140);
   }

   if(noFilter)
      filter_off();

   /*Get the length of the sample*/
   Read(file,(char *)&sampleLength,4);

   SetSignal(0,0);

   /*Play the sample by choosing the appropriate player function*/
   switch(audioChannel)
   {
      case MONO_LEFT:
      case MONO_RIGHT:
      case UNSPECIFIED:
	 /*Simple mono playback*/
	 playMonoSample(file,audioChannel,&vhdr,sampleLength);
	 break;
      case MONO_BOTH:
	 /*Mono playback using both speakers*/
	 playMonoTwice(file,audioChannel,&vhdr,sampleLength);
	 break;
      case STEREO_RIGHT:
	 /*Right stereo channel*/
	 audioChannel=MONO_RIGHT;
	 Seek(file,sampleLength/2,OFFSET_CURRENT);
	 playMonoSample(file,audioChannel,&vhdr,sampleLength/2);
	 break;
      case STEREO_RIGHT_BOTH:
	 /*Right stereo channel out of both speakers*/
	 audioChannel=MONO_RIGHT;
	 Seek(file,sampleLength/2,OFFSET_CURRENT);
	 playMonoTwice(file,audioChannel,&vhdr,sampleLength/2);
	 break;
      case STEREO_LEFT:
	 /*Left stereo channel*/
	 audioChannel=MONO_LEFT;
	 playMonoSample(file,audioChannel,&vhdr,sampleLength/2);
	 break;
      case STEREO_LEFT_BOTH:
	 /*Left stereo channel out of both speakers*/
	 audioChannel=MONO_LEFT;
	 playMonoTwice(file,audioChannel,&vhdr,sampleLength/2);
	 break;
      case STEREO:
	 /*Stereo sample (both channels)*/
	 playStereoSample(file,audioChannel,&vhdr,sampleLength/2,filename);
	 break;
   }

   if(noFilter)
      filter_on();

   /*Free allocated resources and exit*/
   cleanup(0);
}



/* Get an audio channel */
struct IOAudio *GetAudioChannel(ULONG bufferSize,UBYTE *allocationMap)
{
   struct IOAudio *aIOB;
   void *audioBuf;
   struct Port *aPort;

   aPort=(struct Port *)CreatePort("dsound",0);
   if(aPort==NULL)
      return(NULL);

   /* Allocate the chip memory buffer for the channel */
   audioBuf=(void *)AllocMem(bufferSize,MEMF_CHIP);
   if(audioBuf==NULL)
   {
      DeletePort((struct MsgPort *)aPort);
      return(NULL);
   }

   /* Allocate an IOAudio structure*/
   aIOB=(struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC|MEMF_CLEAR);
   if(aIOB==NULL)
   {
      DeletePort((struct MsgPort *)aPort);
      FreeMem(audioBuf,bufferSize);
      return(NULL);
   }

   /* Set up the IOAudio to allocate the command channel */
   aIOB->ioa_Request.io_Message.mn_Node.ln_Pri=0;
   aIOB->ioa_Request.io_Message.mn_ReplyPort=(struct MsgPort *)aPort;

   aIOB->ioa_Data=allocationMap;
   aIOB->ioa_Length=4;
   aIOB->ioa_Request.io_Command=ADCMD_ALLOCATE;

   /*Open the audio device*/
   OpenDevice("audio.device",0,(struct IORequest *)aIOB,0);


   if(aIOB->ioa_AllocKey==0)
   {  /*There was an error*/
      DeletePort((struct MsgPort *)aPort);
      FreeMem(audioBuf,bufferSize);
      FreeMem(aIOB,sizeof(struct IOAudio));
      return(NULL);
   }
   else
   {
      /* Set up the IOAudio for writes */
      aIOB->ioa_Request.io_Command=CMD_WRITE;
      aIOB->ioa_Request.io_Flags=ADIOF_PERVOL;
      aIOB->ioa_Data=audioBuf;
      aIOB->ioa_Length=bufferSize;
      return(aIOB);
   }
}

/* Free an allocated audio channel */
void FreeAudioChannel(struct IOAudio *aIOB)
{
   if(aIOB==NULL)
      return;

   /* Free the audi obuffer */
   if(aIOB->ioa_Data!=NULL)
      FreeMem(aIOB->ioa_Data,aIOB->ioa_Length);

   /* Free the audio channel */
   aIOB->ioa_Request.io_Command=ADCMD_FREE;
   BeginIO((struct IORequest *)aIOB);
   WaitIO((struct IORequest *)aIOB);
   DeletePort(aIOB->ioa_Request.io_Message.mn_ReplyPort);

   /* Close the audio channel */
   CloseDevice((struct IORequest *)aIOB);

   /* Free the IOAudio structure */
   FreeMem(aIOB,sizeof(struct IOAudio));
   return;
}

/* Initialize an IOAudio's volume, period, and set the number of cycles */
/* to one */
void InitAudioChannel(struct IOAudio *aIOB,UWORD volume,UWORD period)
{
   aIOB->ioa_Period=period;
   aIOB->ioa_Volume=volume;
   aIOB->ioa_Cycles=1;
   return;
}

/* Duplicate an IOAudio structure */
struct IOAudio *DuplicateAudioChannel(struct IOAudio *OrigIOB)
{
   struct IOAudio *aIOB;
   void *audioBuf;

   if(OrigIOB==NULL)
      return(NULL);

   /* Allocate the alternate buffer */
   audioBuf=(void *)AllocMem(OrigIOB->ioa_Length,MEMF_CHIP);
   if(audioBuf==NULL)
      return(NULL);

   /*Allocate the IOAudio structure*/
   aIOB=(struct IOAudio *)AllocMem(sizeof(struct IOAudio),MEMF_PUBLIC|MEMF_CLEAR);
   if(aIOB==NULL)
   {
      FreeMem(audioBuf,OrigIOB->ioa_Length);
      return(NULL);
   }

   /*Copy the original IOAudio's contents to the new one*/
   CopyMem(OrigIOB,aIOB,sizeof(struct IOAudio));

   /*Except for the buffer pointer, of course*/
   aIOB->ioa_Data=audioBuf;

   return(aIOB);
}

/*Delete a duplicated IOAudio*/
void DeleteDuplication(struct IOAudio *aIOB)
{
   if(aIOB != NULL)
   {
      /* Free the memory for the buffer and IOAudio */
      if(aIOB->ioa_Data != NULL)
	 FreeMem(aIOB->ioa_Data,aIOB->ioa_Length);
      FreeMem(aIOB,sizeof(struct IOAudio));
   }
   return;
}

/* Load an IOAudio's buffer from an open file */
ULONG LoadAudioBuffer(BPTR file,struct IOAudio *aIOB,ULONG toRead)
{
   if(toRead==0)
      return(0);

   if(file==0L)
      getLeft(aIOB->ioa_Data);
   else if(file==4L)
      getRight(aIOB->ioa_Data);
   else
      aIOB->ioa_Length=Read(file,aIOB->ioa_Data,toRead);
   return(aIOB->ioa_Length);
}

/*Find the beginning of an IFF chunk.  This routine will search for that*/
/*chunk's name, and if found, will leave the file cursor at the chunk size*/
/*field.  If the chunk isn't found, the file cursor will be left at the*/
/*size field of the BODY chunk, if there was one*/
char *FindChunk(BPTR file,char *string)
{
   static char buf[5];
   long len,actLen;
   buf[4]=NULL;

   actLen=Read(file,buf,4);
   while(strcmp(string,buf)!=0 && strcmp(buf,"BODY")!=0 && actLen > 0)
   {
      Read(file,(char *)&len,4);
      Seek(file,len,OFFSET_CURRENT);
      actLen=Read(file,buf,4);
   }
   return(buf);
}

/* Interpret the command line arguments */
void InterpretArgs(int argc,char *argv[])
{
   int i;

   for(i=1;i<argc;i++)
   {
      if(argv[i][0]=='-')
	 switch(argv[i][1])
	 {
	    /*Deactivate title bar filename*/
	    case 'n':
	    case 'N':
	       titleBarName=FALSE;
	       break;

	    /*Deactivate title bar time*/
	    case 't':
	    case 'T':
	       titleBarTime=FALSE;
	       break;

	    /* Read the entire sample into memory before playing */
	    case 'm':
	    case 'M':
	       readAll=TRUE;
	       break;

	    /* Use the left channel */
	    case 'l':
	    case 'L':
	       audioChannel=MONO_LEFT;
	       break;

	    /* Use the right channel */
	    case 'r':
	    case 'R':
	       audioChannel=MONO_RIGHT;
	       break;

	    /*Play a mono sample out of both speakers*/
	    case '2':
	       bothChan=TRUE;
	       break;

	    /* Switch off the low-pass filter while the sample is playing */
	    case 'f':
	    case 'F':
	       noFilter=TRUE;
	       break;

	    /* Play a sample at a given speed */
	    case 's':
	    case 'S':
	       speed=atol(&argv[i][2]);
	       if(speed > 28000)
		  speed=0;
	       break;

	    /* The volume at which the sample should be played */
	    case 'v':
	    case 'V':
	       volume=atol(&argv[i][2]);
	       if(volume > 64)
		  volume=0;
	       break;

	    /* The size of the chip RAM buffers */
	    case 'b':
	    case 'B':
	       bufSize=(atol(&argv[i][2])+1)&(~1);
	       if(bufSize==0)
		  bufSize=DEF_BUF_SIZE;
	       break;

	    /* Loop the sample */
	    case 'o':
	    case 'O':
	       loop=TRUE;
	       break;

	    case 'w':
	    case 'W':
	       openTheWdw=FALSE;
	       break;
	 }
      else if(argv[i][0]=='?')
      {
	 /*On-line help*/
	 WriteMsg("DSound V1.30 ©1991-1993 by Dave Schreiber\n");
	 WriteMsg("Usage:\n");
	 WriteMsg("  DSound <options> <filename>\n");
	 WriteMsg("Where the options are:\n");
	 WriteMsg("  -l -- Play the sample using the left speaker\n");
	 WriteMsg("  -r -- Play the sample using the right speaker\n");
	 WriteMsg("  -2 -- Play the sample using both speakers\n");
	 WriteMsg("  -f -- Shut off the low-pass filter\n");
	 WriteMsg("  -m -- Load the entire sample into memory\n");
	 WriteMsg("  -o -- Play the sample continuously (loop)\n");
	 WriteMsg("  -w -- Do not open the DSound window\n");
	 WriteMsg("  -n -- Do not show the sample name in the window\n");
	 WriteMsg("  -t -- Do not show the sample times in the window\n");
	 WriteMsg("  -s<speed> -- Play the sample at the given speed (samples/sec)\n");
	 WriteMsg("  -v<volume> -- Play the sample at the given volume (0-64)\n");
	 WriteMsg("  -b<bufsize> -- Use a buffer of size <bufsize> (default is 30K)\n");
	 exit(0);
      }
      else     /*Otherwise, the argument is a filename */
	 strcpy(filename,argv[i]);
   }
}

/*Update the sample information in the DSound window's title bar*/
/*This information can included the name of the sample, along with*/
/*its length and amount played (both in seconds)*/
void updateSampleInfo(unsigned int currentPos,unsigned int length,
		      unsigned int sampleRate)
{
   unsigned int currentSeconds,currentMinutes,totalSeconds,totalMinutes;
   static char windowTitle[256];
   char tempBuf[4][4];
   static BOOL nameAlreadyDisplayed=FALSE;

   /*If the user wants neither the name nor time printed, do nothing*/
   /*Also do nothing if the window isn't open*/
   if((titleBarTime==FALSE && titleBarName==FALSE) || window==NULL)
      return;

   /*Return if the user just wanted the name displayed, and it has been*/
   if(titleBarTime==FALSE && nameAlreadyDisplayed==TRUE)
      return;

   /*Beginning of the title*/
   strcpy(windowTitle,"DSound V1.30:  ");

   /*If the user wants the sample name displayed, put it in the buffer*/
   if(titleBarName)
   {
      nameAlreadyDisplayed=TRUE;
      sprintf(&windowTitle[strlen(windowTitle)],"\"%s\" ",filename);

      /*The name has been (or will be shortly) displayed, so don't */
      /*update it again if you don't have to */
      nameAlreadyDisplayed=TRUE;
   }

   /*Likewise for the time left and the total time*/
   if(titleBarTime)
   {
      /*Get the total time*/
      totalSeconds=(length)/sampleRate;
      totalMinutes=totalSeconds/60;
      totalSeconds-=totalMinutes*60;

      /*Get the current time*/
      currentSeconds=(currentPos)/sampleRate;
      currentMinutes=currentSeconds/60;
      currentSeconds-=currentMinutes*60;

      /*Create the string that holds the time, and put it in the*/
      /*title bar buffer*/
      sprintf(&windowTitle[strlen(windowTitle)],"(%s:%s/%s:%s)",
		     getDoubleDigit(currentMinutes,tempBuf[0]),
		     getDoubleDigit(currentSeconds,tempBuf[1]),
		     getDoubleDigit(totalMinutes,tempBuf[2]),
		     getDoubleDigit(totalSeconds,tempBuf[3]));
   }
   Forbid();
      /*Make sure that the screen isn't locked (e.g. holding down the*/
      /*RMB will lock the screen), so that DSound will continue to */
      /*play even if it can't update the title bar*/
      if(window->WScreen->LayerInfo.Lock.ss_NestCount==0)
	 SetWindowTitles(window,windowTitle,NULL);
   Permit();

   return;
}

char *getDoubleDigit(unsigned int value,char *buf)
{
   if(value<10)
      sprintf(buf,"0%d",value);
   else
      sprintf(buf,"%d",value);

   return(buf);
}

/*Switch on the low-pass filter */
void filter_on()
{
   *((char *)0x0bfe001)&=0xFD;
}

/*Switch off the low-pass filter*/
void filter_off()
{
   *((char *)0x0bfe001)|=0x02;
}

/*Write a message to the CLI*/
void WriteMsg(char *errMsg)
{
   Write(Output(),errMsg,strlen(errMsg));
}

/*Take a file handle and that handle's filename, and open that file again*/
/*The position in the second file in set to the position in the first */
/*file (so that the two file handles are essentially identical)*/
/*This requires that the first file was opened in a shared mode, like */
/*MODE_OLDFILE*/
BPTR dupFileHandle(BPTR origFile,char *filename)
{
   BPTR dupFile;

   dupFile=Open(filename,MODE_OLDFILE);

   if(dupFile==NULL)
      return(NULL);

   Seek(dupFile,getPosInFile(origFile),OFFSET_BEGINNING);
   return(dupFile);
}

/*Get the current position in a file*/
ULONG getPosInFile(BPTR file)
{
   LONG position;

   position=Seek(file,0,OFFSET_CURRENT);
   return((ULONG)position);
}

/* Free allocated resources */
void cleanup(int err)
{
   /*If the entire sample was read into memory, this will delete whatever*/
   /*part of the sample still remains in memory*/
   deleteLeft();
   deleteRight();

   if(file!=NULL)
      Close(file);

   if(window!=NULL)
      CloseWindow(window);

   if(titleBarFont!=NULL)
      CloseFont(titleBarFont);

   if(GfxBase!=NULL)
      CloseLibrary((struct Library *)GfxBase);

   if(IntuitionBase!=NULL)
      CloseLibrary((struct Library *)IntuitionBase);

   exit(err);
}

#ifdef LATTICE
int CXBRK(void) {return(0);}
int chkabort(void) {return(0);}
#endif

/*End of DSound.c*/

