/***************************************************************************
*	NAME:  IWVOICE.C $Revision: 1.2 $
**	COPYRIGHT:
**	"Copyright (c) 1994,1995 by e-Tek Labs"
**
**       "This software is furnished under a license and may be used,
**       copied, or disclosed only in accordance with the terms of such
**       license and with the inclusion of the above copyright notice.
**       This software or any other copies thereof may not be provided or
**       otherwise made available to any other person. No title to and
**       ownership of the software is hereby transfered."
****************************************************************************
* $Log: iwvoice.c $
* Revision 1.2  1995/10/13 17:23:39  mleibow
* Added iw_allocate_effects_voice() for effects processing.
* Revision 1.1  1995/02/23 11:07:40  unknown
* Initial revision
***************************************************************************/
#include <dos.h>
#include "iw.h"
#include "iwl.h"
#include "globals.h"

static unsigned long voice_mask;
static unsigned long age=1L;
static int effect_voices=0;

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

LOCAL DEFINITION:
voice_array - array for tracking synthesizer voice usage status

*/
static struct voice_array {
	unsigned long age;
	unsigned int priority;
	void (RFAR *steal_notify)(int);
} voice_array[32];

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

FUNCTION DEFINITION:
iwl_voice_init - initialize voice tracking system

RETURNS: int - 0
*/
int iwl_voice_init(void)
{
	unsigned short i;

	voice_mask = 0L;
	effect_voices = 0;
	for (i=0; i < iwl_voices; i++) {
		voice_array[i].age = 0L;
		voice_array[i].priority = 0;
	}
	return(0);
}

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

FUNCTION DEFINITION:
iw_allocate_voice - allocate a synthesizer voice for use

DESCRIPTION:
iw_allocate_voice makes a synthesizer voice available for use based on
how long it has been in service already and the priority that a caller
requests.

When a voice is requested, the array of voices is search to find the
first voice that has been in use the longest whose priority is greater
than or equal to the priority rrequested.  A voice requested with a 
priority of zero cannot be stolen.

When a voice is stolen, then function passed in to be called when the
voice is stolen is called.

RETURNS: int - voice number of allocated voice
               IW_NO_MORE_VOICES - no voice are available for stealing
*/
int iw_allocate_voice(
  int priority,                   /* priority voice is requested at */
  void (RFAR *steal_notify)(int)) /* function to be called if voice is stolen */
{
    unsigned short i;
    register unsigned long bit;
    unsigned long minage=~0LU;
    unsigned int maxpriority=1; /* 0 priority won't get stolen */
    int steal_voice = IW_NO_MORE_VOICES;

    ENTER;
    for (i=0; i < (iwl_voices-effect_voices); i++) {
	bit = (1L << i);
	if ((bit & voice_mask) == 0) {
	    voice_mask |= bit;
	    voice_array[i].priority = priority;
	    voice_array[i].age = age++;
	    voice_array[i].steal_notify = steal_notify;
	    LEAVE;
	    return(i);
	} else {
	    if (voice_array[i].priority >= maxpriority) {
		if (voice_array[i].priority > maxpriority ||
		    voice_array[i].age < minage) {
		    minage = voice_array[i].age;
		    steal_voice = i;
		}
		maxpriority = voice_array[i].priority;
	    }
	}
    }
    if (steal_voice != IW_NO_MORE_VOICES) {
	if (voice_array[steal_voice].steal_notify) {
	    (*voice_array[steal_voice].steal_notify)(steal_voice);
	}
	/* in case steal notify routine free'd the stolen voice,reallocate it */
	bit = (1L << steal_voice);
        voice_mask |= bit;
	voice_array[steal_voice].priority = priority;
	voice_array[steal_voice].age = age++;
	voice_array[steal_voice].steal_notify = steal_notify;
    }
    LEAVE;
    return(steal_voice);
}

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

FUNCTION DEFINITION:
iw_free_voice - free a synthesizer voice for use

*/
void iw_free_voice(
  unsigned int i) /* voice to be freed */
{
    unsigned long bit;

    ENTER;
    bit = (1L << i);
    voice_mask &= ~bit;
    LEAVE;
}

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

FUNCTION DEFINITION:
iw_adjust_priority - change the priority of a voice in use

*/
void iw_adjust_priority(
  int voice,    /* voice number */
  int priority) /* new priority */
{
	ENTER;
	voice_array[voice].priority += priority;
	LEAVE;
}

#ifdef IW_MODULE_EFFECTS
int iw_allocate_effect_voice(void)
{
    unsigned short i;
    register unsigned long bit;

    effect_voices = 8;	// once effects are enabled, reserve access to
    			// last 8 voices.
    ENTER;
    for (i=iwl_voices-effect_voices; i < iwl_voices; i++) {
	bit = (1L << i);
	if ((bit & voice_mask) == 0) {
	    voice_mask |= bit;
	    voice_array[i].priority = 0;
	    voice_array[i].age = -1L;
	    voice_array[i].steal_notify = 0;
	    LEAVE;
	    return(i);
	}
    }
    LEAVE;
    return(IW_NO_MORE_VOICES);
}
#endif
