/*

This software module was originally developed by 

Mike Guzewicz (E-mu Systems)


in the course of development of the MPEG-4 Audio (ISO/IEC 14496-3) standard. 
This software module is an implementation of a part of one or more 
MPEG-4 Audio (ISO/IEC 14496-3) tools as specified by the MPEG-4 Audio 
(ISO/IEC 14496-3). ISO/IEC gives users of the MPEG-4 Audio (ISO/IEC 14496-3) 
free license to this software module or modifications thereof for use 
in hardware or software products claiming conformance to the MPEG-4 Audio 
(ISO/IEC 14496-3). Those intending to use this software module in hardware 
or software products are advised that its use may infringe existing patents. 
The original developer of this software module and his/her company, the 
subsequent editors and their companies, and ISO/IEC have no liability for 
use of this software module or modifications thereof in an implementation. 
Copyright is not released for non MPEG-4 Audio (ISO/IEC 14496-3) conforming 
products. E-mu Systems retains full right to use the code for his/her own 
purpose, assign or donate the code to a third party and to inhibit third 
parties from using the code for non MPEG-4 Audio (ISO/IEC 14496-3) 
conforming products. This copyright notice must be included in all copies 
or derivative works. 

Copyright (C) 1997 E-mu Systems, Inc.

*/
     /*********************************************************************
     
     sfe_sfnav.cpp
     
          
     *********************************************************************/


/******************************************************************************
*  @(#)sfnav.cpp	1.1 12:06:45 3/15/95 12:06:47
*
* Filename: sfnav.cpp
*
* Description:  The SoundFont Navigator and related methods
*
******************************************************************************
*/

#ifndef __SFNAV_CPP
#define __SFNAV_CPP

/**************
* Includes
**************/

#include "sfe_sfnav.h"
#include "sfe_sfenum.h"
#include "sfe_sfdata.h"
#include "sfe_hydra.h"
#include "sfe_emuerrs.h"
#include <math.h>
#include <string.h>

#include "sfe_sflookup.h"

/******************************************************************************
*
* Function: SoundFontNavigator::SoundFontNavigator
*
* Implemetation Notes:  Constructor for SoundFont object
*
********************************************************************************
*/
SoundFontNavigator::SoundFontNavigator(void)
{
  Reset();
  InitLookup();
}

/******************************************************************************
*
* Function: SoundFontNavigator::~SoundFontNavigator
*
* Implemetation Notes:  Destructor for SoundFont object
*
********************************************************************************
*/
SoundFontNavigator::~SoundFontNavigator(void)
{
  Reset();
  ClearLookup();
}

/******************************************************************************
*
* Function: SoundFontNavigator::Reset
*
* Implemetation Notes:  Initialization of the Navigator object
*
********************************************************************************
*/
void  SoundFontNavigator::Reset(void)
{
  phfNav          = NULL;
  wOsc           = 0;
}

/******************************************************************************
*
* Function: SoundFontNavigator::Navigate
*                               For SoundFont 2.0
*
* Implemetation Notes:  Get articulation data from a SoundFont in memory
*
*******************************************************************************
*/

void  SoundFontNavigator::Navigate(WORD uiSFID, WORD uiKey, WORD uiVel)
{

  WORD  pbagIndex, pgenIndex, ibagIndex, igenIndex;

  WORD  uiPBagNdxL, uiPBagNdxH, uiIBagNdxL, uiIBagNdxH;
  WORD  uiPGenNdxL, uiPGenNdxH, uiIGenNdxL, uiIGenNdxH;
  WORD  uiGenOper,  uiInstGenOper;
  SHORT   iGenAmt,    iInstGenAmt;
  BOOL   bGlobalPrstParams, bGlobalInstParms;
  sfData *psfVectorCurrOsc;

  SFSAMPLEHDRPTR pshSHdrCurrSmpl;
  SFGENLISTPTR   psfCurrPGen;
  SFGENLISTPTR   psfCurrIGen;

  const SFLOOKUP * soundFontLookup;

  if (phfNav == NULL)
    return;

  if ((soundFontLookup = GetLookup()) == NULL)
    return;

  if (uiSFID > (phfNav->awStructSize[prstHdr] - 1))
    return;

  //////////////////////////////////////////////////////////
  // We begin navigation at the Preset Header index 
  // set up by GetSFNum() and stored in uiSFID, in the SoundFont 
  // instance (or HydraClass pointer) setup by SetHydraFont() 
  // and stored in phfNav. See SetHydraFont(), GetSFNum()
  //////////////////////////////////////////////////////////

  //////////////////////////////////////////////////////////
  // The distance from pPHdr[uiSFID] to pPHdr[uiSFID + 1]   
  // indicates the number of LAYERS for the current PRESET
  //////////////////////////////////////////////////////////
  uiPBagNdxL = phfNav->pPHdr[uiSFID].wBagNdx;     // Set the indices for this
  uiPBagNdxH = phfNav->pPHdr[uiSFID + 1].wBagNdx; //   preset number
  wOsc      = 0;                          // Reset the # of Osc's needed

  // Assign the key and velocity values to the default vector 
  phfNav->sfDefault.shKeynum   = uiKey;
  phfNav->sfDefault.shVelocity = uiVel;

  //// Preset Layers ////

  //////////////////////////////////////////////////////////
  // Clear current layer data
  // Being ADDITIVE RELATIVE generators, the default value for all
  // PRESET generators MUST be the additive identity (0)
  //////////////////////////////////////////////////////////
  memset((void *)&sfPresetData, 0, sizeof(sfPresetData));
  memset((void *)linkFound, 0, MAX_SAMPLES);
  nextOscLinkCheck = 0;

  for (pbagIndex = uiPBagNdxL; pbagIndex < uiPBagNdxH; pbagIndex++)
  {
    //////////////////////////////////////////////////////////
    // The distance from pPBag[pbagIndex] to 
    // pPBag[pbagIndex + 1] indicates the      
    // number of GENERATORS for the current LAYER
    //////////////////////////////////////////////////////////
    uiPGenNdxL = phfNav->pPBag[pbagIndex].wGenNdx;
    uiPGenNdxH = phfNav->pPBag[pbagIndex + 1].wGenNdx;
    bGlobalPrstParams = TRUE;

    //// Set the Current SoundFont Layer to the Default ////
    sfCurrPreset = sfPresetData;

    //// Preset Generator operators ////

    for (pgenIndex = uiPGenNdxL; pgenIndex < uiPGenNdxH; pgenIndex++)
    {
      // Assign a pointer to the position of the current generator.
      // Reference the data from this pointer in the future code.
      // Makes for smaller and faster code.
      psfCurrPGen = &(phfNav->pPGen[pgenIndex]);
      uiGenOper = psfCurrPGen->sfGenOper;
      iGenAmt   = psfCurrPGen->unAmt.shAmount;

      /////////////////////////////////////////////////////
      // Overriding keynum/velocity found at LAYER level
      // Overriding keynum/velocity here affects LAYER and 
      // future INSTRUMENT split decisions ONLY! They are NOT
      // RELATIVE values to be added to the INSTRUMENT
      // or DEFAULT keynum/velocity
      /////////////////////////////////////////////////////
      if (uiGenOper == keynum) 
        ;//uiKey = iGenAmt;

      else if (uiGenOper == velocity) 
	;//uiVel = iGenAmt;

      /////////////////////////////////////////////////////
      // Check the key and velocity ranges
      /////////////////////////////////////////////////////

      else if ((uiGenOper == keyRange) && 
              ((uiKey < psfCurrPGen->unAmt.stRange.byLo) ||
               (uiKey > psfCurrPGen->unAmt.stRange.byHi)))
      {
        /////////////////////////////////////////////////////
        // A keyrange was detected, however the current key does not lie
        // within that range. Off to the next PBAG.
        /////////////////////////////////////////////////////
        bGlobalPrstParams = FALSE;
	break;
      }

      else if ((uiGenOper == velRange) && 
                  ((uiVel < psfCurrPGen->unAmt.stRange.byLo) ||
                   (uiVel > psfCurrPGen->unAmt.stRange.byHi)))

      {
        /////////////////////////////////////////////////////
        // A velocity range was detected, however the current velocity does 
        // not lie within that range. Off to the next PBAG.
        /////////////////////////////////////////////////////
        bGlobalPrstParams = FALSE;
	break;
      }

      else if (uiGenOper == instrument)
      {

        //////////////////////////////////////////////////////////
	// sGenAmt equals the Instrument token, that means
        // an instrument was found within the layer. This is the terminal
        // token which contains information for all samples and split level
        // articulation parameters 
        //////////////////////////////////////////////////////////

        //////////////////////////////////////////////////////////
        // The distance from pInst[iGenAmt] to 
        // pInst[iGenAmt + 1] indicates the      
        // number of GENERATOR SPLITS for the current INSTRUMENT
        //////////////////////////////////////////////////////////
	uiIBagNdxL = phfNav->pInst[iGenAmt].wBagNdx;
	uiIBagNdxH = phfNav->pInst[iGenAmt + 1].wBagNdx;

	//// Assign the current vector pointer ////
	psfVectorCurrOsc = &sfVector[wOsc];

	// Set the current vector to the DEFAULT vector
	*psfVectorCurrOsc = phfNav->sfDefault;

        // Set overriding key/vel given at the layer
        psfVectorCurrOsc->shKeynum   = uiKey;
        psfVectorCurrOsc->shVelocity = uiVel;

        // Copy current to default instrument
        // This data vector contains (or will contain) current default 
        // generator values plus the possibly upcoming Global Split generators.
	sfInstData = *psfVectorCurrOsc;

	for (ibagIndex = uiIBagNdxL; ibagIndex < uiIBagNdxH; ibagIndex++)
	{

          //////////////////////////////////////////////////////////
          // The distance from pInst[iGenAmt] to 
          // pInst[iGenAmt + 1] indicates the      
          // number of GENERATORS for the current SPLIT
          //////////////////////////////////////////////////////////
	  uiIGenNdxL = phfNav->pIBag[ibagIndex].wGenNdx;
	  uiIGenNdxH = phfNav->pIBag[ibagIndex + 1].wGenNdx;
	  bGlobalInstParms = TRUE;

	  //// Check for instrument Multisamples Generator Operators ////

	  for (igenIndex = uiIGenNdxL; igenIndex < uiIGenNdxH; igenIndex++)
	  {
            // Assign a pointer to the position of the current generator.
            // Reference the data from this pointer in the future code.
            // Makes for smaller and faster code.
	    psfCurrIGen = &(phfNav->pIGen[igenIndex]);

	    //// uiIBagNdx is an index to the phfNav->pIGen array ////
	    uiInstGenOper = psfCurrIGen->sfGenOper;
	    iInstGenAmt   = psfCurrIGen->unAmt.shAmount;
  
            // Check for overriding key/vel

	    if (uiInstGenOper == keynum)
	      uiKey = iGenAmt;

	    else if (uiInstGenOper == velocity)
	      uiVel = iGenAmt;

	    //// Check for appropriate key and velocity ranges ////

	    if ((uiInstGenOper == keyRange) || (uiInstGenOper == velRange))
	    {
	      ///////////////////////////////////////////////
	      // If we have several keyRanges/velRanges,   //
	      // and the range does not qualify, we do NOT //
	      // want the sfVector[wOsc] copied to the    //
	      // sfInstData vector, hence:                 //
	      // bGlobalInstParms = FALSE;                 //
	      ///////////////////////////////////////////////

	      if (uiInstGenOper == keyRange)
	      {
	        if ((uiKey < psfCurrIGen->unAmt.stRange.byLo) ||
		    (uiKey > psfCurrIGen->unAmt.stRange.byHi))
	        {
		  bGlobalInstParms = FALSE;
		  break;
	        }
	      }

	      else if ((uiVel < psfCurrIGen->unAmt.stRange.byLo) ||
			    (uiVel > psfCurrIGen->unAmt.stRange.byHi))
              {
	        bGlobalInstParms = FALSE;
	        break;
	      }

	      //////////////////////////////////////////////////////
	      // An instrument listed in Preset layer was found
	      // Since an instrument is DEFINED as the terminal 
	      // token for a single layer, this assignment avoids
              // redundant future loops
	      //////////////////////////////////////////////////////
	      pgenIndex = uiPGenNdxH;
	    }

	    // Check for sampleId
	    else if (uiInstGenOper == sampleId)
	    {

	      //////////////////////////////////////////////////////
              // A sample ID was found. This is the terminal token for a
              // single split which contains the index to the sample 
              // information.
	      //////////////////////////////////////////////////////

              // Assign a pointer to the position of the current sample header.
              // Reference the data from this pointer in the future code.
              // Makes for smaller and faster code.
	      pshSHdrCurrSmpl = &phfNav->pSHdr[iInstGenAmt];

	      //////////////////////////////////////////////////////
	      // Establish sample links
              // Build an array of links based on SoundFont 
              // sample header indices, which will (later) be
              // converted to an array based on OUTPUT VECTOR
              // indices. (ProcessSampleLinks)
	      //////////////////////////////////////////////////////
	      if ((pshSHdrCurrSmpl->sfSampleType&0x7FFF) > monoSample)
	      {
	        psfVectorCurrOsc->shSampleLink = pshSHdrCurrSmpl->wSampleLink;
	        shdrIndexLinks[wOsc] = iInstGenAmt;
		psfVectorCurrOsc->shSampleModes |= LINKED;
                if (pshSHdrCurrSmpl->sfSampleType == rightSample)
		  psfVectorCurrOsc->shSampleModes |= FIRST_LINK;
	      }
	      else
	      {
	        psfVectorCurrOsc->shSampleLink = 0;
		psfVectorCurrOsc->shSampleModes &= ~(FIRST_LINK | LINKED);
	      }

	      // Set the sample addresses accounting for coarse and fine
              // address offsets

	      psfVectorCurrOsc->dwStart =
	       (LONG) psfVectorCurrOsc->dwStart +
	       (LONG) psfVectorCurrOsc->shStartAddrsCoarseOffset * 32768 +
		      pshSHdrCurrSmpl->dwStart;

	      psfVectorCurrOsc->dwEnd =
	       (LONG) psfVectorCurrOsc->dwEnd +
	       (LONG) psfVectorCurrOsc->shEndAddrsCoarseOffset * 32768 +
		      pshSHdrCurrSmpl->dwEnd;
   
	      psfVectorCurrOsc->dwStartloop =
	       (LONG) psfVectorCurrOsc->dwStartloop +
	       (LONG) psfVectorCurrOsc->shStartloopAddrsCoarseOffset * 32768 +
		      pshSHdrCurrSmpl->dwStartloop;
   
	      psfVectorCurrOsc->dwEndloop =
	       (LONG) psfVectorCurrOsc->dwEndloop +
	       (LONG) psfVectorCurrOsc->shEndloopAddrsCoarseOffset * 32768 +
		      pshSHdrCurrSmpl->dwEndloop;

              // Obtain the sample rate, root key and sample tuning correction

	      psfVectorCurrOsc->dwSampleRate = pshSHdrCurrSmpl->dwSampleRate;

	      twoByteUnion originalKeyAndCorrection;

	      if ((psfVectorCurrOsc->shOverridingRootKey >=   0) &&
	          (psfVectorCurrOsc->shOverridingRootKey <  128))
	        originalKeyAndCorrection.byVals.by1 =
			   psfVectorCurrOsc->shOverridingRootKey;

	      else if (pshSHdrCurrSmpl->byOriginalKey <  128)
	        originalKeyAndCorrection.byVals.by1 =
			   pshSHdrCurrSmpl->byOriginalKey;

              else
                originalKeyAndCorrection.byVals.by1 = 60; 

	      originalKeyAndCorrection.byVals.by0 = 
			   (BYTE)pshSHdrCurrSmpl->chFineCorrection;

	      psfVectorCurrOsc->shOrigKeyAndCorr =
                           (SHORT)originalKeyAndCorrection.wVal;

	      // Add in preset level changes
	      // Result is in psfVectorCurrOsc.
	      AddSoundFonts(psfVectorCurrOsc, &sfCurrPreset);

	      wOsc++;
	      psfVectorCurrOsc = &(sfVector[wOsc]);

	      // Do not exceed the allowable samples.
              // As a compromise between quick single-note throughput
              // and decent single-note polyphony, this code limits the
              // total number of SAMPLES PER NOTE-ON to MAX_SAMPLES.
	      if (wOsc == MAX_SAMPLES)
	      {
	        ProcessSampleLinks();
	        return;
	      }

	      // Let's not do this if we are certain of not getting another 
              // vector in this bag.
	      else if (ibagIndex != (uiIBagNdxH - 1))
	      {
                // Copy the contents of the DEFAULT INSTRUMENT generators
                // to the NEXT Vector.
	        *psfVectorCurrOsc = sfInstData;
	      }

	      // Reset previous overriding keynumbers
	      uiKey = sfInstData.shKeynum;
	      uiVel = sfInstData.shVelocity;

              ///////////////////////////////////////////////////
              // This navigator automatically handles sample loop
              // point offsets and root key definitions. So we now
	      // zero out those parameters in previous vector which 
              // have been already processed
              ///////////////////////////////////////////////////

              sfVector[wOsc-1].shStartAddrsCoarseOffset = 0;
              sfVector[wOsc-1].shEndAddrsCoarseOffset = 0;
              sfVector[wOsc-1].shStartloopAddrsCoarseOffset = 0;
              sfVector[wOsc-1].shEndloopAddrsCoarseOffset = 0;
              sfVector[wOsc-1].shOverridingRootKey = -1;

              bGlobalInstParms = FALSE;

	    }
	    else
	    {
              ///////////////////////////////////////////////////
              // A single step lookup/assignment to get the 
              // generator value into the output vector.
              // NOTE this will cause problems if the generator
              // value exceeds the maximum defined value within
              // this software! Hence the sanity checking at load
              // time for illegal generators.
              ///////////////////////////////////////////////////

	      // This step necessary for Big Endian systems
	      if (uiInstGenOper <= endloopAddrsOffset)
		*(LONG*)((BYTE*)&(psfVectorCurrOsc->dwStart) +
                   soundFontLookup[uiInstGenOper]) = (LONG)iInstGenAmt;
              else
	        *(SHORT*)((BYTE*)&(psfVectorCurrOsc->dwStart) +
                   soundFontLookup[uiInstGenOper]) = iInstGenAmt;
	    }
	  } // Loop through all Generators in a SPLIT

          /////////////////////////////////////////////////////////////
          // A future loop for SoundFont MODULATOR SPLITS
          // will be inserted here
          // IE from uiIModNdxL = phfNav->pIBag[ibagIndex].wModNdx;
          //    to   uiIModNdxH = phfNav->pIBag[ibagIndex + 1].wModNdx;
          /////////////////////////////////////////////////////////////

          ///////////////////////////////////////////////////
          // If the split we found was a GLOBAL split, then
          // its parameters must be INCLUDED in the DEFAULT
          // INSTRUMENT vector also.
          ///////////////////////////////////////////////////
	  if (bGlobalInstParms)
	  {
            sfInstData = *psfVectorCurrOsc;
	  }
        } // Loop through all SPLITS of Generators and Modulators

        ///////////////////////////////////////////////////////////////////
        // Here the sample links in the previous instrument are converted 
        // from Sample Header indices (0 to number of SAMPLES in the BANK) 
        // to output vector indices (0 to MAX_SAMPLES-1)
        // Note only samples within a single instrument may be linked!
        ///////////////////////////////////////////////////////////////////
        ProcessSampleLinks();

        // If we found an Instrument, this is NOT the global layer.
        bGlobalPrstParams = FALSE;

      } // Instrument found

      else
      {
        ///////////////////////////////////////////////////
        // A single step lookup/assignment to get the 
        // generator value into the output vector.
        // NOTE this will cause problems if the generator
        // value exceeds the maximum defined value within
        // this software! Hence the sanity checking at load
        // time for illegal generators.
        ///////////////////////////////////////////////////

        // Necessary for 'byte-incoherent' systems
        if (uiGenOper <= endloopAddrsOffset)
          *(LONG*)((BYTE*)&sfCurrPreset.dwStart +
            soundFontLookup[uiGenOper]) = (LONG)iGenAmt;
        else
          *(SHORT*)((BYTE*)&sfCurrPreset.dwStart +
            soundFontLookup[uiGenOper]) = iGenAmt;
      }

    } // Loop through all Generators in a LAYER

    //// Preset Modulator operators ////

    ///////////////////////////////////////////////////////////////
    // A future loop for SoundFont MODULATOR LAYERS
    // will be inserted here
    // IE from uiPModNdxL = phfNav->pPBag[pbagIndex].wModNdx;
    //    to   uiPModNdxH = phfNav->pPBag[pbagIndex + 1].wModNdx;
    ///////////////////////////////////////////////////////////////

    ///////////////////////////////////////////////////
    // If the layer we found was the GLOBAL layer, then
    // its parameters must be INCLUDED in the DEFAULT
    // LAYER vector also.
    // Overriding key/vel NOT found at the GLOBAL
    // LAYER should be restored to the input values.
    // Overriding key/vel FOUND at the GLOBAL LAYER
    // should REPLACE the input values
    ///////////////////////////////////////////////////
    if (bGlobalPrstParams)
    {
      sfPresetData = sfCurrPreset;
    }

  } // Loop through all LAYERS of Generators and Modulators 

  // The navigation process is finished.
  return;
}

/******************************************************************************
*
* Function: SoundFontNavigator::ProcessSampleLinks
*
* Implemetation Notes:  Convert sample ID based links to vector index based
*                       links
*******************************************************************************
*/

void SoundFontNavigator::
ProcessSampleLinks(void)
{
  sfData *pCheckSF, *pCurrSF;
  BOOL found;
  WORD vectorIndexLinks[MAX_SAMPLES], saveStart = nextOscLinkCheck;

  // 
  // The idea here is that a LINKED sampled group is defined as
  // two or more samples which WERE FOUND within the SPLIT or LAYER lists in
  // a preset and whose sfSampleTypes field was set to leftSample, or 
  // rightSample, or linkSample, AND whose sfSampleLink fields circularly
  // point to each other. 
  //
  // A STEREO Pair is a Linked sample group of TWO samples.
  // Surround-sound could be implemented as a linked sample group of 
  // FIVE (or six) samples (if supporting hardware exists...)
  //
  // The ONLY audio feature that the STEREO/LINKED samples specifies is 
  // that their pitch amounts must be exactly the same at all times. 
  // (Equal sample rates, NO phase difference between the samples!)
  //
  // This means one oscillator controls the pitch amounts and pitch
  // modulation for all of the linked oscillators. Stereo/Linked 
  // samples do NOT need to have specific pan values (although typically 
  // a stereo pair would be panned right/left) or filter values or whatever. 
  // So you CAN have a "stereo" pair where one sample is being filtered
  // differently or has more trememlo (LFO->volume) than the other.
  //
  // In a stereo or linked sample list, the RIGHT sample controls the pitch,
  // the LEFT or LINKED samples follows the pitch of the RIGHT sample.
  // So in effect, any generators or modulators found in a split with
  // a LEFT or LINKED sample which modulate pitch in any way  (IE:
  // LFO->pitch, Env->pitch, etc) are NOT used if the stereo pair or the 
  // entire link is found in naviagtion.
  //
  // This is a linked sample group.
  // Two vectors (with one sample each) were found upon naviation. (wOsc == 2) 
  // Therefore it is a stereo pair.
  // Sample1 is Right sample, link points to Sample2
  // Sample2 is Left sample,  link points to Sample1
  //
  // This is NOT a linked sample group (nor is it a stereo pair)
  // Two vectors (with one sample each) were found upon naviation. (wOsc == 2)
  // Sample1 is Right sample, link points to Sample2
  // Sample3 is Mono sample,  link point is undefined
  // The samples do not point to each other
  //
  // This is NOT a linked sample group (nor is it a stereo pair)
  // Two vectors (with one sample each) were found upon naviation. (wOsc == 2)
  // There are two vectors (wOsc == 2), one with Sample2, other with Sample4
  // Sample2 is Left sample,  link points to Sample1
  // Sample4 is Right sample,  link points to Sample5
  // The samples are Left and Right samples but NOT to each other
  //
  // IE: This is a LINKED sample group
  // Four vectors (with one sample each) were found upon naviation. (wOsc == 4)
  // Sample10 is Right sample, link points to Sample11
  // Sample11 is Linked sample, link points to Sample12
  // Sample12 is Linked sample, link points to Sample13
  // Sample13 is Linked sample,  link points to Sample10
  //
  // IE: This is NOT a LINKED sample group
  // Three vectors (with one sample each) were found upon naviation. (wOsc == 3)
  // Sample10 is Right sample, link points to Sample11
  // Sample11 is Linked sample, link points to Sample12
  // Sample12 is Linked sample, link points to Sample13
  // The circle is not complete.
  //

  // What is happening here is that all vectors are being scanned to see
  // if stereo and/or linked samples exist AND point to each other

  // The array 'shdrIndexLinks' was filled by the Navigate() routine. It
  // contains a list of which SAMPLE INDEX each
  // vector's sample points to. A second array called 'vectorIndexLinks' is 
  // stuffed with the corresponding VECTOR indices, thus leaving all data
  // in tact until all processing is completed.

  for (WORD count = nextOscLinkCheck; count < wOsc; count++, nextOscLinkCheck++)
  {
    pCurrSF = &sfVector[count];
    found = FALSE;
    // Is this a linked sample?
    if (pCurrSF->shSampleModes & LINKED)
    {
      // If so, find its link
      for (WORD count1 = saveStart; count1 < wOsc; count1++)
      {
        pCheckSF = &sfVector[count1];
  
        if ((pCheckSF->shSampleModes & LINKED)              &&
            (shdrIndexLinks[count1] == pCurrSF->shSampleLink) &&
            (linkFound[count1] == 0))
        {
         // Temporarily store the vector link points 
         vectorIndexLinks[count] = count1;

         // Flag the fact that this vector has been found as a link!
         linkFound[count1] = 1;

         found=TRUE;
         break;
        }
      }

      // If there was no link, then turn off the LINK flags on the CURRENT
      // sample under consideration. Note that if a chain of linked samples
      // is found, this may not follow and reset the entire chain
      // ALL of the way back to the beginning of a list. Thus misleading
      // vectors may be produced. 
      //
      // Say x points to y and y points to z and z points to x. But in this
      // navigation only x and y were found. This process will return saying
      // x is a valid link to y, but y is an invalid link.
      //
      // There is no issue with an two unlinked samples which are linked 
      // elsewhere. 
      //
      // Say a points to b and c points to d. In this navigation a and d 
      // were found. Both a and d will come out saying they have no valid
      // links.

      if (found == FALSE)
      {
	pCurrSF->shSampleModes &= ~(FIRST_LINK|LINKED);
	pCurrSF->shSampleLink = 0;
      }
    }
  }

  // Now assign the each VECTOR's link value to the values which the
  // above stored in the 'vectorIndexLinks' array.
  for (count = saveStart; count < wOsc; count++)
  {
    pCurrSF = &sfVector[count];
    if ((pCurrSF->shSampleModes & LINKED) == LINKED)
        pCurrSF->shSampleLink = vectorIndexLinks[count];
  }

  // Now, all vectors have shSampleLink set to the values of the 
  // corresponding VECTOR indices instead of the sample indices.
  // Each vector's shSampleType field has the 3rd bit set indicating 
  // that it IS a link, and one vector's shSampleType field ALSO has the
  // 4th bit set indicating that is the FIRST link.

  // If the search above found that vectors with linked samples did NOT
  // have their counterparts in the final array of vectors, then all 
  // the shSampleType field get bits 3 and 4 reset. 
}

/******************************************************************************
*
* Function: SoundFontNavigator::GetSFNum
*
* Implemetation Notes:  Return value associated with a single bank/preset
*                       pair in the CURRENT SoundFont to outside world
*
*                       This sets up the index which will be used in the
*                       Navigate() routine. With this being a seperate call,
*                       all Navigation which is ONLY required upon a 
*                       Bank/Patch select call is done once only.
*
********************************************************************************
*/
WORD  SoundFontNavigator::GetSFNum(WORD wBank, BYTE byPatch, WORD* pwSFID)
{
  WORD wSize = phfNav->awStructSize[prstHdr]-1;

  if (phfNav == NULL)
    return(SF_INVALIDBANK);

  for (WORD wNdx = 0; wNdx < wSize; wNdx++)
  {
    if ((phfNav->pPHdr[wNdx].wPresetBank == wBank) &&
	(phfNav->pPHdr[wNdx].wPresetNum  == byPatch))
    {
      *pwSFID = wNdx;
      return (SUCCESS);
    }
  }
  return (SF_PRESETNOTFOUND);
}

/******************************************************************************
*
* Function: SoundFontNavigator::GetHydraFont
*
* Implemetation Notes:  Return a pointer to the CURRENTLY SET SoundFont instance
*                       (see SetHydraFont)
*
********************************************************************************
*/

void  SoundFontNavigator::GetHydraFont(HydraClass* pHydra)
{
  pHydra = phfNav;
  return;
}


/*******************************************************************
* SetHydraFont: Set the in memory instance of a SoundFont (pet-named
*               a HydraFont or a HydraClass) as the one to be used
*               in all future SoundFontNavigator calls. 
*
*               With this being a seperate call,
*               all setup which is ONLY required upon a 
*               Bank select or some other change in the SoundFont 
*               instance (such as unload bank, then load other bank)
*               can be done once only.
*
* The function returns a pointer to the PREVIOUSLY set SoundFont
* in case you need it...
*******************************************************************
*/
HydraClass*  SoundFontNavigator::SetHydraFont(HydraClass* pHydra)
{

  HydraClass * pOldHydra;
  pOldHydra = phfNav;
  phfNav = pHydra;

  return(pOldHydra);
}

/*******************************************************************
* AddSoundFonts: The layer/split addition operation function. Simply
*                take all elements in the final layer vector and add 
*                them to the final split vector.
*
*                The summed vectors is returned in sfSoundFontReturned
*********************************************************************
*/
void
SoundFontNavigator::AddSoundFonts(sfData * sfSoundFontReturned, 
                                  sfData * sfSFPresetAdd)
{
  WORD wCount, uiOffset;
  const SFLOOKUP *soundFontLookup;

  if ((soundFontLookup = GetLookup()) == NULL)
    return;

  for (wCount = 0; wCount < endOper; wCount++)
  {
    uiOffset = (WORD) soundFontLookup[wCount];

    *(SHORT*)((BYTE*)&(sfSoundFontReturned->dwStart) + uiOffset) +=
	*(SHORT*)((BYTE*)&(sfSFPresetAdd->dwStart) + uiOffset);
  }
}

#endif //  __SFNAV_CPP
