/*  
    fm4op.c:

    Copyright (C) 1998 Perry Cook, John ffitch

    This file is part of Csound.

    The Csound Library is free software; you can redistribute it
    and/or modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    Csound is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with Csound; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
    02111-1307 USA
*/

/*********************************************************/
/*  Master Class for 4 Operator FM Synth                 */
/*  by Perry R. Cook, 1995-96; recoded John ffitch 97/98 */
/*  This instrument contains 4 waves, 4 adsr, and        */
/*  various state vars.                                  */
/*                                                       */
/*  The basic Chowning/Stanford FM patent expired April  */
/*  1995, but there exist follow-on patents, mostly      */
/*  assigned to Yamaha.  If you are of the type who      */
/*  should worry about this (making money) worry away.   */
/*                                                       */
/*********************************************************/

#include "fm4op.h"

/***********************************************************/
/*  Two Zero Filter Class,                                 */
/*  by Perry R. Cook, 1995-96; recoded John ffitch 97/98   */
/*  See books on filters to understand more about how this */
/*  works.  Nothing out of the ordinary in this version.   */
/***********************************************************/

void make_TwoZero(TwoZero *p)
{
    p->zeroCoeffs[0] = FL(0.0);
    p->zeroCoeffs[1] = FL(0.0);
    p->gain = FL(1.0);
    p->inputs[0] = FL(0.0);
    p->inputs[1] = FL(0.0);
    p->lastOutput = FL(0.0);
}

void TwoZero_setZeroCoeffs(TwoZero* p, MYFLT *coeffs)
{
    p->zeroCoeffs[0] = coeffs[0];
    p->zeroCoeffs[1] = coeffs[1];
}

MYFLT TwoZero_tick(TwoZero *p, MYFLT sample)
  /*   Perform Filter Operation            */
{ /*  TwoZero is a two zero filter (duh!)  */
  /*  Look it up in your favorite DSP text */
    p->lastOutput = p->zeroCoeffs[0] * p->inputs[0] +
                    p->zeroCoeffs[1] * p->inputs[1];
    p->inputs[1] = p->inputs[0];
    p->inputs[0] = p->gain * sample;
    p->lastOutput += p->inputs[0];
    return p->lastOutput;
}



MYFLT Wave_tick(MYFLT *vTime, int len, MYFLT *data, MYFLT rate, MYFLT phase)
{                                /* Tick on vibrato table */
    long    temp;
    MYFLT   temp_time, alpha;
    MYFLT   lastOutput;

    *vTime += rate;            /*  Update current time    */
    while (*vTime >= len)      /*  Check for end of sound */
      *vTime -= len;           /*  loop back to beginning */
    while (*vTime < FL(0.0))       /*  Check for end of sound */
      *vTime += len;           /*  loop back to beginning */

    temp_time = *vTime;

    if (phase != FL(0.0)) {
      temp_time += phase;      /*  Add phase offset       */
      while (temp_time >= len) /*  Check for end of sound */
        temp_time -= len;      /*  loop back to beginning */
      while (temp_time < FL(0.0))  /*  Check for end of sound */
        temp_time += len;      /*  loop back to beginning */
    }
    temp = (long) temp_time;   /*  Integer part of time address    */
                               /*  fractional part of time address */
    alpha = temp_time - (MYFLT)temp;
    lastOutput = data[temp];   /* Do linear interpolation */
                             /* same as alpha*data[temp+1] + (1-alpha)data[temp] */
    lastOutput += (alpha * (data[temp+1] - lastOutput));
                               /* End of vibrato tick */
    return lastOutput;
}

/* ---------------------------------------------------------------------- */

static int      FM_tabs_built = 0;
static MYFLT    FM4Op_gains[100];
static MYFLT    FM4Op_susLevels[16];
static MYFLT    FM4Op_attTimes[32];

void build_FM(void)
{                                /* The following tables are pre-built */
    MYFLT       temp = FL(1.0);
    int         i;

    for (i=99; i>=0; i--) {
      FM4Op_gains[i] = temp;
      temp *= FL(0.933033);
    }
    temp = FL(1.0);
    for (i=15; i>=0; i--) {
      FM4Op_susLevels[i] = temp;
      temp *= FL(0.707101);
    }
    temp = FL(8.498186);
    for (i=0; i<32; i++) {
      FM4Op_attTimes[i] = temp;
      temp *= FL(0.707101);
    }
    FM_tabs_built = 1;
}

void make_FM4Op(FM4OP *p)
{
    MYFLT       tempCoeffs[2] = {FL(0.0), -FL(1.0)};
    FUNC        *ftp;

    if (!FM_tabs_built) build_FM(); /* Ensure tables exist */

    make_ADSR(&p->adsr[0]);
    make_ADSR(&p->adsr[1]);
    make_ADSR(&p->adsr[2]);
    make_ADSR(&p->adsr[3]);
    make_TwoZero(&p->twozero);
    if ((ftp = ftfind(p->vifn)) != NULL)
      p->vibWave = ftp;
    else {
      perferror(Str(X_384,"No table for VibWaveato")); /* Expect sine wave */
      return;
    }
    p->baseFreq = FL(440.0);
    p->ratios[0] = FL(1.0);
    p->ratios[1] = FL(1.0);
    p->ratios[2] = FL(1.0);
    p->ratios[3] = FL(1.0);
    p->gains[0] = FL(1.0);
    p->gains[1] = FL(1.0);
    p->gains[2] = FL(1.0);
    p->gains[3] = FL(1.0);
    TwoZero_setZeroCoeffs(&p->twozero, tempCoeffs);
    p->twozero.gain = FL(0.0);
}

static int FM4Op_loadWaves(FM4OP *p)
{
    FUNC        *ftp;

    if ((ftp = ftfind(p->ifn0)) != NULL)
      p->waves[0] = ftp;
    else {
      perferror(Str(X_377,"No table for FM4Op")); /* Expect sine wave */
      return 1;
    }
    if ((ftp = ftfind(p->ifn1)) != NULL)
      p->waves[1] = ftp;
    else {
      perferror(Str(X_373,"No table for  FM4Op")); /* Expect sine wave */
      return 1;
    }
    if ((ftp = ftfind(p->ifn2)) != NULL)
      p->waves[2] = ftp;
    else {
      perferror(Str(X_373,"No table for  FM4Op")); /* Expect sine wave */
      return 1;
    }
    if ((ftp = ftfind(p->ifn3)) != NULL)
      p->waves[3] = ftp;
    else {
      perferror(Str(X_373,"No table for  FM4Op")); /* Expect sine wave */
      return 1;
    }
    p->w_time[0] = p->w_time[1] = p->w_time[2] = p->w_time[3] = FL(0.0);
    return 0;
}

void FM4Op_setRatio(FM4OP *p, int whichOne, MYFLT ratio)
{
    p->ratios[whichOne] = ratio;
    if (ratio>FL(0.0))
        p->w_rate[whichOne] = p->baseFreq * ratio;
    else
        p->w_rate[whichOne] = ratio;
}

void FM4Op_keyOff(FM4OP *p)
{
    ADSR_keyOff(&p->adsr[0]);
    ADSR_keyOff(&p->adsr[1]);
    ADSR_keyOff(&p->adsr[2]);
    ADSR_keyOff(&p->adsr[3]);
}

#ifdef never
void FM4Op_controlChange(FM4OP *p, int number, MYFLT value)
{
#if defined(_debug_)
    printf("FM4Op : ControlChange: Number=%i Value=%f\n",number,value);
#endif
    else if (number == MIDI_after_touch)        {
      adsr[0]->setTarget(value * NORM_7);
      adsr[1]->setTarget(value * NORM_7);
      adsr[2]->setTarget(value * NORM_7);
      adsr[3]->setTarget(value * NORM_7);
    }
}
#endif

/*********************************************************/
/*  Algorithm 5 (TX81Z) Subclass of 4 Operator FM Synth  */
/*  by Perry R. Cook, 1995-96; recoded John ffitch 97/98 */
/*  This connection topology is 2 simple FM Pairs summed */
/*  together, like:                                      */
/*                                                       */
/*   Alg 5 is :      4->3--\                             */
/*                          + --> Out                    */
/*                   2->1--/                             */
/*                                                       */
/*  Controls: control1 = mod index 1                     */
/*            control2 = crossfade of two outputs        */
/*                                                       */
/*********************************************************/


MYFLT FM4Alg5_tick(FM4OP *p, MYFLT c1, MYFLT c2)
{
    MYFLT       temp,temp2;
    MYFLT       lastOutput;

    temp = p->gains[1] * ADSR_tick(&p->adsr[1]) *
         Wave_tick(&p->w_time[1], (int)p->waves[1]->flen, p->waves[1]->ftable,
                   p->w_rate[1], p->w_phase[1]);
    temp = temp * c1;
    p->w_phase[0] = p->waves[0]->flen * temp; /* addPhaseOffset */
    p->w_phase[3] = p->waves[0]->flen * p->twozero.lastOutput;
    temp =  p->gains[3] * ADSR_tick(&p->adsr[3]) *
         Wave_tick(&p->w_time[3], (int)p->waves[3]->flen, p->waves[3]->ftable,
                   p->w_rate[3], p->w_phase[3]);
    TwoZero_tick(&p->twozero, temp);
    p->w_phase[2] = p->waves[2]->flen * temp; /* addPhaseOffset */
    temp = (FL(1.0) - ( c2 * FL(0.5))) *  p->gains[0] *
      ADSR_tick(&p->adsr[0]) *
      Wave_tick(&p->w_time[0], (int)p->waves[0]->flen, p->waves[0]->ftable,
                             p->w_rate[0], p->w_phase[0]);
    temp +=  c2 * FL(0.5) *  p->gains[2] * ADSR_tick(&p->adsr[2]) *
      Wave_tick(&p->w_time[2], (int)p->waves[2]->flen, p->waves[2]->ftable,
                p->w_rate[2], p->w_phase[2]);

    temp2 = Wave_tick(&p->v_time, (int)p->vibWave->flen,
                      p->vibWave->ftable, p->v_rate, FL(0.0)) *
            *p->modDepth; /* Calculate amplitude mod */
    temp = temp * (FL(1.0) + temp2); /*  and apply it to output */

    lastOutput = temp * FL(0.5);
    return  lastOutput;
}

/***************************************************************/
/*  Tubular Bell (Orch. Chime) Subclass of Algorithm 5 (TX81Z) */
/*  Subclass of 4 Operator FM Synth by Perry R. Cook, 1995-96  */
/*  Recoded in C by John ffitch 1997-98                        */
/***************************************************************/

void tubebellset(FM4OP *p)
{
    MYFLT       amp = *p->amp * AMP_RSCALE; /* Normalised */

    make_FM4Op(p);
    if (FM4Op_loadWaves(p)) return; /* 4 times  "rawwaves/sinewave.raw" */

    FM4Op_setRatio(p, 0, FL(1.0)   * FL(0.995));
    FM4Op_setRatio(p, 1, FL(1.414) * FL(0.995));
    FM4Op_setRatio(p, 2, FL(1.0)   * FL(1.005));
    FM4Op_setRatio(p, 3, FL(1.414)            );
    p->gains[0] = amp * FM4Op_gains[94];
    p->gains[1] = amp * FM4Op_gains[76];
    p->gains[2] = amp * FM4Op_gains[99];
    p->gains[3] = amp * FM4Op_gains[71];
    ADSR_setAllTimes(&p->adsr[0], FL(0.005), FL(4.0), FL(0.0), FL(0.04));
    ADSR_setAllTimes(&p->adsr[1], FL(0.005), FL(4.0), FL(0.0), FL(0.04));
    ADSR_setAllTimes(&p->adsr[2], FL(0.001), FL(2.0), FL(0.0), FL(0.04));
    ADSR_setAllTimes(&p->adsr[3], FL(0.004), FL(4.0), FL(0.0), FL(0.04));
/*      ADSR_setAll(&p->adsr[0], 0.03f,0.00001f,FL(0.0),0.02f); */
/*      ADSR_setAll(&p->adsr[1], 0.03f,0.00001f,FL(0.0),0.02f); */
/*      ADSR_setAll(&p->adsr[2], 0.07f,0.00002f,FL(0.0),0.02f); */
/*      ADSR_setAll(&p->adsr[3], FL(0.04),0.00001f,FL(0.0),0.02f); */
    p->twozero.gain = FL(0.5);
    p->v_rate = FL(2.0) * p->vibWave->flen / esr; /* Vib rate */
                                /* Set Freq */
    p->baseFreq = *p->frequency;
    p->w_rate[0] = p->baseFreq *  p->ratios[0] * p->waves[0]->flen / esr;
    p->w_rate[1] = p->baseFreq *  p->ratios[1] * p->waves[1]->flen / esr;
    p->w_rate[2] = p->baseFreq *  p->ratios[2] * p->waves[2]->flen / esr;
    p->w_rate[3] = p->baseFreq *  p->ratios[3] * p->waves[3]->flen / esr;
    ADSR_keyOn(&p->adsr[0]);
    ADSR_keyOn(&p->adsr[1]);
    ADSR_keyOn(&p->adsr[2]);
    ADSR_keyOn(&p->adsr[3]);
}

void tubebell(FM4OP *p)
{
    MYFLT       amp = *p->amp * AMP_RSCALE; /* Normalised */
    MYFLT       *ar = p->ar;
    long        nsmps = ksmps;
    MYFLT       c1 = *p->control1;
    MYFLT       c2 = *p->control2;

                                /* Set Freq */
    p->baseFreq = *p->frequency;
    p->gains[0] = amp * FM4Op_gains[94];
    p->gains[1] = amp * FM4Op_gains[76];
    p->gains[2] = amp * FM4Op_gains[99];
    p->gains[3] = amp * FM4Op_gains[71];
    p->w_rate[0] = p->baseFreq *  p->ratios[0] * p->waves[0]->flen / esr;
    p->w_rate[1] = p->baseFreq *  p->ratios[1] * p->waves[1]->flen / esr;
    p->w_rate[2] = p->baseFreq *  p->ratios[2] * p->waves[2]->flen / esr;
    p->w_rate[3] = p->baseFreq *  p->ratios[3] * p->waves[3]->flen / esr;
    p->v_rate = *p->vibFreq * p->vibWave->flen / esr;

    do {
        MYFLT   lastOutput;
        lastOutput = FM4Alg5_tick(p, c1, c2);
        *ar++ = lastOutput*AMP_SCALE*FL(1.8);
    } while (--nsmps);
}

/*****************************************************************/
/*  Fender Rhodes Electric Piano Subclass of Algorithm 5 (TX81Z) */
/*  Subclass of 4 Operator FM Synth by Perry R. Cook, 1995-96    */
/*  Recoded in C by John ffitch 1997-98                          */
/*****************************************************************/

void rhodeset(FM4OP *p)
{
    MYFLT       amp = *p->amp * AMP_RSCALE; /* Normalised */

    make_FM4Op(p);
    if (FM4Op_loadWaves(p)) return; /* 3 times  "sinewave.raw"; 1 x fwavblnk.raw */

    FM4Op_setRatio(p, 0, FL(1.0));
    FM4Op_setRatio(p, 1, FL(0.5));
    FM4Op_setRatio(p, 2, FL(1.0));
    FM4Op_setRatio(p, 3, FL(15.0));
    p->gains[0] = amp * FM4Op_gains[99];
    p->gains[1] = amp * FM4Op_gains[90];
    p->gains[2] = amp * FM4Op_gains[99];
    p->gains[3] = amp * FM4Op_gains[67];
    ADSR_setAllTimes(&p->adsr[0], FL(0.001), FL(1.50), FL(0.0), FL(0.04));
    ADSR_setAllTimes(&p->adsr[1], FL(0.001), FL(1.50), FL(0.0), FL(0.04));
    ADSR_setAllTimes(&p->adsr[2], FL(0.001), FL(1.00), FL(0.0), FL(0.04));
    ADSR_setAllTimes(&p->adsr[3], FL(0.001), FL(0.25), FL(0.0), FL(0.04));
/*      ADSR_setAll(&p->adsr[0], 0.05f,0.00003f,FL(0.0),0.02f); */
/*      ADSR_setAll(&p->adsr[1], 0.05f,0.00003f,FL(0.0),0.02f); */
/*      ADSR_setAll(&p->adsr[2], 0.05f,0.00005f,FL(0.0),0.02f); */
/*      ADSR_setAll(&p->adsr[3], 0.05f,0.0002f,FL(0.0),0.02f); */
    p->twozero.gain = FL(1.0);
    p->v_rate = FL(2.0) * p->vibWave->flen / esr; /* Vib rate */
                                /* Set Freq */
    p->baseFreq = *p->frequency;
    p->w_rate[0] = p->baseFreq *  p->ratios[0] * p->waves[0]->flen / esr;
    p->w_rate[1] = p->baseFreq *  p->ratios[1] * p->waves[1]->flen / esr;
    p->w_rate[2] = p->baseFreq *  p->ratios[2] * p->waves[2]->flen / esr;
    p->w_rate[3] = p->baseFreq *  p->ratios[3] * p->waves[3]->flen / esr;
    ADSR_keyOn(&p->adsr[0]);
    ADSR_keyOn(&p->adsr[1]);
    ADSR_keyOn(&p->adsr[2]);
    ADSR_keyOn(&p->adsr[3]);
}

/***************************************************************/
/*  Wurlitzer Electric Piano Subclass of Algorithm 5 (TX81Z)   */
/*  Subclass of 4 Operator FM Synth by Perry R. Cook, 1995-96  */
/*  Recoded in C by John ffitch 1997-98                        */
/***************************************************************/


void wurleyset(FM4OP *p)
{
    MYFLT       amp = *p->amp * AMP_RSCALE; /* Normalised */

    make_FM4Op(p);
    if (FM4Op_loadWaves(p)) return; /* 3 times  "sinewave.raw"; 1 x fwavblnk.raw */

    FM4Op_setRatio(p, 0, FL(1.0));
    FM4Op_setRatio(p, 1, FL(4.05));
    FM4Op_setRatio(p, 2, -FL(510.0));
    FM4Op_setRatio(p, 3, -FL(510.0));
    p->gains[0] = amp * FM4Op_gains[99];
    p->gains[1] = amp * FM4Op_gains[82];
    p->gains[2] = amp * FM4Op_gains[82];
    p->gains[3] = amp * FM4Op_gains[68];
    ADSR_setAllTimes(&p->adsr[0], FL(0.001), FL(1.50), FL(0.0), FL(0.04));
    ADSR_setAllTimes(&p->adsr[1], FL(0.001), FL(1.50), FL(0.0), FL(0.04));
    ADSR_setAllTimes(&p->adsr[2], FL(0.001), FL(0.25), FL(0.0), FL(0.04));
    ADSR_setAllTimes(&p->adsr[3], FL(0.001), FL(0.15), FL(0.0), FL(0.04));
/*      ADSR_setAll(&p->adsr[0], 0.05f,0.00003f,FL(0.0),0.02f); */
/*      ADSR_setAll(&p->adsr[1], 0.05f,0.00003f,FL(0.0),0.02f); */
/*      ADSR_setAll(&p->adsr[2], 0.05f,0.0002f,FL(0.0),0.02f); */
/*      ADSR_setAll(&p->adsr[3], 0.05f,0.0003f,FL(0.0),0.02f); */
    p->twozero.gain = FL(2.0);
                                /* Set Freq */
    p->baseFreq = *p->frequency;
    p->w_rate[0] = p->baseFreq *  p->ratios[0] * p->waves[0]->flen / esr;
    p->w_rate[1] = p->baseFreq *  p->ratios[1] * p->waves[1]->flen / esr;
    p->w_rate[2] =                p->ratios[2] * p->waves[2]->flen / esr;
    p->w_rate[3] =                p->ratios[3] * p->waves[3]->flen / esr;
    ADSR_keyOn(&p->adsr[0]);
    ADSR_keyOn(&p->adsr[1]);
    ADSR_keyOn(&p->adsr[2]);
    ADSR_keyOn(&p->adsr[3]);
}

void wurley(FM4OP *p)
{
    MYFLT       amp = *p->amp * AMP_RSCALE; /* Normalised */
    MYFLT       *ar = p->ar;
    long        nsmps = ksmps;
    MYFLT       c1 = *p->control1;
    MYFLT       c2 = *p->control2;

                                /* Set Freq */
    p->baseFreq = *p->frequency;
    p->gains[0] = amp * FM4Op_gains[99];
    p->gains[1] = amp * FM4Op_gains[82];
    p->gains[2] = amp * FM4Op_gains[82];
    p->gains[3] = amp * FM4Op_gains[68];
    p->w_rate[0] = p->baseFreq *  p->ratios[0] * p->waves[0]->flen / esr;
    p->w_rate[1] = p->baseFreq *  p->ratios[1] * p->waves[1]->flen / esr;
    p->w_rate[2] =                p->ratios[2] * p->waves[2]->flen / esr;
    p->w_rate[3] =                p->ratios[3] * p->waves[3]->flen / esr;
    p->v_rate = *p->vibFreq * p->vibWave->flen / esr;

    do {
      MYFLT   lastOutput;
      lastOutput = FM4Alg5_tick(p, c1, c2);
      *ar++ = lastOutput*AMP_SCALE*FL(1.9);
    } while (--nsmps);
}

/*********************************************************/
/*  Algorithm 3 (TX81Z) Subclass of 4 Operator FM Synth  */
/*  by Perry R. Cook, 1995-96; recoded John ffitch 97/98 */
/*                                                       */
/*  Alg 3 is :          4--\                             */
/*                  3-->2-- + -->1-->Out                 */
/*                                                       */
/*  Controls: control1 = total mod index                 */
/*            control2 = crossfade of two modulators     */
/*                                                       */
/*********************************************************/

MYFLT FM4Alg3_tick(FM4OP *p, MYFLT c1, MYFLT c2)
{
    MYFLT       temp;
    MYFLT       lastOutput;

    temp = *p->modDepth * FL(0.2) * Wave_tick(&p->v_time, (int)p->vibWave->flen,
                                          p->vibWave->ftable, p->v_rate, FL(0.0));
    p->w_rate[0] = p->baseFreq * (FL(1.0) + temp) * p->ratios[0];
    p->w_rate[1] = p->baseFreq * (FL(1.0) + temp) * p->ratios[1];
    p->w_rate[2] = p->baseFreq * (FL(1.0) + temp) * p->ratios[2];
    p->w_rate[3] = p->baseFreq * (FL(1.0) + temp) * p->ratios[3];

    temp = p->gains[2] * ADSR_tick(&p->adsr[2]) *
      Wave_tick(&p->w_time[2], (int)p->waves[2]->flen, p->waves[2]->ftable,
                p->w_rate[2], p->w_phase[2]);
    p->w_phase[1] = p->waves[1]->flen * temp;
    p->w_phase[3] = p->waves[3]->flen * p->twozero.lastOutput;
    temp = (FL(1.0) - (c2 * FL(0.5))) * p->gains[3] * ADSR_tick(&p->adsr[3]) *
      Wave_tick(&p->w_time[3], (int)p->waves[3]->flen, p->waves[3]->ftable,
                p->w_rate[3], p->w_phase[3]);
    TwoZero_tick(&p->twozero, temp);

    temp += c2 * FL(0.5) * p->gains[1] * ADSR_tick(&p->adsr[1]) *
      Wave_tick(&p->w_time[1], (int)p->waves[1]->flen, p->waves[1]->ftable,
                p->w_rate[1], p->w_phase[1]);
    temp = temp * c1;

     p->w_phase[0] = p->waves[0]->flen * temp;
    temp = p->gains[0] * ADSR_tick(&p->adsr[0]) *
      Wave_tick(&p->w_time[0], (int)p->waves[0]->flen, p->waves[0]->ftable,
                p->w_rate[0], p->w_phase[0]);

    lastOutput = temp * FL(0.5);
    return lastOutput;
}

void heavymetset(FM4OP *p)
{
    make_FM4Op(p);
    if (FM4Op_loadWaves(p)) return;         /* Mixed -- 2 x sine; 1 x fwavblnk */
    FM4Op_setRatio(p, 0, FL(1.00)         );
    FM4Op_setRatio(p, 1, FL(4.00) * FL(0.999));
    FM4Op_setRatio(p, 2, FL(3.00) * FL(1.001));
    FM4Op_setRatio(p, 3, FL(0.50) * FL(1.002));
    ADSR_setAllTimes(&p->adsr[0], FL(0.001), FL(0.001), FL(1.0), FL(0.01));
    ADSR_setAllTimes(&p->adsr[1], FL(0.001), FL(0.010), FL(1.0), FL(0.50));
    ADSR_setAllTimes(&p->adsr[2], FL(0.010), FL(0.005), FL(1.0), FL(0.20));
    ADSR_setAllTimes(&p->adsr[3], FL(0.030), FL(0.010), FL(0.2), FL(0.20));
/*      ADSR_setAll(&p->adsr[0], 0.050f, 0.0100f, FL(1.0), FL(0.001));  */
/*      ADSR_setAll(&p->adsr[1], 0.050f, 0.0010f, FL(1.0), 0.0001f); */
/*      ADSR_setAll(&p->adsr[2], FL(0.001), 0.0020f, FL(1.0), 0.0002f); */
/*      ADSR_setAll(&p->adsr[3], 0.050f, 0.0010f, FL(0.2), 0.0002f); */
    p->twozero.gain = FL(2.0);
/*     p->v_rate = 5.5 * p->vibWave->flen / esr;  Vib rate */
    ADSR_keyOn(&p->adsr[0]);
    ADSR_keyOn(&p->adsr[1]);
    ADSR_keyOn(&p->adsr[2]);
    ADSR_keyOn(&p->adsr[3]);
}

void heavymet(FM4OP *p)
{
    MYFLT       *ar = p->ar;
    long        nsmps = ksmps;
    MYFLT       amp = *p->amp * AMP_RSCALE; /* Normalised */
    MYFLT       c1 = *p->control1;
    MYFLT       c2 = *p->control2;

    p->baseFreq = *p->frequency;
    p->gains[0] = amp * FM4Op_gains[92];
    p->gains[1] = amp * FM4Op_gains[76];
    p->gains[2] = amp * FM4Op_gains[91];
    p->gains[3] = amp * FM4Op_gains[68];

    p->w_rate[0] = p->baseFreq *  p->ratios[0] * p->waves[0]->flen / esr;
    p->w_rate[1] = p->baseFreq *  p->ratios[1] * p->waves[1]->flen / esr;
    p->w_rate[2] = p->baseFreq *  p->ratios[2] * p->waves[2]->flen / esr;
    p->w_rate[3] = p->baseFreq *  p->ratios[3] * p->waves[3]->flen / esr;
    p->v_rate = *p->vibFreq * p->vibWave->flen / esr;
    do {
      MYFLT   lastOutput;
      lastOutput = FM4Alg3_tick(p, c1, c2);
      *ar++ = lastOutput*AMP_SCALE*FL(2.0);
    } while (--nsmps);
}

/**********************************************************/
/*  Algorithm 8 (TX81Z) Subclass of 4 Operator FM Synth   */
/*  by Perry R. Cook, 1995-96; recoded John ffitch 97-98  */
/*  This connection topology is simple Additive Synthesis */
/*                                                        */
/*                   1 --.                                */
/*                   2 -\|                                */
/*                       +-> Out                          */
/*                   3 -/|                                */
/*                   4 --                                 */
/*                                                        */
/*  Controls: control1 = op4 (fb) gain                    */
/*            control2 = op3 gain                         */
/*                                                        */
/**********************************************************/

MYFLT FM4Alg8_tick(FM4OP *p, MYFLT c1, MYFLT c2)
{
    MYFLT       temp;
    MYFLT       lastOutput;

    p->w_phase[3] = p->waves[3]->flen * p->twozero.lastOutput;

    temp = c1 * FL(2.0) * p->gains[3] * ADSR_tick(&p->adsr[3]) *
       Wave_tick(&p->w_time[3], (int)p->waves[3]->flen, p->waves[3]->ftable,
                 p->w_rate[3], p->w_phase[3]);
    TwoZero_tick(&p->twozero, temp);
    temp += c2 * FL(2.0) * p->gains[2] * ADSR_tick(&p->adsr[2]) *
      Wave_tick(&p->w_time[2], (int)p->waves[2]->flen, p->waves[2]->ftable,
                p->w_rate[2], p->w_phase[2]);
    temp += p->gains[1] * ADSR_tick(&p->adsr[1]) *
      Wave_tick(&p->w_time[1], (int)p->waves[1]->flen, p->waves[1]->ftable,
                   p->w_rate[1], p->w_phase[1]);
    temp += p->gains[0] * ADSR_tick(&p->adsr[0]) *
      Wave_tick(&p->w_time[0], (int)p->waves[0]->flen, p->waves[0]->ftable,
                             p->w_rate[0], p->w_phase[0]);

    lastOutput = temp * FL(0.125);
    return lastOutput;
}


/**************************************************************/
/*  Hammond(OID) Organ Subclass of Algorithm 8 (TX81Z)        */
/*  Subclass of 4 Operator FM Synth by Perry R. Cook, 1995-96 */
/*  Recoded in C by John ffitch 1997-98                       */
/**************************************************************/


void b3set(FM4OP *p)
{
    MYFLT       amp = *p->amp * AMP_RSCALE; /* Normalised */

    make_FM4Op(p);
    if (FM4Op_loadWaves(p)) return;         /* sines */
    FM4Op_setRatio(p, 0, FL(0.999));
    FM4Op_setRatio(p, 1, FL(1.997));
    FM4Op_setRatio(p, 2, FL(3.006));
    FM4Op_setRatio(p, 3, FL(6.009));

    p->gains[0] = amp * FM4Op_gains[95];
    p->gains[1] = amp * FM4Op_gains[95];
    p->gains[2] = amp * FM4Op_gains[99];
    p->gains[3] = amp * FM4Op_gains[95];
    ADSR_setAllTimes(&p->adsr[0], FL(0.005), FL(0.003), FL(1.0), FL(0.01));
    ADSR_setAllTimes(&p->adsr[1], FL(0.005), FL(0.003), FL(1.0), FL(0.01));
    ADSR_setAllTimes(&p->adsr[2], FL(0.005), FL(0.003), FL(1.0), FL(0.01));
    ADSR_setAllTimes(&p->adsr[3], FL(0.005), FL(0.001), FL(0.4), FL(0.03));
/*      ADSR_setAll(&p->adsr[0], 0.05f, 0.03f, FL(1.0), FL(0.04)); */
/*      ADSR_setAll(&p->adsr[1], 0.05f, 0.03f, FL(1.0), FL(0.04)); */
/*      ADSR_setAll(&p->adsr[2], 0.05f, 0.03f, FL(1.0), FL(0.04)); */
/*      ADSR_setAll(&p->adsr[3], 0.05f, FL(0.001),0.4f, 0.06f); */
    p->twozero.gain = FL(0.1);
    ADSR_keyOn(&p->adsr[0]);
    ADSR_keyOn(&p->adsr[1]);
    ADSR_keyOn(&p->adsr[2]);
    ADSR_keyOn(&p->adsr[3]);
}

void hammondB3(FM4OP *p)
{
    MYFLT       amp = *p->amp * AMP_RSCALE; /* Normalised */
    MYFLT       *ar = p->ar;
    long        nsmps = ksmps;
    MYFLT       c1 = *p->control1;
    MYFLT       c2 = *p->control2;
    MYFLT       temp;

    p->baseFreq = *p->frequency;
    p->gains[0] = amp * FM4Op_gains[95];
    p->gains[1] = amp * FM4Op_gains[95];
    p->gains[2] = amp * FM4Op_gains[99];
    p->gains[3] = amp * FM4Op_gains[95];
    do {
      MYFLT   lastOutput;
      if (*p->modDepth > FL(0.0)) {
        p->v_rate = *p->vibFreq * p->vibWave->flen / esr;
        temp = FL(1.0) + (*p->modDepth * FL(0.1) *
                          Wave_tick(&p->v_time, (int)p->vibWave->flen,
                                    p->vibWave->ftable, p->v_rate, FL(0.0)));
        temp *= p->baseFreq;
        p->w_rate[0] = p->ratios[0] * temp * p->waves[0]->flen / esr;
        p->w_rate[1] = p->ratios[1] * temp * p->waves[1]->flen / esr;
        p->w_rate[2] = p->ratios[2] * temp * p->waves[2]->flen / esr;
        p->w_rate[3] = p->ratios[3] * temp * p->waves[3]->flen / esr;
      }
      lastOutput = FM4Alg8_tick(p, c1, c2);
      *ar++ = lastOutput*AMP_SCALE;
    } while (--nsmps);
}

/************************************************************/
/*  Algorithm 6 (TX81Z) Subclass of 4 Operator FM Synth     */
/*  by Perry R. Cook, 1995-96; recoded John ffitch 97-98    */
/*  This connection topology is three Carriers and a common */
/*  Modulator     /->1 -\                                   */
/*             4-|-->2 - +-> Out                            */
/*                \->3 -/                                   */
/*                                                          */
/*  Controls: control1 = vowel                              */
/*            control2 = spectral tilt                      */
/*                                                          */
/************************************************************/

MYFLT FM4Alg6_tick(FM4OPV *q)
{
    MYFLT       temp,temp2;
    FM4OP       *p = (FM4OP*)q;

    temp = p->gains[3] * ADSR_tick(&p->adsr[3]) *
      Wave_tick(&p->w_time[3], (int)p->waves[3]->flen, p->waves[3]->ftable,
                 p->w_rate[3], p->w_phase[3]);
                                /*  Calculate frequency mod  */
    temp2 = Wave_tick(&p->v_time, (int)p->vibWave->flen, p->vibWave->ftable,
                 p->v_rate, FL(0.0)) * *p->modDepth * FL(0.1);

    temp2 = (FL(1.0) + temp2) * p->baseFreq / esr;
    p->w_rate[0] = temp2 * p->ratios[0] * p->waves[0]->flen;
    p->w_rate[1] = temp2 * p->ratios[1] * p->waves[1]->flen;
    p->w_rate[2] = temp2 * p->ratios[2] * p->waves[2]->flen;
    p->w_rate[3] = temp2 * p->ratios[3] * p->waves[3]->flen;

    p->w_phase[0] = p->waves[0]->flen * temp * q->mods[0];
    p->w_phase[1] = p->waves[1]->flen * temp * q->mods[1];
    p->w_phase[2] = p->waves[2]->flen * temp * q->mods[2];
    p->w_phase[3] = p->waves[3]->flen * p->twozero.lastOutput;

    TwoZero_tick(&p->twozero, temp);

    temp =  p->gains[0] * q->tilt[0] * ADSR_tick(&p->adsr[0]) *
      Wave_tick(&p->w_time[0], (int)p->waves[0]->flen, p->waves[0]->ftable,
                p->w_rate[0], p->w_phase[0]);
    temp += p->gains[1] * q->tilt[1] * ADSR_tick(&p->adsr[1]) *
      Wave_tick(&p->w_time[1], (int)p->waves[1]->flen, p->waves[1]->ftable,
                p->w_rate[1], p->w_phase[1]);
    temp += p->gains[2] * q->tilt[2] * ADSR_tick(&p->adsr[2]) *
      Wave_tick(&p->w_time[2], (int)p->waves[2]->flen, p->waves[2]->ftable,
                p->w_rate[2], p->w_phase[2]);

    return temp * FL(0.33);
}

MYFLT phonGains[32][2] =
        {{FL(1.0), FL(0.0)},    /* eee */
         {FL(1.0), FL(0.0)},    /* ihh */
         {FL(1.0), FL(0.0)},    /* ehh */
         {FL(1.0), FL(0.0)},    /* aaa */

         {FL(1.0), FL(0.0)},    /* ahh */
         {FL(1.0), FL(0.0)},    /* aww */
         {FL(1.0), FL(0.0)},    /* ohh */
         {FL(1.0), FL(0.0)},    /* uhh */

         {FL(1.0), FL(0.0)},    /* uuu */
         {FL(1.0), FL(0.0)},    /* ooo */
         {FL(1.0), FL(0.0)},    /* rrr */
         {FL(1.0), FL(0.0)},    /* lll */

         {FL(1.0), FL(0.0)},    /* mmm */
         {FL(1.0), FL(0.0)},    /* nnn */
         {FL(1.0), FL(0.0)},    /* nng */
         {FL(1.0), FL(0.0)},    /* ngg */

         {FL(0.0), FL(1.0)},    /* fff */
         {FL(0.0), FL(1.0)},    /* sss */
         {FL(0.0), FL(1.0)},    /* thh */
         {FL(0.0), FL(1.0)},    /* shh */

         {FL(0.0), FL(1.0)},    /* xxx */
         {FL(0.0), FL(0.1)},    /* hee */
         {FL(0.0), FL(0.1)},    /* hoo */
         {FL(0.0), FL(0.1)},    /* hah */

         {FL(1.0), FL(0.1)},    /* bbb */
         {FL(1.0), FL(0.1)},    /* ddd */
         {FL(1.0), FL(0.1)},    /* jjj */
         {FL(1.0), FL(0.1)},    /* ggg */

         {FL(1.0), FL(1.0)},    /* vvv */
         {FL(1.0), FL(1.0)},    /* zzz */
         {FL(1.0), FL(1.0)},    /* thz */
         {FL(1.0), FL(1.0)}     /* zhh */
};

MYFLT phonParams[32][4][3] =
    {{  { FL(273.0), FL(0.996),   FL(0.0)},   /* eee (beet) */
        {FL(2086.0), FL(0.945), -FL(16.0)},
        {FL(2754.0), FL(0.979), -FL(12.0)},
        {FL(3270.0), FL(0.440), -FL(17.0)}},
     {  { FL(385.0), FL(0.987),  FL(10.0)},   /* ihh (bit) */
        {FL(2056.0), FL(0.930), -FL(20.0)},
        {FL(2587.0), FL(0.890), -FL(20.0)},
        {FL(3150.0), FL(0.400), -FL(20.0)}},
     {  { FL(515.0), FL(0.977),  FL(10.0)},   /* ehh (bet) */
        {FL(1805.0), FL(0.810), -FL(10.0)},
        {FL(2526.0), FL(0.875), -FL(10.0)},
        {FL(3103.0), FL(0.400), -FL(13.0)}},
     {  { FL(773.0), FL(0.950),  FL(10.0)},   /* aaa (bat) */
        {FL(1676.0), FL(0.830),  -FL(6.0)},
        {FL(2380.0), FL(0.880), -FL(20.0)},
        {FL(3027.0), FL(0.600), -FL(20.0)}},

     {  { FL(770.0), FL(0.950),   FL(0.0)},   /* ahh (father) */
        {FL(1153.0), FL(0.970),  -FL(9.0)},
        {FL(2450.0), FL(0.780), -FL(29.0)},
        {FL(3140.0), FL(0.800), -FL(39.0)}},
     {  { FL(637.0), FL(0.910),   FL(0.0)},   /* aww (bought) */
        { FL(895.0), FL(0.900),  -FL(3.0)},
        {FL(2556.0), FL(0.950), -FL(17.0)},
        {FL(3070.0), FL(0.910), -FL(20.0)}},
     {  { FL(637.0), FL(0.910),   FL(0.0)},   /* ohh (bone) NOTE:: same as aww (bought) */
        { FL(895.0), FL(0.900),  -FL(3.0)},
        {FL(2556.0), FL(0.950), -FL(17.0)},
        {FL(3070.0), FL(0.910), -FL(20.0)}},
     {  { FL(561.0), FL(0.965),   FL(0.0)},   /* uhh (but) */
        {FL(1084.0), FL(0.930), -FL(10.0)},
        {FL(2541.0), FL(0.930), -FL(15.0)},
        {FL(3345.0), FL(0.900), -FL(20.0)}},

     {  { FL(515.0), FL(0.976),   FL(0.0)},   /* uuu (foot) */
        {FL(1031.0), FL(0.950),  -FL(3.0)},
        {FL(2572.0), FL(0.960), -FL(11.0)},
        {FL(3345.0), FL(0.960), -FL(20.0)}},
     {  { FL(349.0), FL(0.986), -FL(10.0)},   /* ooo (boot) */
        { FL(918.0), FL(0.940), -FL(20.0)},
        {FL(2350.0), FL(0.960), -FL(27.0)},
        {FL(2731.0), FL(0.950), -FL(33.0)}},
     {  { FL(394.0), FL(0.959), -FL(10.0)},   /* rrr (bird) */
        {FL(1297.0), FL(0.780), -FL(16.0)},
        {FL(1441.0), FL(0.980), -FL(16.0)},
        {FL(2754.0), FL(0.950), -FL(40.0)}},
     {  { FL(462.0), FL(0.990),  +FL(5.0)},   /* lll (lull) */
        {FL(1200.0), FL(0.640), -FL(10.0)},
        {FL(2500.0), FL(0.200), -FL(20.0)},
        {FL(3000.0), FL(0.100), -FL(30.0)}},

     {  { FL(265.0), FL(0.987), -FL(10.0)},   /* mmm (mom) */
        {FL(1176.0), FL(0.940), -FL(22.0)},
        {FL(2352.0), FL(0.970), -FL(20.0)},
        {FL(3277.0), FL(0.940), -FL(31.0)}},
     {  { FL(204.0), FL(0.980), -FL(10.0)},   /* nnn (nun) */
        {FL(1570.0), FL(0.940), -FL(15.0)},
        {FL(2481.0), FL(0.980), -FL(12.0)},
        {FL(3133.0), FL(0.800), -FL(30.0)}},
     {  { FL(204.0), FL(0.980), -FL(10.0)},   /* nng (sang) NOTE:: same as nnn */
        {FL(1570.0), FL(0.940), -FL(15.0)},
        {FL(2481.0), FL(0.980), -FL(12.0)},
        {FL(3133.0), FL(0.800), -FL(30.0)}},
     {  { FL(204.0), FL(0.980), -FL(10.0)},   /* ngg (bong) NOTE:: same as nnn */
        {FL(1570.0), FL(0.940), -FL(15.0)},
        {FL(2481.0), FL(0.980), -FL(12.0)},
        {FL(3133.0), FL(0.800), -FL(30.0)}},

     {  {FL(1000.0), FL(0.300),   FL(0.0)},   /* fff */
        {FL(2800.0), FL(0.860), -FL(10.0)},
        {FL(7425.0), FL(0.740),   FL(0.0)},
        {FL(8140.0), FL(0.860),   FL(0.0)}},
     {  {FL(0.0), FL(0.000),   FL(0.0)},      /* sss */
        {FL(2000.0), FL(0.700), -FL(15.0)},
        {FL(5257.0), FL(0.750),  -FL(3.0)},
        {FL(7171.0), FL(0.840),   FL(0.0)}},
     {  { FL(100.0), FL(0.900),   FL(0.0)},   /* thh */
        {FL(4000.0), FL(0.500), -FL(20.0)},
        {FL(5500.0), FL(0.500), -FL(15.0)},
        {FL(8000.0), FL(0.400), -FL(20.0)}},
     {  {FL(2693.0), FL(0.940),   FL(0.0)},   /* shh */
        {FL(4000.0), FL(0.720), -FL(10.0)},
        {FL(6123.0), FL(0.870), -FL(10.0)},
        {FL(7755.0), FL(0.750), -FL(18.0)}},

     {  {FL(1000.0), FL(0.300), -FL(10.0)},   /* xxx  NOTE:: Not Really Done Yet */
        {FL(2800.0), FL(0.860), -FL(10.0)},
        {FL(7425.0), FL(0.740),   FL(0.0)},
        {FL(8140.0), FL(0.860),   FL(0.0)}},
     {  { FL(273.0), FL(0.996), -FL(40.0)},   /* hee (beet)    (noisy eee) */
        {FL(2086.0), FL(0.945), -FL(16.0)},
        {FL(2754.0), FL(0.979), -FL(12.0)},
        {FL(3270.0), FL(0.440), -FL(17.0)}},
     {  { FL(349.0), FL(0.986), -FL(40.0)},   /* hoo (boot)    (noisy ooo) */
        { FL(918.0), FL(0.940), -FL(10.0)},
        {FL(2350.0), FL(0.960), -FL(17.0)},
        {FL(2731.0), FL(0.950), -FL(23.0)}},
     {  { FL(770.0), FL(0.950), -FL(40.0)},   /* hah (father)  (noisy ahh) */
        {FL(1153.0), FL(0.970),  -FL(3.0)},
        {FL(2450.0), FL(0.780), -FL(20.0)},
        {FL(3140.0), FL(0.800), -FL(32.0)}},

     {  {FL(2000.0), FL(0.700), -FL(20.0)},   /* bbb NOTE:: Not Really Done Yet */
        {FL(5257.0), FL(0.750), -FL(15.0)},
        {FL(7171.0), FL(0.840),  -FL(3.0)},
        {FL(9000.0), FL(0.900),   FL(0.0)}},
     {  { FL(100.0), FL(0.900),   FL(0.0)},   /* ddd NOTE:: Not Really Done Yet */
        {FL(4000.0), FL(0.500), -FL(20.0)},
        {FL(5500.0), FL(0.500), -FL(15.0)},
        {FL(8000.0), FL(0.400), -FL(20.0)}},
     {  {FL(2693.0), FL(0.940),   FL(0.0)},   /* jjj NOTE:: Not Really Done Yet */
        {FL(4000.0), FL(0.720), -FL(10.0)},
        {FL(6123.0), FL(0.870), -FL(10.0)},
        {FL(7755.0), FL(0.750), -FL(18.0)}},
     {  {FL(2693.0), FL(0.940),   FL(0.0)},   /* ggg NOTE:: Not Really Done Yet */
        {FL(4000.0), FL(0.720), -FL(10.0)},
        {FL(6123.0), FL(0.870), -FL(10.0)},
        {FL(7755.0), FL(0.750), -FL(18.0)}},

     {  {FL(2000.0), FL(0.700), -FL(20.0)},   /* vvv NOTE:: Not Really Done Yet */
        {FL(5257.0), FL(0.750), -FL(15.0)},
        {FL(7171.0), FL(0.840),  -FL(3.0)},
        {FL(9000.0), FL(0.900),   FL(0.0)}},
     {  { FL(100.0), FL(0.900),   FL(0.0)},   /* zzz NOTE:: Not Really Done Yet */
        {FL(4000.0), FL(0.500), -FL(20.0)},
        {FL(5500.0), FL(0.500), -FL(15.0)},
        {FL(8000.0), FL(0.400), -FL(20.0)}},
     {  {FL(2693.0), FL(0.940),   FL(0.0)},   /* thz NOTE:: Not Really Done Yet */
        {FL(4000.0), FL(0.720), -FL(10.0)},
        {FL(6123.0), FL(0.870), -FL(10.0)},
        {FL(7755.0), FL(0.750), -FL(18.0)}},
     {  {FL(2693.0), FL(0.940),   FL(0.0)},   /* zhh NOTE:: Not Really Done Yet */
        {FL(4000.0), FL(0.720), -FL(10.0)},
        {FL(6123.0), FL(0.870), -FL(10.0)},
        {FL(7755.0), FL(0.750), -FL(18.0)}}
};

#define currentVowel (*q->control1)

void FMVoices_setFreq(FM4OPV *q, MYFLT frequency)
{
    MYFLT       temp,temp2 = FL(0.0);
    int         tempi,tempi2 = 0;

    if (currentVowel < 32)      {
      tempi2 = (int)currentVowel;
      temp2 = FL(0.9);
    }
    else if (currentVowel < 64) {
      tempi2 =(int) currentVowel - 32;
      temp2 = FL(1.0);
    }
    else if (currentVowel < 96) {
      tempi2 = (int)currentVowel - 64;
      temp2 = FL(1.1);
    }
    else if (currentVowel < 128)        {
      tempi2 = (int)currentVowel - 96;
      temp2 = FL(1.2);
    }
    q->baseFreq = frequency;
    temp = (temp2 * phonParams[tempi2][0][0] / q->baseFreq) + FL(0.5);
    tempi = (int) temp;
    FM4Op_setRatio((FM4OP*)q, 0, (MYFLT) tempi);
    temp = (temp2 * phonParams[tempi2][1][0] / q->baseFreq) + FL(0.5);
    tempi = (int) temp;
    FM4Op_setRatio((FM4OP*)q, 1, (MYFLT) tempi);
    temp = (temp2 * phonParams[tempi2][2][0] / q->baseFreq) + FL(0.5);
    tempi = (int) temp;
    FM4Op_setRatio((FM4OP*)q, 2, (MYFLT) tempi);
    q->gains[0] = FL(1.0);  /* pow(10.0f,phonParams[tempi2][0][2] * 0.05f); */
    q->gains[1] = FL(1.0);  /* pow(10.0f,phonParams[tempi2][1][2] * 0.05f); */
    q->gains[2] = FL(1.0);  /* pow(10.0f,phonParams[tempi2][2][2] * 0.05f); */
}

void FMVoiceset(FM4OPV *q)
{
    FM4OP       *p = (FM4OP *)q;
    MYFLT       amp = *q->amp * AMP_RSCALE;

    make_FM4Op(p);
    if (FM4Op_loadWaves(p)) return;
    FM4Op_setRatio(p, 0, FL(2.00));
    FM4Op_setRatio(p, 1, FL(4.00));
    FM4Op_setRatio(p, 2, FL(12.0));
    FM4Op_setRatio(p, 3, FL(1.00));
    p->gains[3] = FM4Op_gains[80];
    ADSR_setAllTimes(&p->adsr[0], FL(0.050), FL(0.050),
                     FM4Op_susLevels[15], FL(0.050));
    ADSR_setAllTimes(&p->adsr[1], FL(0.050), FL(0.050),
                     FM4Op_susLevels[15], FL(0.050));
    ADSR_setAllTimes(&p->adsr[2], FL(0.050), FL(0.050),
                     FM4Op_susLevels[15], FL(0.050));
    ADSR_setAllTimes(&p->adsr[3], FL(0.001), FL(0.010),
                     FM4Op_susLevels[15], FL(0.500));
/*      ADSR_setAll(&p->adsr[0], FL(0.001), FL(0.001),
        FM4Op_susLevels[15], FL(0.001)); */
/*      ADSR_setAll(&p->adsr[1], FL(0.001), FL(0.001),
        FM4Op_susLevels[15], FL(0.001)); */
/*      ADSR_setAll(&p->adsr[2], FL(0.001), FL(0.001),
        FM4Op_susLevels[15], FL(0.001)); */
/*      ADSR_setAll(&p->adsr[3], 0.05f,  0.05f,  FM4Op_susLevels[15], 0.0001f); */
    p->twozero.gain = FL(0.0);
/*     modDepth = 0.005; */
    q->tilt[0] = FL(1.0);
    q->tilt[1] = FL(0.5);
    q->tilt[2] = FL(0.2);
    q->mods[0] = FL(1.0);
    q->mods[1] = FL(1.1);
    q->mods[2] = FL(1.1);
    p->baseFreq = FL(110.0);
    FMVoices_setFreq(q, FL(110.0));
    q->tilt[0] = amp;
    q->tilt[1] = amp * amp;
    q->tilt[2] = amp * amp * amp;
    ADSR_keyOn(&p->adsr[0]);
    ADSR_keyOn(&p->adsr[1]);
    ADSR_keyOn(&p->adsr[2]);
    ADSR_keyOn(&p->adsr[3]);
    q->last_control = -FL(1.0);
}

void FMVoice(FM4OPV *q)
{
    FM4OP       *p = (FM4OP *)q;
    MYFLT       amp = *q->amp * AMP_RSCALE;
    MYFLT       *ar = q->ar;
    long        nsmps = ksmps;

    if (p->baseFreq != *q->frequency || *q->control1 != q->last_control) {
      q->last_control = *q->control1;
      p->baseFreq = *q->frequency;
      FMVoices_setFreq(q, p->baseFreq);
    }
    q->tilt[0] = amp;
    q->tilt[1] = amp * amp;
    q->tilt[2] = amp * amp * amp;
    p->gains[3] = FM4Op_gains[(int) (*p->control2 * FL(0.78125))];

    do {
        MYFLT   lastOutput;
        lastOutput = FM4Alg6_tick(q);
        *ar++ = lastOutput*AMP_SCALE*FL(0.8);
    } while (--nsmps);

}

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

/*********************************************************/
/*  Algorithm 4 (TX81Z) Subclass of 4 Operator FM Synth  */
/*  by Perry R. Cook, 1995-96  Recoded John ffitch 97/98 */
/*                                                       */
/*  Alg 4 is :      4->3--\                              */
/*                     2-- + -->1-->Out                  */
/*                                                       */
/*  Controls: control1 = total mod index                 */
/*            control2 = crossfade of two                */
/*                          modulators                   */
/*                                                       */
/*********************************************************/

MYFLT FM4Alg4_tick(FM4OP *p, MYFLT c1, MYFLT c2)
{
    MYFLT       temp;
    MYFLT       lastOutput;

    temp = Wave_tick(&p->v_time, (int)p->vibWave->flen,
                     p->vibWave->ftable, p->v_rate, FL(0.0)) *
      *p->modDepth * FL(0.2);
    temp = p-> baseFreq * (FL(1.0) + temp)/ esr;
    p->w_rate[0] = p->ratios[0] * temp * p->waves[0]->flen;
    p->w_rate[1] = p->ratios[1] * temp * p->waves[1]->flen;
    p->w_rate[2] = p->ratios[2] * temp * p->waves[2]->flen;
    p->w_rate[3] = p->ratios[3] * temp * p->waves[3]->flen;

    p->w_phase[3] = p->waves[3]->flen * p->twozero.lastOutput;
    temp = p->gains[3] * ADSR_tick(&p->adsr[3]) *
         Wave_tick(&p->w_time[3], (int)p->waves[3]->flen, p->waves[3]->ftable,
                   p->w_rate[3], p->w_phase[3]);
    TwoZero_tick(&p->twozero, temp);
    p->w_phase[2] = p->waves[2]->flen * temp;
    temp = (FL(1.0) - (c2 * FL(0.5))) * p->gains[2] * ADSR_tick(&p->adsr[2]) *
      Wave_tick(&p->w_time[2], (int)p->waves[2]->flen, p->waves[2]->ftable,
                p->w_rate[2], p->w_phase[2]);
    temp += c2 * FL(0.5) * p->gains[1] * ADSR_tick(&p->adsr[1]) *
         Wave_tick(&p->w_time[1], (int)p->waves[1]->flen, p->waves[1]->ftable,
                   p->w_rate[1], p->w_phase[1]);
    temp = temp * c1;
    p->w_phase[0] = p->waves[0]->flen * temp;
    temp = p->gains[0] * ADSR_tick(&p->adsr[0]) *
         Wave_tick(&p->w_time[0], (int)p->waves[0]->flen, p->waves[0]->ftable,
                   p->w_rate[0], p->w_phase[0]);

    lastOutput = temp * FL(0.5);
    return lastOutput;
}


void percfluteset(FM4OP *p)
{
    MYFLT       amp = *p->amp * AMP_RSCALE; /* Normalised */

    make_FM4Op(p);
    if (FM4Op_loadWaves(p)) return;         /* 3 x sines; 1 x fwavblnk */

    FM4Op_setRatio(p, 0, FL(1.50)     );
    FM4Op_setRatio(p, 1, FL(3.00) * FL(0.995));
    FM4Op_setRatio(p, 2, FL(2.99) * FL(1.005));
    FM4Op_setRatio(p, 3, FL(6.00) * FL(0.997));

    p->gains[0] = amp * FM4Op_gains[99];
    p->gains[1] = amp * FM4Op_gains[71];
    p->gains[2] = amp * FM4Op_gains[93];
    p->gains[3] = amp * FM4Op_gains[85];
    ADSR_setAllTimes(&p->adsr[0], FL(0.05), FL(0.05),
                     FM4Op_susLevels[14], FL(0.05));
    ADSR_setAllTimes(&p->adsr[1], FL(0.02), FL(0.50),
                     FM4Op_susLevels[13], FL(0.5));
    ADSR_setAllTimes(&p->adsr[2], FL(0.02), FL(0.30),
                     FM4Op_susLevels[11], FL(0.05));
    ADSR_setAllTimes(&p->adsr[3], FL(0.02), FL(0.05),
                     FM4Op_susLevels[13], FL(0.01));
/*  ADSR_setAll(&p->adsr[0],FL(0.001),FL(0.001),FM4Op_susLevels[14], FL(0.001));*/
/*  ADSR_setAll(&p->adsr[1], 0.05f,  0.0001f, FM4Op_susLevels[13], 0.0001f); */
/*  ADSR_setAll(&p->adsr[2], 0.05f,  0.0020f, FM4Op_susLevels[11], FL(0.001)); */
/*  ADSR_setAll(&p->adsr[3], 0.05f,  0.0010f, FM4Op_susLevels[13], FL(0.005)); */
    p->twozero.gain = FL(0.0);
/*     modDepth = FL(0.005); */
    ADSR_keyOn(&p->adsr[0]);
    ADSR_keyOn(&p->adsr[1]);
    ADSR_keyOn(&p->adsr[2]);
    ADSR_keyOn(&p->adsr[3]);
}


void percflute(FM4OP *p)
{
    MYFLT       *ar = p->ar;
    long        nsmps = ksmps;
    MYFLT       amp = *p->amp * AMP_RSCALE; /* Normalised */
    MYFLT       c1 = *p->control1;
    MYFLT       c2 = *p->control2;

    p->baseFreq = *p->frequency;
    p->gains[0] = amp * FM4Op_gains[99] * FL(0.5);
    p->gains[1] = amp * FM4Op_gains[71] * FL(0.5);
    p->gains[2] = amp * FM4Op_gains[93] * FL(0.5);
    p->gains[3] = amp * FM4Op_gains[85] * FL(0.5);
    do {
      MYFLT   lastOutput;
      lastOutput = FM4Alg4_tick(p, c1, c2);
      *ar++ = lastOutput*AMP_SCALE*FL(2.0);
    } while (--nsmps);
}
