#include <lattice/stdio.h>
#include <exec/types.h>
#include <devices/audio.h>
#include <devices/timer.h>

#define TICKS_PER_SEC		(3579545.0)
#define PRIORITY     		(100)
#define SOUND_PRIORITY 		(50)
#define MAXBUF	      		(131000)

extern struct MsgPort *CreatePort();
extern struct IOAudio *GetMsg();


 int timOpened;
 int finOpened;

 UBYTE allocMap[] = {1,2,4,8};
 int inUse[] = {0,0,0,0};

 struct MsgPort *port5_ = NULL;
 struct MsgPort *port6_ = NULL;
 struct MsgPort *port7_ = NULL;
 struct MsgPort *port8_ = NULL;
 struct MsgPort *port9_ = NULL;
 struct MsgPort *port10_ = NULL;
 struct MsgPort *port11_ = NULL;

 struct IOAudio     sIO1;
 struct IOAudio     sIO2;
 struct IOAudio     sIO3;
 struct IOAudio     sIO4;

 struct IOAudio     finishIO;
 struct IOAudio     ctlIO;
 struct IOAudio     flushIO;

 struct IOAudio	  *sIOarray_[4];

 struct timerequest timReq;
 long		  timMsg;
 struct MsgPort	  *timPort_ = NULL;

 int nextTry;

 static int doneInit = 0;
^L


/*
---------------------------------------------------------------------------
			audio_init
---------------------------------------------------------------------------
	Initializes the audio device.
	Receives:
	 <nothing>
	Returns:
	 int status	-completion status
	 		 0 ==> OK
===========================================================================
*/
audio_init()
{

   doneInit = 1;
/*
Open the audio device:
======================*/
   if(!(port5_=CreatePort("s5",0))){
      return(2);
   }
   if(!(port6_=CreatePort("s6",0))){
      return(2);
   }
   if(!(port7_=CreatePort("s7",0))){
      return(2);
   }
   if(!(port8_=CreatePort("s8",0))){
      return(2);
   }
   if(!(port9_=CreatePort("ctrl2",0))){
      return(3);
   }
   if(!(port10_ = CreatePort("ctrl3",0))){
      return(3);
   }
   if(!(port11_ = CreatePort("ctrl4",0))){
      return(3);
   }
   if(OpenDevice(AUDIONAME,0,&finishIO,0)){
      finOpened = 0;
      return(4);
   }
   finOpened = 1;
/*
Initialize the audio request blocks:
====================================*/
   sIO1 = finishIO;
   sIO2 = finishIO;
   sIO3 = finishIO;
   sIO4 = finishIO;
   flushIO = finishIO;
   ctlIO = finishIO;
   sIO1.ioa_Request.io_Message.mn_ReplyPort = port5_;
   sIO2.ioa_Request.io_Message.mn_ReplyPort = port6_;
   sIO3.ioa_Request.io_Message.mn_ReplyPort = port7_;
   sIO4.ioa_Request.io_Message.mn_ReplyPort = port8_;
   finishIO.ioa_Request.io_Message.mn_ReplyPort = port9_;
   flushIO.ioa_Request.io_Message.mn_ReplyPort = port10_;
   ctlIO.ioa_Request.io_Message.mn_ReplyPort = port11_;

   finishIO.ioa_Request.io_Flags = IOF_QUICK;
   finishIO.ioa_Request.io_Command = ADCMD_FINISH;
   finishIO.ioa_Data = NULL;
   finishIO.ioa_Length = 0;
   flushIO.ioa_Request.io_Flags = IOF_QUICK;
   flushIO.ioa_Request.io_Command = CMD_FLUSH;
   flushIO.ioa_Data = NULL;
   flushIO.ioa_Length = 0;
   flushIO.ioa_Request.io_Message.mn_Node.ln_Pri = PRIORITY;
   sIO1.ioa_Request.io_Command = CMD_WRITE;
   sIO1.ioa_Request.io_Flags = ADIOF_PERVOL|IOF_QUICK|ADIOF_SYNCCYCLE;
   sIO2.ioa_Request.io_Command = CMD_WRITE;
   sIO2.ioa_Request.io_Flags = ADIOF_PERVOL|IOF_QUICK|ADIOF_SYNCCYCLE;
   sIO3.ioa_Request.io_Command = CMD_WRITE;
   sIO3.ioa_Request.io_Flags = ADIOF_PERVOL|IOF_QUICK|ADIOF_SYNCCYCLE;
   sIO4.ioa_Request.io_Command = CMD_WRITE;
   sIO4.ioa_Request.io_Flags = ADIOF_PERVOL|IOF_QUICK|ADIOF_SYNCCYCLE;
   sIOarray_[0] = &(sIO1);
   sIOarray_[1] = &(sIO2);
   sIOarray_[2] = &(sIO3);
   sIOarray_[3] = &(sIO4);
/*
Open a timer:
=============*/
	if(!(timPort_=CreatePort(0,0))){
      		return(10);
	}
   	timReq.tr_node.io_Message.mn_ReplyPort = timPort_;
   	if(OpenDevice(TIMERNAME,UNIT_VBLANK,&timReq,0)){
		timOpened = 0;
      		return(11);
   	}
	timOpened = 1;
   	timReq.tr_node.io_Message.mn_ReplyPort = timPort_;
   	timReq.tr_node.io_Command = TR_ADDREQUEST;
   	timReq.tr_node.io_Flags = 0;
   	timReq.tr_node.io_Error = 0;
   	timMsg = 1L << timPort_->mp_SigBit;

   nextTry = 0;
   return(0);
}
^L


/*
---------------------------------------------------------------------------
			audio_sound
---------------------------------------------------------------------------
	Simulates the sound of a picked string.
	Receives:
	 BYTE *s_	-pointer to the data for the sound
	 int sLen	-length of sound data in bytes
	 int volume	-volume (0 to 64)
	Returns:
	 <nothing>
===========================================================================
*/
audio_sound(s_,sLen,period,volume)
BYTE *s_;
ULONG sLen;
int period;
int volume;
{
	struct IOAudio *mySound_;
	int ch;
	int u;
	int k;
 
/*
Check if there's any IO requests that have finished:
====================================================*/
	ch = GetChannel(&u,&k);
/*
Setup the number of repeats:
============================*/
	mySound_ = sIOarray_[ch];
/*
Setup the sound message:
========================*/
	mySound_->ioa_Data   = s_;
	mySound_->ioa_Period	= period;
	mySound_->ioa_Length = sLen;
	mySound_->ioa_Cycles = 1;
	mySound_->ioa_Volume	= volume;
	mySound_->ioa_AllocKey = k;
	mySound_->ioa_Request.io_Flags = ADIOF_PERVOL;
	mySound_->ioa_Request.io_Unit = u;
	mySound_->ioa_Request.io_Message.mn_Node.ln_Pri = SOUND_PRIORITY;
/*
Play the sound:
===============*/
	BeginIO(mySound_);
	return(0);
}
^L


^L


/*
---------------------------------------------------------------------------
			audio_close
---------------------------------------------------------------------------
	Closes the audio device and de-allocates all associated memory.
	Receives:
	 <nothing>
	Returns:
	 <nothing>
===========================================================================
*/
audio_close()
{
	int i;
	struct IOAudio *msg_;
	
	if(doneInit){
	   for(i=0; i<4; i++){
		flushIO.ioa_Request.io_Flags = IOF_QUICK;
		flushIO.ioa_Request.io_Command = ADCMD_FINISH;
		flushIO.ioa_AllocKey = sIOarray_[i]->ioa_AllocKey;
		flushIO.ioa_Request.io_Unit = sIOarray_[i]->ioa_Request.io_Unit;
		DoIO(&flushIO);
		finishIO.ioa_Request.io_Flags = IOF_QUICK;
		finishIO.ioa_Request.io_Command = ADCMD_FINISH;
		finishIO.ioa_AllocKey = sIOarray_[i]->ioa_AllocKey;
		finishIO.ioa_Request.io_Unit = sIOarray_[i]->ioa_Request.io_Unit;
		DoIO(&finishIO);
		ctlIO.ioa_Request.io_Command = ADCMD_FREE;
		ctlIO.ioa_Request.io_Flags = IOF_QUICK;
		ctlIO.ioa_Data = NULL;
		ctlIO.ioa_Length = 0;
		ctlIO.ioa_AllocKey = sIOarray_[i]->ioa_AllocKey;
		ctlIO.ioa_Request.io_Message.mn_Node.ln_Pri = PRIORITY;
		ctlIO.ioa_Request.io_Unit = sIOarray_[i]->ioa_Request.io_Unit;
		DoIO(&ctlIO);
		while(msg_=GetMsg(sIOarray_[i]->ioa_Request.io_Message.mn_ReplyPort)){
		}
	   }
	}
	if(finOpened){
		CloseDevice(&finishIO);
	}
	if(timOpened){
		CloseDevice(&timReq);
	}
	if(timPort_){
		DeletePort(timPort_);
	}
	if(port5_){
		DeletePort(port5_);
	}
	if(port6_){
		DeletePort(port6_);
	}
	if(port7_){
		DeletePort(port7_);
	}
	if(port8_){
		DeletePort(port8_);
	}
	if(port9_){
		DeletePort(port9_);
	}
	if(port10_){
		DeletePort(port10_);
	}
	if(port11_){
		DeletePort(port11_);
	}
	return(0);
}
^L

 
/*
---------------------------------------------------------------------------
			audio_delay
---------------------------------------------------------------------------
	Delays for a given amount of time.  (Used for the time between
	notes.)
	Receives:
	 int hsecs	-length of time to delay in hundredths of a sec
	Returns:
	 <nothing>
===========================================================================
*/
audio_delay(hsecs)
int hsecs;
{
	timReq.tr_time.tv_secs  = (int)(hsecs/100);
	timReq.tr_time.tv_micro = (int)(hsecs%100) * 10000;
	DoIO(&timReq);
	return(0);
}
^L


/*
---------------------------------------------------------------------------
			EmptyPorts
---------------------------------------------------------------------------
	Used internaly by th audio routines.  Waits for each pending
	audio request and frees the request when it finishes.
	Receives:
	 <nothing>
	Returns:
	 <nothing>
===========================================================================
*/
EmptyPorts()
{
	struct IOAudio *GetMsg();
	struct IOAudio *msg_;
	int i;
	
	for(i=0; i<4; i++){
		if(inUse[i]){
			while(msg_=GetMsg(sIOarray_[i]->ioa_Request.io_Message.mn_ReplyPort)){
				inUse[i] = 0;
			}
			if(!inUse[i]){
				ctlIO.ioa_Request.io_Command = ADCMD_FREE;
				ctlIO.ioa_Request.io_Flags = IOF_QUICK;
				ctlIO.ioa_Data = NULL;
				ctlIO.ioa_Length = 0;
				ctlIO.ioa_AllocKey = sIOarray_[i]->ioa_AllocKey;
				ctlIO.ioa_Request.io_Message.mn_Node.ln_Pri = PRIORITY;
				ctlIO.ioa_Request.io_Unit = sIOarray_[i]->ioa_Request.io_Unit;
				DoIO(&ctlIO);
			}
		}
	}
	return(0);
}
^L
	

/*
---------------------------------------------------------------------------
			GetChannel
---------------------------------------------------------------------------
	Finds a free channel, or if they're all busy, cancels the
	oldest one and frees it.
	Receives:
	 int *u_	-pointer to space for the unit
	 int *k_	-pointer to space for the key
	Returns:
	 int *u_	-the free channel's unit
	 int *k_	-the free channel's key
===========================================================================
*/
GetChannel(u_,k_)
int *u_;
int *k_;
{
	int ch;
	
	while(1){
		EmptyPorts();
		ctlIO.ioa_Request.io_Command = ADCMD_ALLOCATE;
		ctlIO.ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK;
		ctlIO.ioa_Data = allocMap;
		ctlIO.ioa_AllocKey = 0;
		ctlIO.ioa_Request.io_Unit = NULL;
		ctlIO.ioa_Request.io_Message.mn_Node.ln_Pri = PRIORITY;
		ctlIO.ioa_Length = sizeof(allocMap);
		BeginIO(&ctlIO);
		if(!WaitIO(&ctlIO)){
			*u_ = (int)(ctlIO.ioa_Request.io_Unit);
			*k_ = (int)(ctlIO.ioa_AllocKey);
			break;
		}
	}
	while(1){
		for(ch=0; ch<4; ch++){
			if(!inUse[ch]){
				inUse[ch] = 1;
				nextTry = (ch+1)%4;
				return(ch);
			}
		}
		EmptyPorts();
	}
}
^L


/*
---------------------------------------------------------------------------
			audio_free
---------------------------------------------------------------------------
	Frees all of the channels.
	Receives:
	 <nothing>
	Returns:
	 <nothing>
===========================================================================
*/
audio_free()
{
	while(inUse[0]+inUse[1]+inUse[2]+inUse[3]){
		EmptyPorts();
	}
	return(0);
}
