#include <dos.h>
#include <proto/dos.h>
#include <exec/exec.h>
#include <exec/libraries.h>
#include <exec/ports.h>
#include <proto/exec.h>
#include <devices/audio.h>
#include <stdio.h>
#include <string.h>
#include <workbench/startup.h>

extern struct WBStartup *WBenchMsg;

byte c,*buffer[4]={0,0,0,0};
LONG fileSize[4];

struct Sample 
{
 byte *Start;
 ULONG Size;
 UWORD Period,Volume;
 struct Sample *Next;
};
struct Sample sam[4], *sp;

struct Sample *Scan8SVX(byte *, long);
LONG GetFileSize(char *);
LONG ReadToBuffer(char *, byte *, LONG);
void Play(struct Sample *);
char *file[4];
int i,file_ct;

main(int argc, char **argv)
{

 if (NULL==(DOSBase=(struct DOSLibrary *)OpenLibrary("dos.library",0)))
 {printf("No DOS!\n"); goto Error;}

 if (argc>0)			/* Started from CLI */
 {
  file_ct=argc-1;
  if (file_ct < 1)
  {printf("Usage is \"");printf(argv[0]);printf(" <filename> [<filename>*]\"\n"); goto Error;}

  for(i=0; i<file_ct; i++) file[i]=argv[i+1];
 }
 else				/* Started from Workbench */
 {
  struct WBArg *arg;

  file_ct=(WBenchMsg->sm_NumArgs)-1;

  arg=WBenchMsg->sm_ArgList;
  arg++;
  for(i=0; i<file_ct; i++,arg++) file[i]=arg->wa_Name;
 }

 if(file_ct < 1){printf("You must select at least one sample.\n");goto Error;}
 if(file_ct > 4){file_ct=0;printf("You can't play more than 4 samples at once.\n");goto Error;}

 for(i=0; i<file_ct; i++)
 {
  if (0==(fileSize[i]=GetFileSize(file[i])))
  {printf("Aborted.\n");goto Error;}

  if(NULL==(buffer[i]=AllocMem(fileSize[i],MEMF_CHIP)))
  {printf("Out of memory\n"); goto Error;}

  if(fileSize[i]!=(ReadToBuffer(file[i],buffer[i],fileSize[i]))) goto Error;

  if((struct Sample *)NULL==(sp=Scan8SVX(buffer[i],fileSize[i]))) goto Error;
  sam[i]=*sp;
  if(i>0) sam[i-1].Next=&sam[i];
 }

 Play(&sam[0]);
 goto Exit;

Error:
 Delay(4*50);

Exit:
 for(i=0; i<file_ct; i++) if(buffer[i]) FreeMem(buffer[i],fileSize[i]);
 if(DOSBase) CloseLibrary((struct Library *)DOSBase);
 return(0);
}

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

void Play(struct Sample *sp)
{
#define L0 1	/* Definitions of audio channels */
#define R0 2
#define R1 4
#define L1 8

 UBYTE wantChans[4][6] =
      {
       {L0,L1,R0,R1,0,0},		/* Pref L for 1st channel */
       {R0,R1,L0,L1,0,0},		/* Pref R for 2nd channel */
       {L0,L1,R0,R1,0,0},		/* Don't really give a toss about other 2*/
       {L0,L1,R0,R1,0,0}
      };

 ULONG gotChans;

 struct MsgPort *port=0;
 struct IOAudio chanr[4],opr,sgr;
 struct Device *device;
 struct Sample *tempsp;
 int i,chan_ct;

 for (chan_ct=1,tempsp=sp;
      tempsp->Next!=(struct Sample *)NULL;
      chan_ct++,tempsp=tempsp->Next){};		/* Follow chain of samples to find out how many there are */
 if(chan_ct > 4){printf("Can't have more than 4 channels.\n"); goto Error;}

 if(NULL==(port=CreatePort("replay",0)))
 {printf("Couldn't create reply port.\n"); goto Error;}

 port->mp_SigTask=FindTask(0);

 for(i=0; i<chan_ct; i++)
 {
  chanr[i].ioa_Request.io_Message.mn_Node.ln_Pri=0;
  chanr[i].ioa_Request.io_Message.mn_ReplyPort=port;
 }
 opr.ioa_Request.io_Message.mn_Node.ln_Pri=0;
 opr.ioa_Request.io_Message.mn_ReplyPort=port;
 sgr.ioa_Request.io_Message.mn_Node.ln_Pri=0;
 sgr.ioa_Request.io_Message.mn_ReplyPort=port;

 if(0!=OpenDevice(AUDIONAME,0,&(opr.ioa_Request),0))
  {printf("Couldn't open audio device.\n"); goto Error;}
 device=opr.ioa_Request.io_Device;

 gotChans=0;
 for(i=0; i<chan_ct; i++)
 {
  chanr[i].ioa_Request.io_Command=ADCMD_ALLOCATE;
  chanr[i].ioa_Request.io_Flags=ADIOF_NOWAIT;
  chanr[i].ioa_Request.io_Device=device;
  chanr[i].ioa_Data=wantChans[i];
  chanr[i].ioa_Length=sizeof(wantChans[i]);
  chanr[i].ioa_AllocKey=opr.ioa_AllocKey;
  BeginIO(&(chanr[i].ioa_Request));
  gotChans=(gotChans | (ULONG)chanr[i].ioa_Request.io_Unit);
  if(NULL==chanr[i].ioa_Request.io_Unit)
   {printf("Couldn't allocate required channel(s).\n"); goto Error;}
 }

 sgr.ioa_Request.io_Unit=(struct Unit *)gotChans;
 sgr.ioa_Request.io_Command=CMD_STOP;
 sgr.ioa_Request.io_Device=device;
 sgr.ioa_AllocKey=opr.ioa_AllocKey;
 BeginIO(&(sgr.ioa_Request));	/* Queue WRITEs until given CMD_START */

 for(i=0,tempsp=sp; i<chan_ct; i++,tempsp=tempsp->Next)
 {
  chanr[i].ioa_Request.io_Command=CMD_WRITE;
  chanr[i].ioa_Request.io_Flags=ADIOF_PERVOL;
  chanr[i].ioa_Data=tempsp->Start;
  chanr[i].ioa_Length=tempsp->Size;
  chanr[i].ioa_Period=tempsp->Period;
  chanr[i].ioa_Volume=tempsp->Volume;
  chanr[i].ioa_Cycles=1;
  BeginIO(&(chanr[i].ioa_Request));
 }

 sgr.ioa_Request.io_Command=CMD_START;
 DoIO(&(sgr.ioa_Request));	/* Go on all (reqd.) channels */

 for(i=0; i<chan_ct; i++) WaitIO(&(chanr[i].ioa_Request));

 opr.ioa_Request.io_Unit=(struct unit *)gotChans; /* Ensure all allocated channels are freed by CloseDevice */
 CloseDevice(&(opr.ioa_Request));

Error:

 if(port) DeletePort(port);

}

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

LONG ReadToBuffer(char *name, byte *buffer, LONG size)
{
 BPTR fileHandle;
 LONG actual=0;
 
 if(NULL==(fileHandle=Open(name,MODE_OLDFILE)))
 {
  printf("File \""); printf(name); printf("\" could not be opened.\n");
  goto Error;
 }

 if( (size) != (actual=Read(fileHandle,buffer,size)) )
 {
  printf("Read error in file \"");printf(name);printf("\".\n");
  actual=0;
 }

 if(fileHandle) Close(fileHandle);
Error:
 return(actual);
}

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

LONG GetFileSize(char *name)
{
 struct FileInfoBlock *fib;
 BPTR fileLock=0;
 LONG size=0;

 if(NULL==(fib=(struct FileInfoBlock *)AllocMem(sizeof(struct FileInfoBlock),MEMF_CHIP)))
 {printf("Out of memory\n"); goto Error;}

 if(NULL==(fileLock=Lock(name,ACCESS_READ)))
 {
  printf("File \"");printf(name);printf("\" could not be locked.\n");
  goto Error;
 }
 
 if(NULL==Examine(fileLock,fib))
 {
  printf("File \"");printf(name);printf("\" could not be examined.\n");
  UnLock(fileLock);
  goto Error;
 }
 else size=fib->fib_Size;
  
Error:
 if(fileLock) UnLock(fileLock);
 if(fib) FreeMem(fib,sizeof(struct FileInfoBlock));
 return(size);
}

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

struct Sample *Scan8SVX(byte *buffer, long size)
{
 struct VHDR
 {
  long VHDR_oneShotHiSamples,VHDR_repeatHiSamples,VHDR_samplesPerHiCycle;
  UWORD VHDR_samplesPerSec;
  byte VHDR_ctOctave,VHDR_sCompression;
  long VHDR_volume;
 };
 struct VHDR *vp;
 const long CyclesPerSec=0x369E9A;
 static struct Sample sample;
 BOOL error=FALSE;
 byte *bp;
 
 sample.Start=(byte *)NULL;	/* Ensure all fields nil at start of EACH call */
 sample.Size=0;
 sample.Period=0;
 sample.Volume=0;
 sample.Next=(struct Sample *)NULL;

 bp=buffer;
  
 if(0!=strncmp("FORM", (char *)bp, 4))
 {printf("File must be a standard IFF FORM 8SVX.\n"); return(NULL);}
 bp+=4;
 
 if((size-8)!= *(long *)bp)
 {printf("File must be a standard IFF FORM 8SVX.\n"); return(NULL);}
 bp+=4;
 
 if(0!=strncmp("8SVX", (char *)bp, 4))
 {printf("File must be a standard IFF FORM 8SVX.\n"); return(NULL);}
 bp+=4;
 
 while (((sample.Start==0) || (sample.Period==0))
	&& (error==FALSE) && (bp<(buffer+size)))
 {
  if(0==strncmp("VHDR", (char *)bp, 4))
  {
   bp+=4;
   
   if(sizeof(struct VHDR) != *(long *)bp)
   {printf("VHDR is wrong size.\n"); return(0);}
   bp+=4;

   vp=(struct VHDR *)bp;
   sample.Size=(vp->VHDR_oneShotHiSamples)+(vp->VHDR_repeatHiSamples);
   if(0==(sample.Period=CyclesPerSec/(vp->VHDR_samplesPerSec)))
   {printf("Period value in file is zero.\n"); return(0);}

   if(vp->VHDR_sCompression!=0)
   {printf("Sample must be uncompressed.\n"); return(0);}
   
   if(0x40<(sample.Volume=(vp->VHDR_volume)>>10))	/* Volume range is 0x00 to 0x40 (not 0x3F strangely enough)*/
   {printf("Volume value in file is wrong.\n"); return(0);}
   
   bp+=sizeof(struct VHDR);
  }
  else 
  {if(0==strncmp("BODY", (char *)bp, 4))
   {
    bp+=4;
    if(0==sample.Period){printf("BODY found without preceding VHDR.\n"); return(0);}
    if(*(long *)bp != sample.Size)
    {printf("Size given in VHDR disagrees with BODY size.\n"); return(0);}
    bp+=4;
    sample.Start=bp;
   }
   else
   {
    printf("Chunk ignored.\n");
    bp+=4;
    bp+=*(long *)bp;
   }
  }
 }
 return(&sample);
}

