
/*************************************************************************/
/*				   Play.c				 */
/*Contains code used to play samples (mono out of one or both speakers,  */
/*and stereo).								 */
/*************************************************************************/

#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 "dsound.h"

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

UBYTE rightAMap[]={4,2,1,8};
UBYTE leftAMap[]={1,8,2,4};
UBYTE eitherAMap[]={1,2,4,8};
UBYTE bothAMap[]={8,3,5,10};

extern UBYTE volume;
extern UWORD speed;
extern ULONG bufSize;

extern BOOL readAll;
extern struct Window *window;

/*Play a sample out of one speaker (left, right, or either)*/
void playMonoSample(BPTR file,channel audioChannel,struct Voice8Header *vhdr,
		    ULONG len)
{
   struct IOAudio *iob1,*iob2;
   ULONG toRead;
   BOOL done=FALSE;
   UBYTE *allocationMap;

   /*Load the entire sample into memory, if the user so specified*/
   if(readAll)
   {
      storeLeft(file,len,bufSize);
      file=0L;
   }

   /*Decide which audio channel will be allocated*/
   switch(audioChannel)
   {
      case MONO_LEFT:
	 allocationMap=leftAMap;
	 break;
      case MONO_RIGHT:
	 allocationMap=rightAMap;
	 break;
      case UNSPECIFIED:
	 allocationMap=eitherAMap;
	 break;
   }

   /*Get the first audio channel*/
   iob1=GetAudioChannel(bufSize,allocationMap);
   if(iob1==NULL)
   {
      WriteMsg("Couldn't create the first buffer\n");
      cleanup(150);
   }

   /* If the user didn't specify a volume, get it from the VHDR */
   if(volume==0)
      volume=(vhdr->volume>>10);

   /* If the VHDR gave a volume of zero, use maximum volume*/
   if(volume==0)
      volume=64;

   /* Get the samples/sec rate (either the rate given by the user, or the*/
   /* rate found in the VHDR) */
   if(speed==0)
      speed=1000000000/(vhdr->samplesPerSec*279);
   else
      speed=1000000000/(speed*279);

   InitAudioChannel(iob1,volume,speed);

   /*Get the 2nd audio channel*/
   iob2=DuplicateAudioChannel(iob1);

   if(iob2==NULL)
   {
      FreeAudioChannel(iob1);
      WriteMsg("Couldn't create the second buffer\n");
      cleanup(175);
   }

   /* Load the first buffer*/
   toRead=MIN(len,bufSize);
   LoadAudioBuffer(file,iob1,toRead);

   len-=toRead;

   /*Store the number of samples to be played*/
   iob1->ioa_Length=toRead;

   /* Make sure there's enough data so that we have something to put in */
   /* the second buffer */
   if(len!=0)
   {
      toRead=MIN(len,bufSize);
      LoadAudioBuffer(file,iob2,toRead);
      len-=toRead;
      iob2->ioa_Length=toRead;
   }
   else
      /* It appears that the entire sound sample is small enough to */
      /* fit into the first buffer, so don't play the second */
      iob2->ioa_Length=0;

   /*And queue up the play requests*/
   BeginIO((struct IORequest *)iob1);
   if(iob2->ioa_Length!=0)
      BeginIO((struct IORequest *)iob2);

   /* If the sound sample was small enough to fit into the two buffers, */
   /* play them then finish up */
   if(len==0)
   {
      Wait((1<<iob1->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) +
	   (1<<window->UserPort->mp_SigBit));

						   /*	vvvv Bug fix */
      if(iob2->ioa_Length!=0 && GetMsg(window->UserPort)==NULL)
	 Wait((1<<iob2->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
	      (1<<window->UserPort->mp_SigBit));

	 done=TRUE;
   }

   /*Otherwise, play those samples then read more from disk*/

   /*Loop while there's stuff to read*/
   while(!done)
   {
      /*Fill the first buffer*/
      Wait((1<<iob1->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
	   (1<<window->UserPort->mp_SigBit));

      toRead=MIN(len,bufSize);

      if(GetMsg(window->UserPort)!=NULL)
      {
	 done=TRUE;
	 break;
      }
      else
	 if(toRead==0)
	 {
	    /*If there's no stuff left to read, wait 'till the second buffer*/
	    /*finishes, then quit*/
	    Wait((1<<iob2->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
		 (1<<window->UserPort->mp_SigBit));
	    done=TRUE;
	    break;
	 }

      LoadAudioBuffer(file,iob1,toRead);
      len-=toRead;

      iob1->ioa_Length=toRead;

      /*Play the first buffer*/
      BeginIO((struct IORequest *)iob1);

      /*Wait for the second buffer to finish*/
      Wait((1<<iob2->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
	   (1<<window->UserPort->mp_SigBit));

      toRead=MIN(len,bufSize);

      if(GetMsg(window->UserPort)!=NULL)
      {
	 done=TRUE;
	 break;
      }
      else
	 if(toRead==0)
	 {
	    /*If there's no stuff left to read, wait 'till the first buffer*/
	    /*finishes, then quit*/
	    Wait((1<<iob1->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
		 (1<<window->UserPort->mp_SigBit));
	    done=TRUE;
	    break;
	 }

      /*Reload it*/
      LoadAudioBuffer(file,iob2,toRead);
      len-=toRead;
      iob2->ioa_Length=toRead;

      /*Play it*/
      BeginIO((struct IORequest *)iob2);
   }

   /*Restore the buffer lengths, so that FreeAudio() channel, etc., knows*/
   /*how much memory to free*/
   iob1->ioa_Length=iob2->ioa_Length=bufSize;

   FreeAudioChannel(iob1);
   DeleteDuplication(iob2);

   return;
}

/*Play a stereo sample out of both speakers*/
/*If the user specifies that just one of the two stereo channels will*/
/*be played, DSound.c calls playMonoSample*/

void playStereoSample(BPTR leftFile,channel audioChannel,
		      struct Voice8Header *vhdr, ULONG length, char *filename)
{
   struct IOAudio *iob1_right,*iob2_right,*iob1_left,*iob2_left;
   ULONG toRead;
   BOOL done=FALSE;
   BPTR rightFile;

   /*Open the file again*/
   rightFile=dupFileHandle(leftFile,filename);

   /*And position ourselves at the start of the right channel's data*/
   Seek(rightFile,length,OFFSET_CURRENT);

   /*Read the entire sample into memory, if specified*/
   if(readAll)
   {
      storeLeft(leftFile,length,bufSize);
      storeRight(rightFile,length,bufSize);
      Close(rightFile);
      leftFile=0L;
      rightFile=4L;
   }

   /*Get the first audio channel*/
   iob1_left=GetAudioChannel(bufSize,leftAMap);
   if(iob1_left==NULL)
   {
      WriteMsg("Couldn't create the first stereo buffer\n");
      cleanup(150);
   }

   iob1_right=GetAudioChannel(bufSize,rightAMap);
   if(iob1_right==NULL)
   {
      WriteMsg("Couldn't create the second stereo buffer\n");
      cleanup(150);
   }

   /* If the user didn't specify a volume, get it from the VHDR */
   if(volume==0)
      volume=(vhdr->volume>>10);

   /* If the VHDR gave a volume of zero, use maximum volume*/
   if(volume==0)
      volume=64;

   /* Get the samples/sec rate (either the rate given by the user, or the*/
   /* rate found in the VHDR) */
   if(speed==0)
      speed=1000000000/(vhdr->samplesPerSec*279);
   else
      speed=1000000000/(speed*279);

   InitAudioChannel(iob1_left,volume,speed);
   InitAudioChannel(iob1_right,volume,speed);

   /*Get the 2nd audio channel*/
   iob2_left=DuplicateAudioChannel(iob1_left);

   if(iob2_left==NULL)
   {
      FreeAudioChannel(iob1_left);
      FreeAudioChannel(iob1_right);
      WriteMsg("Couldn't create the second buffer");
      cleanup(175);
   }

   iob2_right=DuplicateAudioChannel(iob1_right);
   if(iob2_right==NULL)
   {
      FreeAudioChannel(iob1_left);
      DeleteDuplication(iob2_left);
      FreeAudioChannel(iob1_right);
      WriteMsg("Couldn't create the second buffer");
      cleanup(175);
   }


   /* Load the first buffer*/
   toRead=MIN(length,bufSize);
   LoadAudioBuffer(leftFile,iob1_left,toRead);
   LoadAudioBuffer(rightFile,iob1_right,toRead);
   iob1_left->ioa_Length=iob1_right->ioa_Length=toRead;

   length-=toRead;
   iob1_right->ioa_Length=iob1_left->ioa_Length=toRead;

   /* Make sure there's enough data so that we have something to put in */
   /* the second buffer */
   if(length!=0)
   {
      toRead=MIN(length,bufSize);
      LoadAudioBuffer(leftFile,iob2_left,toRead);
      LoadAudioBuffer(rightFile,iob2_right,toRead);
      length-=toRead;
      iob2_right->ioa_Length=iob2_left->ioa_Length=toRead;
   }
   else
      /* It appears that the entire sound sample is small enough to */
      /* fit into the first buffer, so don't play the second */
      iob2_left->ioa_Length=iob2_right->ioa_Length=0;

   /*And queue up the play requests*/
   BeginIO((struct IORequest *)iob1_left);
   BeginIO((struct IORequest *)iob1_right);
   if(iob2_left->ioa_Length!=0)
   {
      BeginIO((struct IORequest *)iob2_left);
      BeginIO((struct IORequest *)iob2_right);
   }

   /* If the sound sample was small enough to fit into the two buffers, */
   /* play them then finish up */
   if(length==0)
   {
      Wait((1<<iob1_left->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
	   (1<<window->UserPort->mp_SigBit));

      if(iob2_left->ioa_Length!=0 && GetMsg(window->UserPort)==NULL)
	 Wait((1<<iob2_left->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
	      (1<<window->UserPort->mp_SigBit));

      done=TRUE;
   }
   /*Otherwise, play those samples then read more from disk*/

   /*Loop while there's stuff to read*/
   while(!done)
   {
      /*Wait for the first buffer to finish playing*/
      Wait((1<<iob1_left->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
	   (1<<window->UserPort->mp_SigBit));

      toRead=MIN(length,bufSize);

      if(GetMsg(window->UserPort)!=NULL)
      {
	 done=TRUE;
	 break;
      }
      else
	 if(toRead==0)
	 {
	    /*If there's no stuff left to read, wait 'till the second buffer*/
	    /*finishes, then quit*/
	    Wait((1<<iob1_left->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
		 (1<<window->UserPort->mp_SigBit));
	    done=TRUE;
	    break;
	 }

      LoadAudioBuffer(leftFile,iob1_left,toRead);
      LoadAudioBuffer(rightFile,iob1_right,toRead);
      length-=toRead;
      iob1_left->ioa_Length=iob1_right->ioa_Length=toRead;

      /*Play the first buffer*/
      BeginIO((struct IORequest *)iob1_left);
      BeginIO((struct IORequest *)iob1_right);

      /*Wait for the second buffer to finish*/
      Wait((1<<iob2_left->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
	   (1<<window->UserPort->mp_SigBit));

      toRead=MIN(length,bufSize);

      if(GetMsg(window->UserPort)!=NULL)
      {
	 done=TRUE;
	 break;
      }
      else
	 if(toRead==0)
	 {
	    /*If there's no stuff left to read, wait 'till the first buffer*/
	    /*finishes, then quit*/
	    Wait((1<<iob1_left->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
		 (1<<window->UserPort->mp_SigBit));
	    done=TRUE;
	    break;
	 }

      /*Reload it*/
      LoadAudioBuffer(leftFile,iob2_left,toRead);
      LoadAudioBuffer(rightFile,iob2_right,toRead);
      length-=toRead;
      iob2_left->ioa_Length=iob2_right->ioa_Length=toRead;

      /*Play it*/
      BeginIO((struct IORequest *)iob2_left);
      BeginIO((struct IORequest *)iob2_right);
   }

   /*Restore the buffer lengths, so that FreeAudio() channel, etc., knows*/
   /*how much memory to free*/
   iob1_left->ioa_Length=iob2_left->ioa_Length=bufSize;
   iob1_right->ioa_Length=iob2_right->ioa_Length=bufSize;

   FreeAudioChannel(iob1_left);
   DeleteDuplication(iob2_left);
   FreeAudioChannel(iob1_right);
   DeleteDuplication(iob2_right);

   if(rightFile != 4L)
      Close(rightFile);

   return;
}

/*Play a mono sample (or a single channel of a stereo sample) out of */
/*both speakers simultaneously*/
void playMonoTwice(BPTR file,channel audioChannel,struct Voice8Header *vhdr,
		   ULONG length)
{
   struct IOAudio *iob1_right,*iob2_right,*iob1_left,*iob2_left;
   ULONG toRead;
   BOOL done=FALSE;

   /*Read the entire sample into memory, if the user so specified*/
   if(readAll)
   {
      storeLeft(file,length,bufSize);
      file=0L;
   }

   /*Get the first audio channel*/
   iob1_left=GetAudioChannel(bufSize,leftAMap);
   if(iob1_left==NULL)
   {
      WriteMsg("Couldn't create the first stereo buffer\n");
      cleanup(150);
   }

   iob1_right=GetAudioChannel(bufSize,rightAMap);
   if(iob1_right==NULL)
   {
      WriteMsg("Couldn't create the second stereo buffer\n");
      cleanup(150);
   }
   FreeMem(iob1_right->ioa_Data,iob1_right->ioa_Length);
   iob1_right->ioa_Data=iob1_left->ioa_Data;

   /* If the user didn't specify a volume, get it from the VHDR */
   if(volume==0)
      volume=(vhdr->volume>>10);

   /* If the VHDR gave a volume of zero, use maximum volume*/
   if(volume==0)
      volume=64;

   /* Get the samples/sec rate (either the rate given by the user, or the*/
   /* rate found in the VHDR) */
   if(speed==0)
      speed=1000000000/(vhdr->samplesPerSec*279);
   else
      speed=1000000000/(speed*279);

   InitAudioChannel(iob1_left,volume,speed);
   InitAudioChannel(iob1_right,volume,speed);

   /*Get the 2nd audio channel*/
   iob2_left=DuplicateAudioChannel(iob1_left);

   if(iob2_left==NULL)
   {
      FreeAudioChannel(iob1_left);
      FreeAudioChannel(iob1_right);
      WriteMsg("Couldn't create the second buffer");
      cleanup(175);
   }

   iob2_right=DuplicateAudioChannel(iob1_right);
   if(iob2_right==NULL)
   {
      FreeAudioChannel(iob1_left);
      DeleteDuplication(iob2_left);
      FreeAudioChannel(iob1_right);
      WriteMsg("Couldn't create the second buffer");
      cleanup(175);
   }
   FreeMem(iob2_right->ioa_Data,iob2_right->ioa_Length);
   iob2_right->ioa_Data=iob2_left->ioa_Data;

   /* Load the first buffer*/
   toRead=MIN(length,bufSize);
   LoadAudioBuffer(file,iob1_left,toRead);

   length-=toRead;
   iob1_right->ioa_Length=iob1_left->ioa_Length=toRead;

   /* Make sure there's enough data so that we have something to put in */
   /* the second buffer */
   if(length!=0)
   {
      toRead=MIN(length,bufSize);
      LoadAudioBuffer(file,iob2_left,toRead);
      length-=toRead;
      iob2_right->ioa_Length=iob2_left->ioa_Length=toRead;
   }
   else
      /* It appears that the entire sound sample is small enough to */
      /* fit into the first buffer, so don't play the second */
      iob2_left->ioa_Length=iob2_right->ioa_Length=0;

   /*And queue up the play requests*/
   BeginIO((struct IORequest *)iob1_left);
   BeginIO((struct IORequest *)iob1_right);
   if(iob2_left->ioa_Length!=0)
   {
      BeginIO((struct IORequest *)iob2_left);
      BeginIO((struct IORequest *)iob2_right);
   }

   /* If the sound sample was small enough to fit into the two buffers, */
   /* play them then finish up */
   if(GetMsg(window->UserPort)!=NULL)
      done=TRUE;
   else
      if(length==0)
      {
	 /* Bug!!! */
	 Wait((1<<iob1_left->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
	      (1<<window->UserPort->mp_SigBit));

	 if(iob2_left->ioa_Length!=0 && GetMsg(window->UserPort)==NULL)
	    Wait((1<<iob2_left->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
		 (1<<window->UserPort->mp_SigBit));

	 done=TRUE;
      }
   /*Otherwise, play those samples then read more from disk*/

   /*Loop while there's stuff to read*/
   while(!done)
   {
      /*Fill the first buffer*/
      Wait((1<<iob1_left->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
	   (1<<window->UserPort->mp_SigBit));

      toRead=MIN(length,bufSize);

      if(GetMsg(window->UserPort)!=NULL)
      {
	 done=TRUE;
	 break;
      }
      else
	 if(toRead==0)
	 {
	    /*If there's no stuff left to read, wait 'till the second buffer*/
	    /*finishes, then quit*/
	    Wait((1<<iob2_left->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
		 (1<<window->UserPort->mp_SigBit));
	    done=TRUE;
	    break;
	 }

      LoadAudioBuffer(file,iob1_left,toRead);
      length-=toRead;
      iob1_right->ioa_Length=iob1_left->ioa_Length=toRead;

      /*Play the first buffer*/
      BeginIO((struct IORequest *)iob1_left);
      BeginIO((struct IORequest *)iob1_right);

      /*Wait for the second buffer to finish*/
      Wait((1<<iob2_left->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
	   (1<<window->UserPort->mp_SigBit));

      toRead=MIN(length,bufSize);

      if(GetMsg(window->UserPort)!=NULL)
      {
	 done=TRUE;
	 break;
      }
      else
	 if(toRead==0)
	 {
	    /*If there's no stuff left to read, wait 'till the first buffer*/
	    /*finishes, then quit*/
	    Wait((1<<iob1_left->ioa_Request.io_Message.mn_ReplyPort->mp_SigBit) |
		 (1<<window->UserPort->mp_SigBit));
	    done=TRUE;
	    break;
	 }

      /*Reload it*/
      LoadAudioBuffer(file,iob2_left,toRead);
      length-=toRead;
      iob2_right->ioa_Length=iob2_left->ioa_Length=toRead;

      /*Play it*/
      BeginIO((struct IORequest *)iob2_left);
      BeginIO((struct IORequest *)iob2_right);
   }

   /*Restore the buffer lengths, so that FreeAudio() channel, etc., knows*/
   /*how much memory to free*/
   iob1_left->ioa_Length=iob2_left->ioa_Length=bufSize;
   iob1_right->ioa_Length=iob2_right->ioa_Length=bufSize;

   iob1_right->ioa_Data=NULL;
   iob2_right->ioa_Data=NULL;

   FreeAudioChannel(iob1_left);
   DeleteDuplication(iob2_left);
   FreeAudioChannel(iob1_right);
   DeleteDuplication(iob2_right);

   return;
}

/*End of Play.c*/
