/***************************************************************************
*       NAME:  XLAT.C $Revision: 1.16 $
**      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."
****************************************************************************/
//#define HALFREQ

#include <dos.h>
#include "sbosdata.h"
#include "sbosdefs.h"
#include "shared.h"
#include "fm.h"
#include "synproto.h"
#include "adsr.h"
#include "synth.h"

extern SHARED shared;

extern unsigned int fm_chan;         /** fm channel number                   **/
extern unsigned int fm_voice;        /** IW channel number                   **/
extern unsigned int car_voice;
extern unsigned int mod_voice;
extern unsigned char hit_perc;       /** TRUE if current register hit is perc**/
extern unsigned char things_to_do;   /** See fm.h                            **/
extern unsigned char pending_to_do[];/** See fm.h                            **/
extern unsigned char perc_to_do;     /** See fm.h                            **/
extern unsigned char rhythm_reg;     /** Register BD                         **/
extern FM_CHANNEL * chan_ptr;        /** Ptr to channel if applicable        **/
extern FM_OPERATOR * voice_ptr;      /** Ptr to voice if applicable (c or m) **/
extern FM_OPERATOR * car_ptr;        /** Channel's carrier if applicable     **/
extern FM_OPERATOR * mod_ptr;        /** Channel's modulator if applicable   **/
extern unsigned char iw_syn_flags[];

// Extern prototypes
extern void asm_calc_frequency(void);
extern void asm_do_car_mod_freq(void);
extern unsigned int asm_calc_rate(void);
extern unsigned int asm_calc_volume(void);
extern unsigned int asm_calc_ksl(void);
extern void set_global_ptrs(void);
extern void fm_picker(void);
extern void sine_picker(void);

// Local Prototypes
void update_volume_frequency(void);
void prog_frequency(void);
void update_sustain(void);
void update_percussion(void);
void update_instrument(void);
void update_keyon(void);
void pascal calc_new_envelope(unsigned int level);
unsigned int calc_new_volume(void);
void check_for_effect(void);
unsigned long pascal get_effect_frequency(unsigned long freq);
void set_channel(void);
void pascal play_perc(int);

/** Globals for passing to Play functions **/
SYN_ENVELOPE envelope;
unsigned int lvol,rvol;

/** FREQUENCIES **/
unsigned long iw_frequency; /** dup of structure element **/
unsigned long car_frequency;
unsigned long mod_frequency;

/** Optimization flags **/
int is_rhythm_voice;
unsigned char force_reselect[8];

/** Debug stuff **/
#ifdef DEBUG
extern int force_instrument[];
extern int chan_disable[];
#endif

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

FUNCTION DEFINITION:
process_updates - Runs through all valid bits in the things_to_do list and 
    calls the appropriate emulation routines in the right order.

DESCRIPTION:
    If UPD_PERCUSSION is set in things_to_do - call update_percussion()
    Else if neither of the voices are running
        If a note is going to play
            Retrieve pending jobs in things_to_do and set flag
        Else
            Store job in pending list and return
(1) Call xlation routines based on bits in things_to_do for current voice
    If flag is set change pointers to modulator and goto (1)
    Start or stop a note if applicable

EXPECTS:
    things_to_do and perc_to_to must reflect the most recent change in the
    fm parameters.  Global values are based on type of FM register modified.
    If the register changed is a global register:
        fm_voice and all voice pointers are invalid
    If the register changed is channel specific:
        fm_voice and all voice pointers represent the carrer for that channel
    If the register changed is operator specific:
        fm_voice and all voice pointers represent the operator changed

SEE ALSO:
    play_perc() - When a perc is played (through BD) and bits are pending

*/
void process_updates(void)
{
int flag=0;
unsigned char * pending_ptr;
#ifdef DEBUG

    /** 
     ** If I'm forcing or done forcing an instrument, update instrument
     **/
if ((force_instrument[fm_chan]) && (things_to_do | UPD_KEYON))
    {
    things_to_do |= UPD_INSTRUMENT;

    /** 
     ** if force_instrument is 0xffff, it is off but I need to reselect 
     **/
    if(force_instrument[fm_chan] == -1)
        force_instrument[fm_chan] = 0;
    }
    
#endif

//things_to_do |= UPD_INSTRUMENT;

/**
 ** If the UPD_PERCUSSION bit is on in things_to_do, update_percussion() will
 ** be called.  Control will skip over all other routines up to update_keyon(). 
 ** None of the other bits in things_to_do can be on while that one is on.
 ** This is guaranteed in the parsing code in that a percussion register
 ** write only sets the UPD_PERCUSSION flag. Also, this will NOT do any pending
 ** jobs now.  See play_perc() for details on how perc voices get setup if 
 ** bits are pending.  NO code which depends on fm_voice is ever called 
 ** if this bit is on.  There is no voice associated with the percussion 
 ** register.
 **/
if (things_to_do & UPD_PERCUSSION)
    update_percussion();

/**
 ** If the register hit is the perc register, fm_voice is invalid now
 ** and everything that needs to be done will be done in update_keyon
 ** if it is called.  So, just go there now
 **/
if(hit_perc == TRUE) 
    goto done_perc;

/**
 ** fm_voice is guaranteed to be setup now so I can get the pending bits
 **/
pending_ptr = &pending_to_do[fm_voice];

/**
 ** If the voices are NOT running and the frequency is ZERO, the app is 
 ** trying to do some sort of adlib digital so we let them modify the DACs
 **/
if ((!syn_voices_running()) && (car_frequency != 0L))
    {
    /**
     ** The voices are off.
     ** See if the app wants to play a note now.  If so, get all pending jobs
     ** and execute them for both carrier and modulator (in that order).
     ** If the app just modifies a note charateristic, set a bit in the pending
     ** jobs and leave.
     **/
    if (things_to_do & UPD_KEYON)
        {
        if(force_reselect[fm_chan])
            {
            things_to_do |= UPD_INST_WAVE;
            force_reselect[fm_chan] = 0;
            }
        things_to_do |= *pending_ptr;
        *pending_ptr = 0;
        flag = 1;                  /** Set so it will do modulator next **/
        }
    else
        {
        *pending_ptr |= things_to_do;
        things_to_do = 0;
        return;
        }
    }

next_one:

    is_rhythm_voice =((rhythm_reg & PERC_ENABLE)&&(fm_voice >= VOICE_PERC_LOW));

    if (things_to_do & UPD_SUSTAIN)
        update_sustain();

    if (things_to_do & (UPD_VOLUME | UPD_FREQUENCY))
        update_volume_frequency();

    if (things_to_do & (UPD_INSTRUMENT | UPD_INST_WAVE))
        update_instrument();

    if (things_to_do & UPD_FREQUENCY)
        prog_frequency();

    /**
     ** This is before update_keyon() because we need to setup the modulator too
     **/
    if (flag)
        {
        flag = 0;    /** Don't come through here again **/
        /** 
         ** Force Keyon here so we execute update_keyon().
         ** (flag was originally set on the carrier - not modulator)
         ** Problem since things_to_do is voice based and not channel based
         **/
        fm_voice = mod_voice;
        pending_ptr = &pending_to_do[mod_voice];
        things_to_do = *pending_ptr | UPD_KEYON; /** Only got here on keyon **/
        *pending_ptr = 0;
        set_global_ptrs();
        goto next_one;
        }

done_perc:

if (things_to_do & UPD_KEYON)
    update_keyon();

things_to_do = 0;    /** Reset the bits **/
perc_to_do = 0;
hit_perc = 0;
}

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

FUNCTION DEFINITION:
prog_frequency -  Programs the frequencies for both modulator and carrier voice

DESCRIPTION:
    Makes a call to the synth to download the frequencies previously set by
    a call to syn_set_frequency().

EXPECTS:
    syn_set_frequency() has been called for both carrier and modulator voices.

*/
void prog_frequency(void)
{
    syn_prog_frequency(car_voice,-1);
    syn_prog_frequency(mod_voice,-1);
}

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

FUNCTION DEFINITION: 
update_volume_frequency - Recalculates all that has changed if a frequency or
    volume related FM register has been changed.

DESCRIPTION:
    Recalculates everything related to frequencies and envelope volumes 
    and rates.  Determines downloadable frequencies for both carrier and
    modulator voices. Calculates complete volume envelopes for both carrier
    and modulator voices.  Takes into account the ADLIB 'feature' related to
    percussions and frequency multiples.

EXPECTS:
    Voice and channel pointers must be setup to the voice and channel which
    needs updating.

*/
void update_volume_frequency(void)
{
    unsigned int level;

    /**
     ** Calculate the channel frequency based on only the block f-num.
     **/
    _AL = chan_ptr->freql; /** Setup registers for frequency calculation     **/
    _CL = chan_ptr->freqh; /** It's gonna be fast so hold on...              **/
    asm_calc_frequency();  /** Result is in 'iw_frequency' times 1024        **/

    /**
     ** Now, use that and take into consideration the multiple for each 
     ** operator and come up with frequencies for the carrier and modulator.
     **/
    _AL = car_ptr->param;  /** Pass in the car's and mod's multiple          **/
    _CL = mod_ptr->param;  /** and set the result in 'car_frequency' and     **/
    asm_do_car_mod_freq(); /** 'mod_frequency' - Both * 1024                 **/

    /** 
     ** A change in frequency will change the envelope if the KSL value is
     ** set to anything but 0.  So, we must get the new envelope.
     **/
    calc_new_envelope(0); /** Calc IW envelope at full and use offsets **/
    level = calc_new_volume(); /** level is an attenuation 0.75dB incr **/
    level <<= 9;  /** shift up to 16 bits for offset regs **/
    lvol=rvol=level;

    /**
     ** We want to set the modulator's envelope to the carrier's only if
     ** it is playing noise or it is not being used (it's playing the same
     ** instrument)
     **/
    check_for_effect();
    
    syn_set_frequency(car_voice,car_frequency);
    syn_set_frequency(mod_voice,mod_frequency);

    /**
     ** This takes into account the anamoly in the ADLIB part which seems
     ** to affect carrier and modulator multiples.  Apparently, when in 
     ** percussion mode (0x20 bit in 0xBD), the carrier's multiple has no
     ** effect on the carrier voice.  It is the modulator's multiple
     ** which is copied over and used for the carrier voice.
     **/
    if((is_rhythm_voice) && (mod_voice == VOICE_TOMTOM))
        syn_set_frequency(VOICE_TOPCYM,syn_get_frequency(VOICE_TOMTOM));
    else if((is_rhythm_voice) && (mod_voice == VOICE_HIHAT))
        syn_set_frequency(VOICE_SNARE,syn_get_frequency(VOICE_HIHAT));

    /** 
     ** If the voice is in fm mode and there was a change in the
     ** frequency, reselect the instrument if it was running or set a flag
     ** if it was not moving. This stops the picking of 
     ** another instrument if the app wants to slide frequencies.
     **/
    if (((chan_ptr->con & 0x01) == (UCHAR)0) && (things_to_do & UPD_FREQUENCY))
        {
        if (!syn_voices_running())
            things_to_do |= UPD_INST_WAVE;
        else
            force_reselect[fm_chan] = 1;
        }

    /**
     ** If this is not to be called later, call it now
     **/
    if(!(things_to_do & (UPD_INSTRUMENT | UPD_INST_WAVE)))
        set_channel();
}

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

FUNCTION DEFINITION: 
update_sustain - Notifies the synth the state of the sustain bit for a voice

DESCRIPTION:
    Calls a synth procedure to notify it of the current state of the sustain
    bit for the current voice.

*/
void update_sustain(void)
{
    unsigned int val;

    /**
     ** Tell the synth if the sustain bit is on or not
     **/
    if (voice_ptr->param & 0x20)
        val = TRUE;
    else
        val = FALSE;
    syn_set_sustain(fm_voice,val);
}

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

FUNCTION DEFINITION: 
calc_new_envelope - Determines IW envelope levels and rates based on current
    FM parameters.

DESCRIPTION:
    Calls assembly routines to determine the envelope for the current voice.
    The attack level is at the attenuated volume passed in as 'level'.   The
    sustain level is an attenuation down from the attack level.  (The sustain
    volume can never be louder than the attack level.)  Rates are determined
    from an index into a table built specifically for the timing for the chip
    used.

MODIFIES:
    envelope struct

*/
void pascal calc_new_envelope(unsigned int level)
{
    /** Calculate the level to attack to **/
    _AL = level;               /** we attack to the highest point allowed **/
    envelope.level[0] = asm_calc_volume();

    /** 
     ** Calculate the level to sustain at - by adding attenuations 
     ** Sustain level in the FM part is by 3dB increments,
     ** Therefore we will change the number into .75dB increments by multiplying
     ** by 4
     **/
    _AL = (voice_ptr->sustain<<2) + level; /** we sus at the sustain level **/
    envelope.level[1] = asm_calc_volume();

    /**
     ** Calculate each rate - attack, decay, release
     ** Setup parameters and call asm_calc_rate()
     ** Use the return value to index table to get IW ADR rate
     **
     ** Determine the attack rate. **/
    _AL = voice_ptr->attack;
    _CL = voice_ptr->param;
    envelope.rate[0] = attack_index[asm_calc_rate()];

    /** Determine the decay rate. **/
    _AL = voice_ptr->decay;
    _CL = voice_ptr->param;
    envelope.rate[1] = decay_release_index[asm_calc_rate()];

    /** Determine the release rate. **/
    _AL = voice_ptr->release;
    _CL = voice_ptr->param;
    envelope.rate[2] = decay_release_index[asm_calc_rate()];

    envelope.level[2] = FLOOR_VOL<<4;
    envelope.nattacks = 2;    
    envelope.nreleases = 1;
}

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

FUNCTION DEFINITION: 
calc_new_volume - Determines the IW volume level based on FM parameters

DESCRIPTION:
    Calculates the attenuation for the attack level based on KSL, FM atten,
    and user synth attenuation from the loader.

EXPECTS:
    Voice and channel pointers must be setup.
    Synth attenuation value in shared memory must be valid.

RETURNS: unsigned int - Volume level in ADLIB levels (0.75 dB incr)

*/
unsigned int calc_new_volume(void)
{
    unsigned int level;

    /** 
     ** Calculate new max level based on KSL bits and Frequency 
     **/
    _AL = chan_ptr->freql;
    _AH = chan_ptr->freqh;
    _CL = voice_ptr->ksl;
    level = asm_calc_ksl();      /** level is the atten from KSL in 0.75 dB  **/
    level += voice_ptr->level;   /** FM atten in 0.75 dB increments          **/
    level += shared.synth_atten; /** account for master synth volume         **/

    /** 
     ** Having a level of full volume will mess up the ramping in the enveolpes
     ** Normally, one would cap off the attack and sustain level to be 
     ** less than max.  Instead, we will attenuate the entire envelope by
     ** a certain amount to get rid of that effect.  1 seems to work fine.
     ** AND, add in some small synth attenuation to cut out clipping when 
     ** there are alot of voices playing.
     **/
    level += 12;

    if(level > 127) level = 127;

    /**
     ** 0.75 dB increments gives a range of 0-127 down to -94dB
     **/ 

    return level;
}

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

FUNCTION DEFINITION: 
update_instrument - Determines an instrument bank and number based on FM 
    parameters

DESCRIPTION:
    Stops both carrier and modulator voices.  Performs a picking algorithm
    to determine the patch to play.  Sets the UPD_KEYON bit in things_to_do
    if the voices were playing.

EXPECTS:
    All voice and channel pointers are valid.

*/
void update_instrument(void)
{
    char vrunning;
    char sinemode;

    sinemode = chan_ptr->con & 0x01;

    /** Save current running state **/
    vrunning = syn_voices_running();

    /** Stop BOTH voices **/
    syn_stop_voice(mod_voice);
    syn_stop_voice(car_voice);

    /** 
     ** If the percussions are enabled and the voice is a percussion voice
     ** I don't want to do anything here -- the instrument stays the same.
     **/
    if(is_rhythm_voice)
        {}
    else if(sinemode)
        sine_picker();
    else
        {
        if (things_to_do & UPD_INSTRUMENT)
            fm_picker();
        else/** don't do picker, just set instrument so we can update wave # **/
            syn_set_instrument(car_voice,CUR_BANK,CUR_INST);
        }

    set_channel();

    /** If either WAS running, re-trigger the note-on sequence **/
    /** NOTE, I don't need to test here for the frequency effect because
     ** the play code copies all needed information from the carrier to
     ** the modulator.  It would be redundant to do it here too
     **/
    if (vrunning)
        things_to_do |= UPD_KEYON;

    /** 
     ** Now that we've found a new wave, we must recalculate a new fc.
     ** NOTE that this does NOT call the first update_frequency() routine
     ** but the second -- prog_frequency().
     **/
    things_to_do |= UPD_FREQUENCY;
}

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

FUNCTION DEFINITION: 
play_perc - Makes sure that the IW voice is setup to play percussions

DESCRIPTION:
    Called when told to play a percussion through the percussion register.
    This tests the pending flags to see if there is any changes to be made
    in the voice characteristics.  If there is, it sets up global pointers and
    calls the setup routines in order to setup everything for the voice.  This
    routine exists because the percussion register has multiple voices 
    associated with it and therefore can not get setup in the normal way in
    process_updates().

*/
void pascal play_perc(int voice)
{
    if(pending_to_do[voice])
        {
        fm_voice = voice;
        set_global_ptrs();
        is_rhythm_voice=((rhythm_reg&PERC_ENABLE)&&(fm_voice>=VOICE_PERC_LOW));
        update_volume_frequency();
        update_instrument();
        prog_frequency();
        pending_to_do[fm_voice] = 0;
        }
    syn_play_voice(voice,FM_EMUL);
}

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

FUNCTION DEFINITION: 
update_keyon - Starts/stops voice(s) based on FM parameters

DESCRIPTION:
    Starts of stops a voice based on the keyon bit and percussion play bits.
    If perc_to_do is true and percussions are enabled, try to play percussions
    each separately based on set bits in perc_to_do.  There is no way to stop
    a percussion from playing other than re-triggering the note again.  
    Otherwise, if the keyon bit for the channel is on, try to play the note.
    It assumes it wouldn't have gotten here if the keyon bit has not changed
    state.  So, if it is now high, it must have just changed.  The carrier
    is always played on the channel and the modulator only under certain 
    circumstances.  If the modulator has been setup to play noise or effect,
    it will be played.  If the keyon bit is off, both voices will start
    the release.
    
EXPECTS:
    voice and channel pointers are setup.
    perc_to_do and rhythm_reg reflect the current state of the machine.

*/
void update_keyon(void)
{
    char sinemode;
    int bank;
    int inst;

#ifdef DEBUG
    if(chan_disable[fm_chan])
        return;
#endif

    sinemode = chan_ptr->con & 0x01;

    /** 
     ** In the FM part, a percussion note will NOT play if the keyon bit is
     ** on for that voice.  This code does not handle that condition.
     **/
    if((rhythm_reg & PERC_ENABLE) && (perc_to_do))
        {
        if(perc_to_do & PERC_HIHAT)
            play_perc(VOICE_HIHAT);
        if(perc_to_do & PERC_TOPCYM)
            play_perc(VOICE_TOPCYM);
        if(perc_to_do & PERC_TOMTOM)
            play_perc(VOICE_TOMTOM);
        if(perc_to_do & PERC_SNARE)
            play_perc(VOICE_SNARE);
        if(perc_to_do & PERC_BASS)
            play_perc(VOICE_BASS);
        perc_to_do = 0;
        }
    else if(chan_ptr->keyon) /** if keyon is on **/
        {
        if(sinemode)
            syn_play_voice(mod_voice,FM_EMUL);
        else if(iw_syn_flags[mod_voice] & SYN_PLAY_NOISE)
            {
            syn_get_instrument(car_voice, &bank, &inst);
            if(bank != TYPE_PERC)
                syn_play_voice(mod_voice,FM_EMUL);
            }
        else /** in FM mode and playing single melodic on carrier **/
            {
            /**
             ** If we are playing an effect
             ** OR if percs are on and playing perc voice
             **/
            if((iw_syn_flags[mod_voice] & SYN_PLAY_EFFECT) || is_rhythm_voice)
                {
                syn_get_instrument(car_voice, &bank, &inst);
                if(bank != TYPE_PERC)
                    syn_play_voice(mod_voice,FM_EMUL);
                }
            }

        /** Play carrier all the time **/
        syn_play_voice(car_voice,FM_EMUL);
        }
    else /** keyon is now off **/
        {
        syn_start_release(mod_voice);        
        syn_start_release(car_voice);        
        }
}

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

FUNCTION DEFINITION: 
update_percussion - Changes state of INTERWAVE voices if FM percussion register
    has been modified.

DESCRIPTION:
    update_percussion gets called when the percussion enable bit in the 
    perc register on the ADLIB part has been changed.  This is bit 20 in
    register BDh.  Things_to_do reflects a change in the state of this bit
    but can not distinguish what the change was.  To distinguish the difference
    perc_to_do will have one bit set, PERC_ENABLE_ON or PERC_ENABLE_OFF which
    will reflect the current state of the percussion enable.  If the percussion
    enable is now on, each voice above VOICE_PERC_LOW will have a new instrument
    set cooresponding to what the FM part will play, the modulators of each
    of the 3 channels will have their state (NOISE or EFFECT) cleared, and
    the pending_to_do bits for each of the voices will be modified to update
    both the volume/frequency and instruments (see play_perc()).  

*/
void update_percussion(void)
{
    /** if the perc enable bit is now on **/
    if(perc_to_do & PERC_ENABLE_ON)
        {
        /** assign percussion instrument for all perc voices **/
        syn_set_instrument(VOICE_HIHAT,  TYPE_PERC, INST_HIHAT);
        syn_set_instrument(VOICE_TOPCYM, TYPE_PERC, INST_TOPCYM);
        syn_set_instrument(VOICE_TOMTOM, TYPE_PERC, INST_TOMTOM);
        syn_set_instrument(VOICE_SNARE,  TYPE_PERC, INST_SNARE);
        syn_set_instrument(VOICE_BASS,   TYPE_PERC, INST_BASS);
        iw_syn_flags[VOICE_HIHAT] &= ~(SYN_PLAY_NOISE | SYN_PLAY_EFFECT);
        iw_syn_flags[VOICE_TOMTOM] &= ~(SYN_PLAY_NOISE | SYN_PLAY_EFFECT);
        iw_syn_flags[VOICE_PERC_LOW] &= ~(SYN_PLAY_NOISE | SYN_PLAY_EFFECT);
        pending_to_do[VOICE_HIHAT] |= (UPD_INSTRUMENT | UPD_FREQUENCY);
        pending_to_do[VOICE_TOPCYM] |= (UPD_INSTRUMENT | UPD_FREQUENCY);
        pending_to_do[VOICE_TOMTOM] |= (UPD_INSTRUMENT | UPD_FREQUENCY);
        pending_to_do[VOICE_SNARE] |= (UPD_INSTRUMENT | UPD_FREQUENCY);
        pending_to_do[VOICE_BASS] |= (UPD_INSTRUMENT | UPD_FREQUENCY);
        }
    /** if the perc enable bit is now off **/
    else if(perc_to_do & PERC_ENABLE_OFF)
        {
        /**
         ** Instead of resetting the voices here, we'll set a flag and do it
         ** the next time a voice gets called on 
         **/
        pending_to_do[VOICE_HIHAT] |= (UPD_INSTRUMENT | UPD_FREQUENCY);
        pending_to_do[VOICE_TOPCYM] |= (UPD_INSTRUMENT | UPD_FREQUENCY);
        pending_to_do[VOICE_TOMTOM] |= (UPD_INSTRUMENT | UPD_FREQUENCY);
        pending_to_do[VOICE_SNARE] |= (UPD_INSTRUMENT | UPD_FREQUENCY);
        pending_to_do[VOICE_BASS] |= (UPD_INSTRUMENT | UPD_FREQUENCY);
        }
}

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

FUNCTION DEFINITION: 
check_for_effect - Checks FM parameters to determine if the modulator can 
    be used for noise or an effect voice

DESCRIPTION:
    Sets the INTERWAVE modulator voice to:
        NOTHING - when we are in sine wave mode
        NOISE - not sine mode & when mod volume and feedback say there is noise
        EFFECT - not sine mode & not noise

    If it can, sets up all parameters for the synth so it can be played
    Otherwise, clears flags and programs envelope normally

EXPECTS:
    envelope - to hold valid UN-programed envelope values.
        This will call syn_set_envelope() when appropriate

    fm_voice, car_voice, mod_voice MUST be valid.

*/
void check_for_effect(void)
{
    register unsigned char feedback;

    /**
     ** If I'm in sine wave mode or percs are on on the channel specified,
     ** both voices are used 
     ** and nothing else can be played - so just leave
     **/
    if((chan_ptr->con&0x01) || is_rhythm_voice)
        {
        /** shut both flags off **/
        syn_set_envelope(fm_voice,&envelope);
        syn_set_iw_atten(fm_voice,lvol,rvol);
        iw_syn_flags[mod_voice] &= ~(SYN_PLAY_NOISE | SYN_PLAY_EFFECT);
        return;
        }

    /**
     ** Get the feedback and volume for this voice.
     ** See if they constitute noise or not.
     ** If they don't, we can play effect on the modulator.
     ** (Since we ARE in FM mode)
     **/
    feedback = (unsigned char)(chan_ptr->con >> 1) & (unsigned char)0x07;
    if(feedback < 6) // || (mod_ptr->level >= 0x0a)) /** TRUE if not noise **/
        {
        iw_syn_flags[mod_voice] &= ~SYN_PLAY_NOISE; /** off with noise **/
        iw_syn_flags[mod_voice] |= SYN_PLAY_EFFECT; /** on with effect **/
        }
    else
        {
        iw_syn_flags[mod_voice] &= ~SYN_PLAY_EFFECT; /** off with effect **/
        iw_syn_flags[mod_voice] |= SYN_PLAY_NOISE; /** on with noise **/
        }

    /**
     ** It is ALWAYS safe to update the carrier here, it is the modulator
     ** that is under question
     ** If the envelope belongs to the modulator, get the carrier's for the
     ** calculations later
     **/
    if(fm_voice == car_voice)
        {
        syn_set_envelope(car_voice,&envelope);
        syn_set_iw_atten(fm_voice,lvol,rvol);
        }
    else
        {
        syn_get_iw_atten(car_voice,&lvol,&rvol);
        syn_set_iw_atten(car_voice,lvol,rvol);
        syn_get_envelope(car_voice,&envelope);
        syn_set_envelope(mod_voice,&envelope);
        }
}

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

FUNCTION DEFINITION: 
set_channel - Configures modulator voice based on what it has been selected to 
    play

*/
void set_channel(void)
{
    int bank;
    int instrument;
    unsigned long frequency;
    unsigned int atten;
	unsigned int tval;
    /**
     ** break up the rest of the code into categories
     ** Here, the instrument selection is based on whether there is noise
     ** or not:
     **   If there is noise, the mod instrument is noise
     **   If there is no noise, the mod instrument is the carrier's instrument
     ** Also, the frequency is based on noise:
     **   If there is noise, use FREQ_NOISE
     **   If there is no noise, use carrier's frequency changed a little
     **/
    if(iw_syn_flags[mod_voice] & SYN_PLAY_NOISE)
        {
		tval = mod_ptr->level;
        if(((chan_ptr->con >> 1) & 0x07) == 7)
            atten = tval;
        else
            atten = tval+4; // less noise for vol of 6

        syn_get_envelope(car_voice, &envelope);
		if(atten > 63)
			atten = 63; // don't want to wrap
        atten <<= 10;
        syn_get_iw_atten(car_voice,&lvol,&rvol);
		if(atten > (0xffff-rvol)) 
			rvol = lvol = 0xffff;
		else 
			{
			rvol += atten;
			lvol = rvol;
			}
        if (car_ptr->param & 0x20)
            syn_set_sustain(mod_voice,TRUE);
        else
            syn_set_sustain(mod_voice,FALSE);
        frequency=FREQ_NOISE;
        instrument = INST_NOISE;
        bank = BANK_NOISE;
        }
    else if(iw_syn_flags[mod_voice] & SYN_PLAY_EFFECT)
        {
        if (car_ptr->param & 0x20)
            syn_set_sustain(mod_voice,TRUE);
        else
            syn_set_sustain(mod_voice,FALSE);
        frequency = get_effect_frequency(syn_get_frequency(car_voice));
        syn_get_instrument(car_voice, &bank, &instrument);
        syn_get_envelope(car_voice, &envelope);
        syn_get_iw_atten(car_voice,&lvol,&rvol);
        }
    else
        goto done_set_channel;

    /** 
     ** use the envelope already set for the carrier
     ** on the modulator and use correct instrument selection
     **/
    syn_set_frequency(mod_voice,frequency);
    syn_set_envelope(mod_voice,&envelope);
    syn_set_iw_atten(mod_voice,lvol,rvol);
    /** 
     ** Don't want to reselect a different wave based on frequency 
     ** changes if the voice is running.  This can choose a different magic
     ** number not matching the currently playing wave...
     **/
    if((!syn_voices_running()) || (things_to_do & UPD_INSTRUMENT)) 
        syn_set_instrument(mod_voice,bank,instrument);

done_set_channel:
    syn_upload_envelopes(fm_voice,UPLOAD_FM);
    
}

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

FUNCTION DEFINITION: 
get_effect_frequency - Calculates new frequency for mod in effect mode

RETURNS - unsigned long - IW frequency value

*/
unsigned long pascal get_effect_frequency(unsigned long freq)
{
unsigned long new_freq;

#ifdef HALFREQ
    return(freq>>1);
#else
    /**
     ** Make sure that new freq is in same range as base freq so
     ** wave num is guaranteed to be the same. Else it might sound
     ** off key.
     **/
    new_freq = (freq>>8)+freq;
    if ((new_freq>>15) != (freq>>15))
        new_freq = freq - (freq>>8);

    return(new_freq);
#endif
}

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

FUNCTION DEFINITION:
default_callback - default synth callback if MPU401 is not loaded 

*/
void default_callback()
{
// voice = voice; // Keep compiler happy ...
}

