#pragma inline
/***************************************************************************
*       NAME:  MPU401C.C $Revision: 1.45 $
**      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 EXCLUSIVE_CYMBAL
#define LAYER_PIANO_0
#define SCALE_SYNTOM

#include "mpu401.h"
#include "sbosdefs.h"
#include "sbosdata.h"
#include "synproto.h"
#include "synth.h"
#include "shared.h"
// #include "mtmap.h"

/** Defined in mpu401.asm **/
extern MPU_VOICES mpu_voices[32];
extern MPU_CHANNELS mpu_channels[16];

extern SHARED shared;

unsigned char chan_instrument[16] = {0};
unsigned char rpn_number = 0;
int channel,voice;
unsigned char in_sysex=0;
unsigned char command;
unsigned int args[2];
unsigned int current_age = 0;
int argptr=0;
int args_to_get=0;
unsigned long notes_playing = 0L;
#ifdef DEBUG
unsigned char color = 0;
#endif

unsigned short log_table[128] = {
  4095 /* 0   */,1789 /* 1   */,1533 /* 2   */,1383 /* 3   */,1277 /* 4   */,
  1195 /* 5   */,1127 /* 6   */,1070 /* 7   */,1021 /* 8   */,978  /* 9   */,
  939  /* 10  */,903  /* 11  */,871  /* 12  */,842  /* 13  */,814  /* 14  */,
  789  /* 15  */,765  /* 16  */,743  /* 17  */,722  /* 18  */,702  /* 19  */,
  683  /* 20  */,665  /* 21  */,647  /* 22  */,631  /* 23  */,615  /* 24  */,
  600  /* 25  */,586  /* 26  */,572  /* 27  */,558  /* 28  */,545  /* 29  */,
  533  /* 30  */,521  /* 31  */,509  /* 32  */,498  /* 33  */,487  /* 34  */,
  476  /* 35  */,466  /* 36  */,455  /* 37  */,446  /* 38  */,436  /* 39  */,
  427  /* 40  */,418  /* 41  */,409  /* 42  */,400  /* 43  */,391  /* 44  */,
  383  /* 45  */,375  /* 46  */,367  /* 47  */,359  /* 48  */,352  /* 49  */,
  344  /* 50  */,337  /* 51  */,330  /* 52  */,323  /* 53  */,316  /* 54  */,
  309  /* 55  */,302  /* 56  */,296  /* 57  */,289  /* 58  */,283  /* 59  */,
  277  /* 60  */,271  /* 61  */,265  /* 62  */,259  /* 63  */,253  /* 64  */,
  247  /* 65  */,242  /* 66  */,236  /* 67  */,231  /* 68  */,225  /* 69  */,
  220  /* 70  */,215  /* 71  */,210  /* 72  */,205  /* 73  */,199  /* 74  */,
  195  /* 75  */,190  /* 76  */,185  /* 77  */,180  /* 78  */,175  /* 79  */,
  171  /* 80  */,166  /* 81  */,162  /* 82  */,157  /* 83  */,153  /* 84  */,
  148  /* 85  */,144  /* 86  */,140  /* 87  */,135  /* 88  */,131  /* 89  */,
  127  /* 90  */,123  /* 91  */,119  /* 92  */,115  /* 93  */,111  /* 94  */,
  107  /* 95  */,103  /* 96  */,100  /* 97  */,96   /* 98  */,92   /* 99  */,
  88   /* 100 */,85   /* 101 */,81   /* 102 */,77   /* 103 */,74   /* 104 */,
  70   /* 105 */,67   /* 106 */,63   /* 107 */,60   /* 108 */,56   /* 109 */,
  53   /* 110 */,50   /* 111 */,46   /* 112 */,43   /* 113 */,40   /* 114 */,
  37   /* 115 */,33   /* 116 */,30   /* 117 */,27   /* 118 */,24   /* 119 */,
  21   /* 120 */,18   /* 121 */,15   /* 122 */,12   /* 123 */,9    /* 124 */,
  6    /* 125 */,3    /* 126 */,0    /* 127 */,
};

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

FUNCTION_DEFINITION:
parse_midi - Parses the incomming midi data stream and takes the appropriate
    action

DESCRIPTION:
  Parses the midi data stream.  All sysex messages are ignored.  The correct
  number of arguments are collected and the midi command routine is called.
  Also implements running status for midi command bytes.

*/
int got_command;
void parse_midi(void)
{
    register unsigned int data = _AX;
    got_command = (data & 0x80);

    /**
     ** Ignore all sysex messages
     **/
    if(data == 0xf0) /** What turns in sysex on?? **/
        in_sysex = 1;
    else if(got_command) /** Any command turns sysex off (including EOX) **/
        in_sysex = 0;

    if(in_sysex)
        return;

    /**
     ** If data is a command byte OR
     ** if data is a data byte and there are no args to get (running status)
     **/
    if(got_command || (!args_to_get && ((data & 0x80) == 0)))
        {
        /**
         ** if data is command - save command and current channel information 
         ** otherwise use old info and continue
         **/
        if(got_command)
            {
            command = data & 0xf0;
            channel = data & 0x0f;
            }
        if((command == 0xc0) || (command == 0xd0))
            args_to_get = 1;
        else
            args_to_get = 2;
        if(got_command)
            return;
        }

    if(args_to_get)
        {
        args[argptr] = data;
        argptr++;
        args_to_get--;
        if(args_to_get == 0)
            {
            argptr = 0;
            if(command == 0x80)
                midi_note_off();
            else if(command == 0x90)
                midi_note_on();
            else if(command == 0xb0)
                midi_control_change();
            else if(command == 0xc0)
                midi_program_change();
            else if(command == 0xe0)
                midi_pitch_bend();
            }
        }
}

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

FUNCTION_DEFINITION:
mpu_done_release callback - callback routine from the synth interrupt handler 
notifying when a voice has finished the release.

EXPECTS:
    Expects but does not require that the notes_playing bit is on for the
    specified voice

*/
void mpu_done_release_callback(void)
{
    voice = _AX;
    mpu_reset_voice();
}

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

FUNCTION DEFINITION:
mpu_start_release_callback - Callback established with the synth code to
  notify the MPU401 code when a voice has started the release.

DESCRIPTION:
  The notification that a voice has begun a release is helpful in the voice
  stealing routines.

EXPECTS:
  A voice must be playing in order to start a release.

MODIFIES:
  voice flags

SEE ALSO:
  mpu_done_release_callback, mpu_get_voice

*/
void mpu_start_release_callback(void)
{
    mpu_voices[_AX].flags |= VOICE_IN_RELEASE;
}

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

FUNCTION_DEFINITION:
midi_note_off - Handles a MIDI note off on the spedified channel and note.

DESCRIPTION:
  A note off is initiated by starting a release on a note that is in an
  earlier part of its envelope.  A note off will not occur if the pedal has
  been pressed for that channel.  In that case, a flag will be set and the note
  will start the release when the pedal is released.  Otherwise, the note off
  will effect the specified note and channel if a note off has not already 
  been received for that note and channel.  This allows for multiple note ons
  and note offs for a single note.

EXPECTS:
  Expects but does not require that the specified note is playing.

MODIFIES:
  Running state of note.

SEE ALSO:
  midi_note_on

*/
void pascal midi_note_off(void)
{
    int note = args[0];
    MPU_VOICES *vptr;
    
    /**
     ** If the voice is playing AND its the right note AND it is not 
     ** in the release AND it is not sustaining because of the pedal
     ** AND it has the ability to sustain (not one shot)...
     **   If the pedal is down, ignore the note off and set a flag
     **   If the pedal is up, start the release and set a flag
     ** Else try the next voice
     **/
    voice = 0;
    while(voice < NUM_VOICES)
        {
        vptr = &mpu_voices[voice];
        if(mpu_voice_is_on())
        if(vptr->note == note)
        if((vptr->flags&(VOICE_IN_RELEASE|VOICE_PEDAL_SUSTAIN))==0)
        if(syn_get_sustain(voice)) /** not one shot **/
        if(mpu_channels[channel].flags & CHAN_PEDAL) /** pedal says sustain **/
            {
            vptr->flags |= VOICE_PEDAL_SUSTAIN;
            break; /** pedal says sustain **/
            }
        else
            {
            vptr->flags |= VOICE_IN_RELEASE;
            syn_start_release(voice);
            break;
            }
        voice++;
        }
}

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

FUNCTION_DEFINITION:
midi_note_on - Handles a MIDI note on for the spedified channel and note.

DESCRIPTION:
  This routine will perform all that is necessary to create a playing note in
  the synth.  If the velocity of the note on is zero, a note off is executed.
  If the channel for the note on is nine, the GM percussion bank is used instead
  of the GM melodic bank.  This will reset all voice parameters to the default,
  program the frequency, envelope, volume/pan of the voice and start the voice
  playing.

MODIFIES:
  all voice structures

SEE ALSO:
  midi_note_off

*/
void pascal midi_note_on(void)
{
    unsigned long frequency;
    int bank;
    int inst;
    int actual_inst;
    unsigned int note = args[0];
    int velocity = args[1];
    MPU_VOICES *vptr;

    if(velocity == 0)
        {
        midi_note_off();
        return;
        }

    if(channel == 9)
        {
        bank = TYPE_PERC;
        inst = note;

        /**
         ** Do a quick exclusion on the hihats
         ** Well, only stop the open hihat if any other hihat plays...
         **/
#ifdef EXCLUSIVE_CYMBAL
        if((note == 42) || (note == 44) || (note == 46))
            {
            /** see if there is an open hihat playing **/
            for(voice=0;voice<NUM_VOICES;voice++)
                {
                vptr = &mpu_voices[voice];
                if(vptr->note == 46)
                if(vptr->channel == 9)
                    {
                    vptr->flags |= VOICE_IN_RELEASE;
                    syn_start_release(voice);
                    }
                }
            }
#endif 

        /**
         ** Range check the percs - not all can be played...
         **/
        if(inst >= 88)
            return;
        }
    else
        {
        bank = TYPE_MELODIC;
        inst = chan_instrument[channel];

        /**
         ** Do a quick layering on the first piano
         ** If the velocity is high, use bright piano instead of acoustic
         ** This is the same velocity constant as the kernel
         **/
#ifdef LAYER_PIANO_0
        if((velocity > 90) && (inst == 0))
            inst = 1;
#endif

        /**
         ** This will implement the frequency scaling for the synth tom ONLY!  
         ** Providing the scaling is always 512 and never changes, this
         ** should be OK...
         ** The equation is:
         **
         **                 512 * (old note - root)
         **     new note =  -----------------------
         **                          1024
         **
         ** where root is middle C (60) and the 512 is the frequency scale
         **/
#ifdef SCALE_SYNTOM
        if(inst == 118) /** synth tom **/
            note = 60 + ((note-60)>>1);     /** it really is the same... **/
#endif
        }

    current_age++;
    frequency = mpu_get_frequency(note);
    voice = mpu_get_voice(note);
    mpu_reset_voice();
    vptr = &mpu_voices[voice];
    vptr->age     = current_age;
    vptr->note    = note;
    vptr->channel = channel;
    vptr->velocity= velocity;
    //notes_playing |= (1L<<voice); /** turn on the bit **/
    asm mov ax,voice
    asm bts notes_playing,ax
    syn_set_frequency(voice,frequency);
    actual_inst = syn_set_instrument(voice,bank,inst);

   /**
    ** Play the perc at the frequency of the note hit on the keyboard 
    ** taking into account our remaping strategy
    **/
   if (bank == TYPE_PERC)
      {
      frequency = mpu_get_frequency(actual_inst); /** for right perc freq **/
      syn_set_frequency(voice,frequency);
      }

    syn_upload_envelopes(voice,UPLOAD_MPU);
    mpu_set_voice_volume();
    syn_prog_frequency(voice,channel);
    syn_set_mpu_sustain(voice);
    syn_play_voice(voice,MPU401_EMUL);
}

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

FUNCTION_DEFINITION:
mpu_get_voice - Allocate a voice in the synth for the next note on

DESCRIPTION:
  Tries to use a smart algorithm to allocate a voice:
    If all voices are currently playing, search the channel to see if there 
      is already the note playing that is requested.  If there is, use that
      voice.  If there is more than one, use the oldest.
    Otherwise, search all voices again and use the first empty one, or the
      oldest voice that is in the release phase of its envelope, or the oldest
      voice if there are none in the release phase, in that order of priority.

SEE ALSO:
  midi_note_on

RETURNS - int voice allocated

*/
int pascal mpu_get_voice(unsigned char note)
{
    int best_age = -1;
    int best_voice = 0;
    int is_a_voice_inrel = 0;
    MPU_VOICES *vptr;

    /**
     ** See if this note is already playing and use that one
     ** Only if all voices are used...
     ** If there are multiples of the same note, use the oldest
     **/
    if(notes_playing == 0xFFFFFFFF)
        {
        /**
         ** Everytime we voice steal, lets blink the overscan
         **/
#ifdef DEBUG
        asm mov dx,3daH
        asm in al,dx
        asm mov dx,3c0h
//        ; select overscan
        asm mov al,31h
        asm out dx,al
//        ; select color
        asm mov al,color
        asm out dx,al
        color++;
        if(color == 32) color = 0;
#endif
        voice = 0;
        while(voice < NUM_VOICES)
            {
            vptr = &mpu_voices[voice];
            if(vptr->channel == channel)
            if(vptr->note == note)
            if(vptr->age < best_age)
                {
                best_age = vptr->age;
                best_voice = voice;
                }
            voice++;
            }
        if(best_age != -1)
            goto ret_best_voice;
        }

    /**
     ** See if there is an unused voice
     ** If not, use the oldest voice in release
     ** If no voices in release, use the oldest sustaining voice
     **/
    voice = 0;
    while(voice < NUM_VOICES)
        {
        vptr = &mpu_voices[voice];
        if(voice_is_playing() == 0)
            {
            best_voice = voice; /** use a voice if its free **/
            goto ret_best_voice;
            }
        else
            {
            if(!is_a_voice_inrel)
                {
                if(vptr->age < best_age)
                    {
                    best_voice = voice;
                    best_age = vptr->age;
                    }
                }
            if(vptr->flags & VOICE_IN_RELEASE)
                {
                if(is_a_voice_inrel==0)/**first time finding release - clear **/
                    best_age = -1; /* status so we only pick one that is **/
                is_a_voice_inrel = 1;
                if(vptr->age < best_age)
                    {
                    best_voice = voice;
                    best_age = vptr->age;
                    }
                }
            }
        voice++;
        }
ret_best_voice:
    return best_voice;
}

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

FUNCTION DEFINITION:
mpu_set_voice_volume - sets a voice volume for a voice

DESCRIPTION:
  Uses the offset register attenuation method of volume attenuation and
  panning for a voice.

MODIFIES:
  Volume and pan for a voice

*/
void pascal mpu_set_voice_volume(void)
{
    unsigned int pan,lvol,rvol;
    MPU_CHANNELS *mptr;

    /** 
     ** Add up attenuations
     ** 0 from GM is off 
     ** log_table[0] = 0x0fff;
     **/
    mptr = &mpu_channels[channel];
    rvol = (log_table[mpu_voices[voice].velocity] << 1) +
           (log_table[mptr->volume] << 1) +
           (log_table[mptr->expression] << 1) +
            log_table[MPU_ATTEN] +
            syn_get_voice_atten(voice) +
            (shared.synth_atten<<5);
//fff is off
//000 is full

    lvol = rvol;
    pan = (mptr->pan);

    /**
     ** To rail these: if pan is 7f, make lvol 0x0fff
     ** if pan is 0, make rvol 0x0fff
     **/
    lvol += ((log_table[0x7f-pan]) >> 1);
    rvol += (log_table[pan] >> 1);

    if(rvol > 0x0fff) rvol = 0xfff;
    if(lvol > 0x0fff) lvol = 0xfff;

    /**
     ** Now, make volume an attenuation value from 0 to fff0
     **/
    rvol = rvol << 4;
    lvol = lvol << 4;
//fff0 is off
//0000 is full

    syn_set_iw_atten(voice,lvol,rvol);
}

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

FUNCTION_DEFINITION:
midi_control_change - parses the controller number for a controller change 
message and acts appropriately

DESCRIPTION:
  Implements only a few of the control changes listed in the MIDI spec.

*/
void pascal midi_control_change(void)
{
    int numbr = args[0];
    int val = args[1];
    MPU_CHANNELS *mptr;

    mptr = &mpu_channels[channel];

    if (numbr == 0x06)
        {
        if((rpn_number == 0x000) && (mptr->flags & CHAN_RPN_LAST))
            mptr->pb_sensitivity = (unsigned int)val;
        }
    else if (numbr == 0x07)
        {
        mptr->volume = val;
        mpu_update_channel_volume();
        }
    else if (numbr == 0x0A)
        {
        mptr->pan = val;
        mpu_update_channel_volume();
        }
    else if (numbr == 0x0B)
        {
        mptr->expression = val;
        mpu_update_channel_volume();
        }
    else if (numbr == 0x40)
        {
        if(val > 0x64)
            mpu_set_pedal(TRUE);
        else
            mpu_set_pedal(FALSE);
        }
    else if (numbr == 0x60)
        {
        if((rpn_number == 0x0000) && (mptr->flags & CHAN_RPN_LAST))
            mptr->pb_sensitivity++;
        }
    else if (numbr == 0x61)
        {
        if((rpn_number == 0x0000) && (mptr->flags & CHAN_RPN_LAST))
            mptr->pb_sensitivity--;
        }
    else if (numbr == 0x62)
        {
        mptr->flags &= ~CHAN_RPN_LAST;
        }
    else if (numbr == 0x63)
        {
        mptr->flags &= ~CHAN_RPN_LAST;
        }
    else if (numbr == 0x64)
        {
        mptr->flags |= CHAN_RPN_LAST;
        rpn_number &= 0xff00;
        rpn_number |= (unsigned int)val;
        }
    else if (numbr == 0x65)
        {
        mptr->flags |= CHAN_RPN_LAST;
        rpn_number &= 0x00ff;
        rpn_number |= (unsigned int)(val<<8);
        }
    else if (numbr == 120)
        {
        for(voice=0;voice<NUM_VOICES;voice++)
            syn_stop_voice(voice);
        }
    else if (numbr == 121)
        {
        mpu_reset_channels();
        }
    else if ((numbr >= 123) && (numbr <= 127))
        {
        midi_all_notes_off();
        }
}

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

FUNCTION_DEFINITION:
mpu_set_pedal - set a flag telling the note off routine that this channel has
  a pedal pressed down

DESCRIPTION:
  Pedal does not tell voice to sustain.  It only stops a voice from doing
  a release on a note off.

MODIFIES:
  channel flags

SEE ALSO:
  midi_note_off

*/
void pascal mpu_set_pedal(int onoff)
{
    if(onoff)
        mpu_channels[channel].flags |= CHAN_PEDAL;
    else
        {
        mpu_channels[channel].flags &= ~CHAN_PEDAL;
        for(voice = 0; voice < NUM_VOICES; voice++)
            {
            if(mpu_voice_is_on())
            if(mpu_voices[voice].flags & VOICE_PEDAL_SUSTAIN)
                {
                mpu_voices[voice].flags |= VOICE_IN_RELEASE;
                syn_start_release(voice);
                }
            }
        }
}

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

FUNCTION_DEFINITION:
mpu_update_channel_volume - Go through each voice on the the channel and 
  updates the volume.

DESCRIPTION:
  Performs an update on a channel volume voice by voice if there has been a
  change in expression, master volume, pan.

MODIFIES:
  voice volume

SEE ALSO:
  mpu_set_voice_volume

*/
void pascal mpu_update_channel_volume(void)
{
    voice = 0;
    while(voice < NUM_VOICES)
        {
        if(mpu_voice_is_on())
        if(!(mpu_voices[voice].flags & VOICE_IN_RELEASE))
            mpu_set_voice_volume();
        voice++;
        }
}

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

FUNCTION_DEFINITION:
midi_program_change - Assigns an instrument to a channel

MODIFIES:
  instrument channel array

*/
void pascal midi_program_change(void)
{
//chan_instrument[channel] = mt_to_gm[args[0]];
chan_instrument[channel] = args[0];
}

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

FUNCTION DEFINITION:
midi_pitch_bend - Performs a pitch bend on the specified channel of the
  specified amount.

DESCRIPTION:
  Does a LOG table lookup to change the frequency of a note a part of a 
  semitone.

MODIFIES:
  voice fc bend multiplier

SEE ALSO:
  asm_get_bend

*/
void pascal midi_pitch_bend(void)
{
    long pb_value;
    int bend_down = 0;
    unsigned int lsb = (unsigned int)args[0];
    unsigned int msb = (unsigned int)args[1];

    pb_value = (lsb | (msb<<7)) - 8192L; /** pos/neg pitch raw value **/
    if(pb_value < 0L)
        {
        bend_down = 1;
        pb_value = -pb_value;
        }
    pb_value = pb_value * mpu_channels[channel].pb_sensitivity; 
    pb_value = mpu_get_bend(pb_value);
    if(bend_down)
        pb_value = 1048576L / pb_value;
    mpu_channels[channel].fc_bend_mult = pb_value;
    mpu_update_channel_frequency();
}

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

FUNCTION DEFINITION:
mpu_full_reset - Stops each voice and resets all controllers to a default value

MODIFIES:
  all channel and voice structures

SEE ALSO:
  mpu_reset_channels, mpu_reset_voice

*/
void pascal mpu_full_reset(void)
{
    /**
     ** Global initialization
     **/
    current_age = 0;

    /**
     ** Channel initialization
     **/
    mpu_reset_channels();

    /**
     ** Voice initialization
     **/
    for(voice = 0; voice < NUM_VOICES; voice++)
        mpu_reset_voice();
}

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

FUNCTION DEFINITION:
midi_all_notes_off - Notifies each voice to start its release

DESCRIPTION:
  If a voice is playing and it is not already in the release phase of its 
  envelope, start the release.

MODIFIES:
  running state of all voices

SEE ALSO:
  mpu_reset_voice

*/
void pascal midi_all_notes_off(void)
{
    int avoice;
    MPU_VOICES *vptr;

    for(avoice=0;avoice<NUM_VOICES;avoice++)
        {
        voice = avoice;
        vptr = &mpu_voices[avoice];
        if(voice_is_playing())
        if(!(vptr->flags & VOICE_IN_RELEASE))
            {
            channel = vptr->channel;
            args[0] = vptr->note;
            midi_note_off();
            }
        }
}

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

FUNCTION DEFINITION:
mpu_reset_voice - Stops the running state of a voice and resets to default

DESCRIPTION:
  Stops a voice in hardware and resets all local flags to default state.  This
  can be called on a stopped voice.

MODIFIES:
  running state of a voice

SEE ALSO:
  midi_all_notes_off

*/
void pascal mpu_reset_voice(void)
{
    syn_stop_voice(voice);
    mpu_voices[voice].flags = 0;

    asm mov ax,voice
    asm btr notes_playing,ax

    // notes_playing &= ~(1L<<voice);
}

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

FUNCTION DEFINITION:
mpu_reset_channels - Resets all channel values to default

MODIFIES:
  channel parameters

*/
void pascal mpu_reset_channels(void)
{
    int tchan;
    MPU_CHANNELS *mptr;
    for(tchan=0;tchan<16;tchan++)
        {
        mptr = &mpu_channels[tchan];
        mptr->flags          = 0;
        mptr->fc_bend_mult   = 1024;
        mptr->pb_sensitivity = 2;
        mptr->volume         = 100;
        mptr->expression     = 100;
        mptr->pan            = 0x40;
        }
}

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

FUNCTION DEFINITION:
mpu_update_channel_frequency - Updates the frequency on all voices playing
  on a single channel.

MODIFIES:
  voice frequency

*/
void pascal mpu_update_channel_frequency(void)
{
    voice = 0;
    while(voice < NUM_VOICES)
        {
        if(mpu_voice_is_on())
            syn_prog_frequency(voice,channel);
        voice++;
        }
}

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

FUNCTION DEFINITION:
mpu_voice_is_on - Returns true if the current voice is playing and is playing
  on the current channel.

EXPECTS:
  voice and channel to be setup

RETURNS:
  TRUE - voice is playing and voice's channel is channel

*/
int pascal mpu_voice_is_on(void)
{
    if(voice_is_playing() && (mpu_voices[voice].channel == channel))
        return 1;
    return 0;
}

